diff --git a/.gitignore b/.gitignore
index 4d50219..9c34e25 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,6 @@
node_modules
bower_components
-mock.png
.*.sw*
.build*
jquery.fn.*
-/test
+.vscode
\ No newline at end of file
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..3b9c6cb
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,51 @@
+# Contributing
+
+This element contains a script to automatically rebuild it for choosen Sortable release.
+
+**TL;DR**
+
+- Change `polymer-sortablejs-template.html` file instead of ~~`polymer-sortablejs.html`~~
+- Run `$ node bower_components/polymer-sortablejs/build.js` to update and rebuild element
+
+## Overview
+
+When _there's an update to Sortable framework_ for which this element is a wraper or you want to _rebuild to include changes you made in template_, you should run `build.js` script inside of element folder with **Node 8** or newer. It will overwrite `polymer-sortable.html` with content of template file `polymer-sortable-template.html` and all options in currently installed Sortable version, excluding those that are defined at the top of `build.js` file.
+
+### Choosing SOrtable version
+
+By default script looks for Sortable files in bower_components and node_modules and will use currently installed verison.
+If it can't find them, script will download from GitHub (RubaXa/Sortable#master). You can use `-b` to specify branch or `-u` - username from which Sortable will be downloaded.
+
+### Exluding options
+
+On top of `build.js` file there is a constant Array
+
+````javascript
+const DISABLED_PROPERTIES = [
+ 'draggable',
+ 'setData',
+ 'supportPointer'
+];
+````
+
+Those properties will not be included in built file so you either need to manually set them in the element or make sure they aren't nescessary.
+
+### Editing template
+
+You can do whatever you want in the template file as long as you don't remove two comments:
+
+````javascript
+/*properties*/
+
+/*propertyobservers*/
+````
+
+Those two must stay in their places and you mustn't forget they will be overwriten with normal property/function definiton - do not remove commas!
+
+## Pull Requests
+
+Before submitting a Pull Request be sure to run `build.js`.
+
+If you change the code, commit the same message both to the template and generated file if possible.
+
+If you change is only update to latest Sortable, update version in `bower.json` and commit your change as `Rebuilt for Sortable [verison-number]`.
\ No newline at end of file
diff --git a/bower.json b/bower.json
index 94be943..471d5ac 100644
--- a/bower.json
+++ b/bower.json
@@ -25,7 +25,15 @@
"tests"
],
"dependencies": {
- "polymer": "polymer/polymer#^1.6.0",
- "Sortable": "sortablejs#^1.4.2"
+ "polymer": "polymer/polymer#1.9 - 2",
+ "Sortable": "sortablejs#^1.6.0"
+ },
+ "variants": {
+ "1.x": {
+ "dependencies": {
+ "polymer": "polymer/polymer#1.9",
+ "Sortable": "sortablejs#^1.6.0"
+ }
+ }
}
}
diff --git a/build.js b/build.js
new file mode 100644
index 0000000..ec76af6
--- /dev/null
+++ b/build.js
@@ -0,0 +1,182 @@
+const DISABLED_PROPERTIES = [
+ 'draggable',
+ 'setData',
+ 'supportPointer'
+];
+
+
+const fsCallbacks = require('fs'),
+ path = require('path'),
+ https = require('https'),
+ promisify = require('util').promisify;
+
+const fs = {
+ stat: promisify(fsCallbacks.stat),
+ readFile: promisify(fsCallbacks.readFile),
+ writeFile: promisify(fsCallbacks.writeFile),
+ exists: fsCallbacks.existsSync
+};
+
+// From https://stackoverflow.com/a/17676794/6166832
+function donwload(url, dest) {
+ return new Promise((resolve, reject) => {
+ const file = fsCallbacks.createWriteStream(dest),
+ request = http.get(url, response => {
+ response.pipe(file);
+ file.on('finish', () => file.close(reslove));
+ });
+ });
+}
+
+
+// Helper for folder lookup
+function generatePath(depth, ending) {
+ const generatedDepth = new Array(depth).fill('..');
+ return path.join(__dirname, ...generatedDepth, ending);
+}
+
+let found = false;
+
+// Look in bower_components
+for (let depth = 0; depth <= 2; depth++) {
+ if (fs.exists(generatePath(depth, 'bower_components/Sortable/Sortable.js'))) {
+ found = true;
+ console.log('Building from version in bower_components');
+ loadFromFile(generatePath(depth, 'bower_components/Sortable/Sortable.js'));
+ break;
+ }
+}
+
+if (!found) {
+ // Look in node_modules
+ for (let depth = 0; depth <= 2; depth++) {
+ if (fs.exists(generatePath(depth, 'node_modules/Sortable/Sortable.js'))) {
+ found = true;
+ console.log('Building from version in node_modules');
+ loadFromFile(generatePath(depth, 'node_modules/Sortable/Sortable.js'));
+ break;
+ }
+ }
+}
+
+if (!found) {
+ downloadFromGit();
+}
+
+
+async function loadFromFile(filePath) {
+ const file = await fs.readFile(filePath, 'utf8');
+ proceed(file);
+}
+
+async function downloadFromGit() {
+ const gitUsername = process.argv.includes('-u')
+ ? process.argv[process.argv.indexOf('-u') + 1] : 'RubaXa';
+ const gitBranch = process.argv.includes('-b')
+ ? process.argv[process.argv.indexOf('-b') + 1] : 'master';
+ https.get(`https://raw.githubusercontent.com/${gitUsername}/Sortable/${gitBranch}/Sortable.js`, resp => {
+ let data = '';
+ resp.on('data', (chunk) => {
+ data += chunk;
+ });
+ resp.on('end', () => {
+ proceed(data);
+ });
+ }).on("error", err => {
+ console.trace(err);
+ throw new Error('Couldn\'t get Sortable.js');
+ });
+ console.log(`Building from Gtihub ${gitUsername}/Sortable#${gitBranch}`);
+}
+
+async function proceed(string) {
+ // Get options from source code of Sortblejs
+ const optionsString = /var\s+defaults\s*=\s*{\s*(([^:]+:[^,}]+)+\s*)}/m.exec(string)[1],
+ optionsStringSplit = optionsString.split(''),
+ optionsArray = [];
+ // Read them into an array [ key, value, key, value... ]
+ let current = '',
+ depthLevel = 0,
+ depthExpectColon = false,
+ depthStringOpen = false;
+ for (let i in optionsStringSplit) {
+ const character = optionsStringSplit[i];
+ if (character === '{'
+ || (character === '\'' && !depthStringOpen) || (character === '"' && !depthStringOpen)
+ || character === '?'
+ || character === '(') {
+ depthLevel++;
+ if (character === '?') depthExpectColon = true;
+ if (character === '\'' || character === '"') depthStringOpen = true;
+ if (depthLevel > 0) current += character;
+ } else if (character === '}'
+ || (character === '\'' && depthStringOpen) || (character === '"' && depthStringOpen)
+ || (depthExpectColon && character === ':')
+ || character === ')') {
+ depthLevel--;
+ if (character === ':') depthExpectColon = false;
+ if (character === '\'' || character === '"') depthStringOpen = false;
+ if (depthLevel >= 0) current += character;
+ } else if ((character === ','
+ || character === ':')
+ && depthLevel === 0) {
+ optionsArray.push(current);
+ current = '';
+ } else if (depthLevel > 0 || /[^\s:,]/.test(character)) {
+ current += character;
+ }
+ if (depthLevel < 0) {
+ optionsArray.push(current);
+ break;
+ }
+ }
+ // Throw if read options aren't even
+ if (optionsArray.length % 2) {
+ console.log('Options that were read:', optionsArray);
+ throw new Error('Something went wrong when reading options');
+ }
+ // Process the array to attach types
+ const computedOptions = {};
+ let key, value, type;
+ optionsArray.forEach((item, index) => {
+ index % 2 ? value = item : key = item;
+ if (value && key) {
+ if (!DISABLED_PROPERTIES.includes(key)) {
+ if (value === 'false' || value === 'true') type = 'Boolean'
+ else if (Number(value) !== NaN) type = 'Number'
+ else if (/$\s*\{/.test(value)) type = 'Object'
+ else if (/$\s*\[/.test(value)) type = 'Array'
+ else type = 'String'
+ computedOptions[key] = { value, type };
+ }
+ key = value = type = null;
+ }
+ });
+
+ // Get template file
+ const template = await fs.readFile(path.join(__dirname, 'polymer-sortablejs-template.html'), 'utf8');
+ let generatedTemplate = template;
+
+ // Generate properties
+ generatedTemplate = generatedTemplate.replace(/^(\s*)\/\*properties\*\//m, (_, spacing) => {
+ let output = '';
+ for (key in computedOptions) {
+ const type = computedOptions[key].type,
+ value = computedOptions[key].value;
+ output += `${spacing}${key}: { type: ${type}, value: function() { return ${value} }, observer: '${key}Changed' },\n`
+ }
+ return output.slice(0, -2);
+ });
+
+ // Generate observers
+ generatedTemplate = generatedTemplate.replace(/^(\s*)\/\*propertyobservers\*\//m, (_, spacing) => {
+ let output = '';
+ for (key in computedOptions) {
+ if (!new RegExp(key + 'Changed').test(template))
+ output += `${spacing}${key}Changed: function(value) { this.sortable && this.sortable.option("${key}", value); },\n`
+ }
+ return output.slice(0, -2);
+ });
+
+ await fs.writeFile(path.join(__dirname, 'polymer-sortablejs.html'), generatedTemplate, 'utf8');
+}
\ No newline at end of file
diff --git a/polymer-sortablejs-template.html b/polymer-sortablejs-template.html
new file mode 100644
index 0000000..964c486
--- /dev/null
+++ b/polymer-sortablejs-template.html
@@ -0,0 +1,163 @@
+
+
+
+
+
+
+
+
+
diff --git a/polymer-sortablejs.html b/polymer-sortablejs.html
index 215174b..de1e1e4 100644
--- a/polymer-sortablejs.html
+++ b/polymer-sortablejs.html
@@ -3,7 +3,7 @@
-
+