Skip to content

Commit

Permalink
Add things
Browse files Browse the repository at this point in the history
  • Loading branch information
osvaldasvalutis committed Aug 27, 2018
1 parent 81935c0 commit 5f5fdf4
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 1 deletion.
43 changes: 43 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module.exports = {
"root": true,
"extends": "eslint:recommended",
"env": {
"node": true,
"es6": true,
"amd": true,
"browser": true
},
"parserOptions": {
"ecmaFeatures": {
"globalReturn": true,
"generators": false,
"objectLiteralDuplicateProperties": false,
"experimentalObjectRestSpread": true
},
"ecmaVersion": 2017,
"sourceType": "module"
},
"plugins": [
"import"
],
"settings": {
"import/core-modules": [],
"import/ignore": [
"node_modules",
"\\.(coffee|scss|css|less|hbs|svg|json)$"
]
},
"rules": {
"no-console": 0,
"comma-dangle": [
"error",
{
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "ignore"
}
]
}
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
25 changes: 24 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,25 @@
# cocoon-vanilla-js
# cocoon-vanilla-js (private)

A vanilla JS replacement for (Rails) Cocoon's jQuery script


## Usage

Replace `<token>` and `<commit>` with the corresponding values:

```
yarn add git+https://<token>:[email protected]/kollegorna/cocoon-vanilla-js.git#<commit>
```

Import as ES6 module:

```js
import 'cocoon-vanilla-js'
```

## Notes

To broaden browser support, use the following polyfills:

- [Element.closest](https://www.npmjs.com/package/element-closest)
- [Element.classList](https://www.npmjs.com/package/classlist-polyfill)
162 changes: 162 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
let cocoon_element_counter = 0;

const create_new_id = function() {
return (new Date().getTime() + cocoon_element_counter++);
};

const newcontent_braced = function(id) {
return '[' + id + ']$1';
};

const newcontent_underscord = function(id) {
return '_' + id + '_$1';
};

const getInsertionNodeElem = function(insertionNode, insertionTraversal, btn) {
if(!insertionNode){
return btn.parentNode;
}
else { // string
if(insertionTraversal) {
// TODO:
// https://github.com/nathanvda/cocoon/blob/master/app/assets/javascripts/cocoon.js#L32
// data-association-insertion-traversal: the jquery traversal method to
// allow node selection relative to the link. closest, next, children, etc.
// return $this[insertionTraversal](insertionNode);
return null;
}
else {
return document.querySelector(insertionNode);
}
}
};

const addFieldsHandler = (btn) => {
const assoc = btn.getAttribute('data-association');
const assocs = btn.getAttribute('data-associations');
const content = btn.getAttribute('data-association-insertion-template');
const insertionNode = btn.getAttribute('data-association-insertion-node');
const insertionTraversal = btn.getAttribute('data-association-insertion-traversal');
let insertionMethod = btn.getAttribute('data-association-insertion-method') || btn.getAttribute('data-association-insertion-position') || 'before';
let new_id = create_new_id();
let count = parseInt(btn.getAttribute('data-count'), 10);
let regexp_braced = new RegExp('\\[new_' + assoc + '\\](.*?\\s)', 'g');
let regexp_underscord = new RegExp('_new_' + assoc + '_(\\w*)', 'g');
let new_content = content.replace(regexp_braced, newcontent_braced(new_id));
let new_contents = [];

if(new_content == content) {
regexp_braced = new RegExp('\\[new_' + assocs + '\\](.*?\\s)', 'g');
regexp_underscord = new RegExp('_new_' + assocs + '_(\\w*)', 'g');
new_content = content.replace(regexp_braced, newcontent_braced(new_id));
}

new_content = new_content.replace(regexp_underscord, newcontent_underscord(new_id));
new_contents = [new_content];

count = (isNaN(count) ? 1 : Math.max(count, 1));
count -= 1;

while(count) {
new_id = create_new_id();
new_content = content.replace(regexp_braced, newcontent_braced(new_id));
new_content = new_content.replace(regexp_underscord, newcontent_underscord(new_id));
new_contents.push(new_content);
count -= 1;
}

const insertionNodeElem = getInsertionNodeElem(insertionNode, insertionTraversal, btn);

if(!insertionNodeElem || (insertionNodeElem.length == 0)) {
console.warn("Couldn't find the element to insert the template. Make sure your `data-association-insertion-*` on `link_to_add_association` is correct.");
}

new_contents.forEach((node) => {
const event = new CustomEvent('cocoon:before-insert', {detail: node});
insertionNodeElem.dispatchEvent(event);

if(!event.defaultPrevented) {
switch(insertionMethod) {
default:
case 'before':
insertionMethod = 'beforebegin';
break;
case 'after':
insertionMethod = 'afterend';
break;
case 'append':
insertionMethod = 'beforeend';
break;
case 'prepend':
insertionMethod = 'afterbegin';
break;
}

insertionNodeElem.insertAdjacentHTML(insertionMethod, node);
insertionNodeElem.dispatchEvent(
CustomEvent('cocoon:before-insert', {detail: node})
);
}
});
};

document.addEventListener('click', (e) => {
if(e.target.matches('.add_fields')) {
e.preventDefault();
e.stopPropagation();
addFieldsHandler(e.target);
}
});

const removeFieldsHandler = (btn) => {
const wrapperClass = btn.getAttribute('data-wrapper-class') || 'nested-fields';
const nodeToDelete = btn.closest(`.${wrapperClass}`);
const triggerNode = nodeToDelete.parentNode;

const event = new CustomEvent('cocoon:before-remove', {detail: nodeToDelete});
triggerNode.dispatchEvent(event);

if(!event.defaultPrevented) {
const timeout = triggerNode.getAttribute('data-remove-timeout') || 0;

setTimeout(() => {
if(btn.classList.contains('dynamic')) {
// nodeToDelete.remove();
nodeToDelete.parentNode.removeChild(nodeToDelete);
}
else {
const input = btn.previousElementSibling;
if(input && input.matches('input[type=hidden]')) {
input.value = 1;
}
nodeToDelete.style.display = 'none';
}

triggerNode.dispatchEvent(
CustomEvent('cocoon:after-remove', {detail: nodeToDelete})
);
}, timeout);
}
};

document.addEventListener('click', (e) => {
if(
e.target.matches('.remove_fields.dynamic') ||
e.target.matches('.remove_fields.existing')
) {
e.preventDefault();
e.stopPropagation();
removeFieldsHandler(e.target);
}
});

const hideFields = () => {
[...document.querySelectorAll('.remove_fields.existing.destroyed')].forEach((btn) => {
const wrapperClass = btn.getAttribute('data-wrapper-class') || 'nested-fields';
btn.closest(`.${wrapperClass}`).style.display = 'none';
});
};

document.addEventListener('DOMContentLoaded', hideFields);
document.addEventListener('turbolinks:load', hideFields);
document.addEventListener('page:load', hideFields);
19 changes: 19 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "cocoon-vanilla-js",
"version": "1.0.0",
"description": "A vanilla JS replacement for (Rails) Cocoon's jQuery script",
"main": "index.js",
"engines": {
"node": "<=9.11.2"
},
"repository": "git+https://github.com/kollegorna/cocoon-vanilla-js.git",
"author": "Kollegorna",
"license": "MIT",
"bugs": {
"url": "https://github.com/kollegorna/cocoon-vanilla-js/issues"
},
"homepage": "https://github.com/kollegorna/cocoon-vanilla-js#readme",
"devDependencies": {},
"dependencies": {},
"private": true
}
7 changes: 7 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"js-utils@git+https://8265015e18616ae2d58e38f4fd9ca111b8beaff8:[email protected]/kollegorna/js-utils.git#6507589":
version "1.0.0"
resolved "git+https://8265015e18616ae2d58e38f4fd9ca111b8beaff8:[email protected]/kollegorna/js-utils.git#6507589ae81c91ff2db72d7c4905a3d2d52674f9"

0 comments on commit 5f5fdf4

Please sign in to comment.