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 @@