...one VectorDrawable to rule all screen densities
24 | Last update:
25 |
26 |
27 |
28 |
29 |
30 |
Sorry but your browser does not support some of new HTML5 features! Try using latest version of Google Chrome or
31 | Firefox.
32 |
33 |
34 |
35 |
36 |
37 | Drop file here (or multiple files) to load content or click on this box to open file dialog.
38 |
No file will be uploaded - uses only JavaScript HTML5 FileReader.
39 |
40 |
41 | This tool has been deprecated. Use official Vector Asset Studio instead.
42 |
43 |
44 |
45 |
46 |
47 |
48 |
50 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 | Success: create xml file and copy all contents into res/drawable directory
61 |
62 |
63 |
64 |
67 |
68 |
69 |
70 |
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
85 |
86 |
87 |
88 |
91 |
92 |
93 | Android Support Library 23.2 has added support for VectorDrawable on Android API 7+ (more info)
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
Multiple SVG files ( files)
104 |
105 |
106 |
107 |
108 |
112 |
113 |
114 |
115 |
116 |
117 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/js/colors.min.js:
--------------------------------------------------------------------------------
1 | // colors.js - v2.0.0
2 | // Copyright 2012-2016 Matt Jordan
3 | // MIT License
4 | !function e(r,a,f){function n(o,d){if(!a[o]){if(!r[o]){var i="function"==typeof require&&require;if(!d&&i)return i(o,!0);if(t)return t(o,!0);var s=new Error("Cannot find module '"+o+"'");throw s.code="MODULE_NOT_FOUND",s}var u=a[o]={exports:{}};r[o][0].call(u.exports,function(e){var a=r[o][1][e];return n(a?a:e)},u,u.exports,e,r,a,f)}return a[o].exports}for(var t="function"==typeof require&&require,o=0;o1&&(i.h-=1)),[f.round(360*i.h),f.round(100*i.s),f.round(100*i.v)]};r.exports=n},{"./utils":14}],4:[function(e,r,a){var f=function(e){return e=e.replace(/^\#/,""),6===e.length?[parseInt(e.substr(0,2),16),parseInt(e.substr(2,2),16),parseInt(e.substr(4,2),16)]:parseInt(e,16)};r.exports=f},{}],5:[function(e,r,a){var f=e("./hsv2rgb"),n=function(e,r,a){var n,t,o,d,i,s,u,l,c,h,b,g;return"object"==typeof e?(n=e[0],t=e[1],o=e[2]):(n=e,t=r,o=a),u=f(n,t,o),l=u.R/255,c=u.G/255,h=u.B/255,b=Math.max(l,c,h),g=Math.min(l,c,h),s=(b+g)/2,i=0,d=0,b!=g&&(r=.5>s?(b-g)/(b+g):(b-g)/(2-b-g),e=l==b?(c-h)/(b-g):c==b?2+(h-l)/(b-g):4+(l-c)/(b-g)),s=100*s,i=100*i,d=60*d,0>d&&(d+=360),[Math.floor(e),Math.floor(r),Math.floor(a)]};r.exports=n},{"./hsv2rgb":6}],6:[function(e,r,a){var f=function(e,r,a){var f,n,t,o,d,i,s,u,l=[];switch("object"==typeof e?(f=e[0],n=e[1],t=e[2]):(f=e,n=r,t=a),n/=100,t/=100,o=Math.floor(f/60%6),d=f/60-o,i=t*(1-n),s=t*(1-d*n),u=t*(1-(1-d)*n),o){case 0:l=[t,u,i];break;case 1:l=[s,t,i];break;case 2:l=[i,t,u];break;case 3:l=[i,s,t];break;case 4:l=[u,i,t];break;case 5:l=[t,i,s]}return[Math.min(255,Math.floor(256*l[0])),Math.min(255,Math.floor(256*l[1])),Math.min(255,Math.floor(256*l[2]))]};r.exports=f},{}],7:[function(e,r,a){r.exports={complement:e("./complement"),hex2hsv:e("./hex2hsv"),hex2rgb:e("./hex2rgb"),hsv2hsl:e("./hsv2hsl"),hsv2rgb:e("./hsv2rgb"),name2hex:e("./name2hex"),name2hsv:e("./name2hsv"),name2rgb:e("./name2rgb"),rand:e("./rand"),rgb2hex:e("./rgb2hex"),rgb2hsl:e("./rgb2hsl"),utils:e("./utils")}},{"./complement":2,"./hex2hsv":3,"./hex2rgb":4,"./hsv2hsl":5,"./hsv2rgb":6,"./name2hex":8,"./name2hsv":9,"./name2rgb":10,"./rand":11,"./rgb2hex":12,"./rgb2hsl":13,"./utils":14}],8:[function(e,r,a){var f={aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},n=function(e){return e=e.toLowerCase(),f[e]?f[e]:"Invalid Color Name"};r.exports=n},{}],9:[function(e,r,a){var f=e("./name2hex"),n=e("./hex2hsv"),t=function(e){return n(f(e))};r.exports=t},{"./hex2hsv":3,"./name2hex":8}],10:[function(e,r,a){var f=e("./name2hex"),n=e("./hex2rgb"),t=function(e){return n(f(e))};r.exports=t},{"./hex2rgb":4,"./name2hex":8}],11:[function(e,r,a){var f="0123456789abcdef",n=function(e){return e?e():[Math.floor(-254*Math.random()+255),Math.floor(-254*Math.random()+255),Math.floor(-254*Math.random()+255)]};r.exports=n},{}],12:[function(e,r,a){var f=e("./utils"),n=function(e,r,a){return e=f.paddedHex(e),r=void 0!==r?f.paddedHex(r):e,a=void 0!==a?f.paddedHex(a):e,"#"+e+r+a};r.exports=n},{"./utils":14}],13:[function(e,r,a){var f=e("./utils"),n=function(e,r,a){var n,t,o,d,i,s,u,l,c;if("object"==typeof e?(n=e[0],t=e[1],o=e[2]):(n=e,t=r,o=a),n/=255,t/=255,o/=255,i=Math.max(n,t,o),d=Math.min(n,t,o),l=(i+d)/2,i==d)s=u=0;else{switch(c=i-d,u=l>.5?c/(2-i-d):c/(i+d),i){case n:s=(t-o)/c+(o>t?6:0);break;case t:s=(o-n)/c+2;break;case o:s=(n-t)/c+4}s/=6}return[Math.floor(360*s),f.round(100*u,1),f.round(100*l,1)]};r.exports=n},{"./utils":14}],14:[function(e,r,a){a.render=function(e,r){return e},a.paddedHex=function(e){var r=10>e?"0":"";return r+=e.toString(16),1===r.length?"0"+r:r},a.round=function(e,r){return r=r||10,parseFloat(e.toFixed(r))},a.hexRegexMatch=function(e){return/^\x23[a-f0-9]{3}([a-f0-9]{3})?$/i.test(e)}},{}]},{},[1]);
--------------------------------------------------------------------------------
/img/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
151 |
--------------------------------------------------------------------------------
/js/cssjson.js:
--------------------------------------------------------------------------------
1 | /**
2 | * CSS-JSON Converter for JavaScript
3 | * Converts CSS to JSON and back.
4 | * Version 2.1
5 | *
6 | * Released under the MIT license.
7 | *
8 | * Copyright (c) 2013 Aram Kocharyan, http://aramk.com/
9 |
10 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
11 | documentation files (the "Software"), to deal in the Software without restriction, including without limitation
12 | the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
13 | to permit persons to whom the Software is furnished to do so, subject to the following conditions:
14 |
15 | The above copyright notice and this permission notice shall be included in all copies or substantial portions
16 | of the Software.
17 |
18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
19 | THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 | */
24 |
25 | var CSSJSON = new function () {
26 |
27 | var base = this;
28 |
29 | base.init = function () {
30 | // String functions
31 | String.prototype.trim = function () {
32 | return this.replace(/^\s+|\s+$/g, '');
33 | };
34 |
35 | String.prototype.repeat = function (n) {
36 | return new Array(1 + n).join(this);
37 | };
38 | };
39 | base.init();
40 |
41 | var selX = /([^\s\;\{\}][^\;\{\}]*)\{/g;
42 | var endX = /\}/g;
43 | var lineX = /([^\;\{\}]*)\;/g;
44 | var commentX = /\/\*[\s\S]*?\*\//g;
45 | var lineAttrX = /([^\:]+):([^\;]*);/;
46 |
47 | // This is used, a concatenation of all above. We use alternation to
48 | // capture.
49 | var altX = /(\/\*[\s\S]*?\*\/)|([^\s\;\{\}][^\;\{\}]*(?=\{))|(\})|([^\;\{\}]+\;(?!\s*\*\/))/gmi;
50 |
51 | // Capture groups
52 | var capComment = 1;
53 | var capSelector = 2;
54 | var capEnd = 3;
55 | var capAttr = 4;
56 |
57 | var isEmpty = function (x) {
58 | return typeof x == 'undefined' || x.length == 0 || x == null;
59 | };
60 |
61 | var isCssJson = function (node) {
62 | return !isEmpty(node) ? (node.attributes && node.children) : false;
63 | };
64 |
65 | /**
66 | * Input is css string and current pos, returns JSON object
67 | *
68 | * @param cssString
69 | * The CSS string.
70 | * @param args
71 | * An optional argument object. ordered: Whether order of
72 | * comments and other nodes should be kept in the output. This
73 | * will return an object where all the keys are numbers and the
74 | * values are objects containing "name" and "value" keys for each
75 | * node. comments: Whether to capture comments. split: Whether to
76 | * split each comma separated list of selectors.
77 | */
78 | base.toJSON = function (cssString, args) {
79 | var node = {
80 | children: {},
81 | attributes: {}
82 | };
83 | var match = null;
84 | var count = 0;
85 |
86 | if (typeof args == 'undefined') {
87 | var args = {
88 | ordered: false,
89 | comments: false,
90 | stripComments: false,
91 | split: false
92 | };
93 | }
94 | if (args.stripComments) {
95 | args.comments = false;
96 | cssString = cssString.replace(commentX, '');
97 | }
98 |
99 | while ((match = altX.exec(cssString)) != null) {
100 | if (!isEmpty(match[capComment]) && args.comments) {
101 | // Comment
102 | var add = match[capComment].trim();
103 | node[count++] = add;
104 | } else if (!isEmpty(match[capSelector])) {
105 | // New node, we recurse
106 | var name = match[capSelector].trim();
107 | // This will return when we encounter a closing brace
108 | var newNode = base.toJSON(cssString, args);
109 | if (args.ordered) {
110 | var obj = {};
111 | obj['name'] = name;
112 | obj['value'] = newNode;
113 | // Since we must use key as index to keep order and not
114 | // name, this will differentiate between a Rule Node and an
115 | // Attribute, since both contain a name and value pair.
116 | obj['type'] = 'rule';
117 | node[count++] = obj;
118 | } else {
119 | if (args.split) {
120 | var bits = name.split(',');
121 | } else {
122 | var bits = [name];
123 | }
124 | for (i in bits) {
125 | var sel = bits[i].trim();
126 | if (sel in node.children) {
127 | for (var att in newNode.attributes) {
128 | node.children[sel].attributes[att] = newNode.attributes[att];
129 | }
130 | } else {
131 | node.children[sel] = newNode;
132 | }
133 | }
134 | }
135 | } else if (!isEmpty(match[capEnd])) {
136 | // Node has finished
137 | return node;
138 | } else if (!isEmpty(match[capAttr])) {
139 | var line = match[capAttr].trim();
140 | var attr = lineAttrX.exec(line);
141 | if (attr) {
142 | // Attribute
143 | var name = attr[1].trim();
144 | var value = attr[2].trim();
145 | if (args.ordered) {
146 | var obj = {};
147 | obj['name'] = name;
148 | obj['value'] = value;
149 | obj['type'] = 'attr';
150 | node[count++] = obj;
151 | } else {
152 | if (name in node.attributes) {
153 | var currVal = node.attributes[name];
154 | if (!(currVal instanceof Array)) {
155 | node.attributes[name] = [currVal];
156 | }
157 | node.attributes[name].push(value);
158 | } else {
159 | node.attributes[name] = value;
160 | }
161 | }
162 | } else {
163 | // Semicolon terminated line
164 | node[count++] = line;
165 | }
166 | }
167 | }
168 |
169 | return node;
170 | };
171 |
172 | /**
173 | * @param node
174 | * A JSON node.
175 | * @param depth
176 | * The depth of the current node; used for indentation and
177 | * optional.
178 | * @param breaks
179 | * Whether to add line breaks in the output.
180 | */
181 | base.toCSS = function (node, depth, breaks) {
182 | var cssString = '';
183 | if (typeof depth == 'undefined') {
184 | depth = 0;
185 | }
186 | if (typeof breaks == 'undefined') {
187 | breaks = false;
188 | }
189 | if (node.attributes) {
190 | for (i in node.attributes) {
191 | var att = node.attributes[i];
192 | if (att instanceof Array) {
193 | for (var j = 0; j < att.length; j++) {
194 | cssString += strAttr(i, att[j], depth);
195 | }
196 | } else {
197 | cssString += strAttr(i, att, depth);
198 | }
199 | }
200 | }
201 | if (node.children) {
202 | var first = true;
203 | for (i in node.children) {
204 | if (breaks && !first) {
205 | cssString += '\n';
206 | } else {
207 | first = false;
208 | }
209 | cssString += strNode(i, node.children[i], depth);
210 | }
211 | }
212 | return cssString;
213 | };
214 |
215 | /**
216 | * @param data
217 | * You can pass css string or the CSSJS.toJSON return value.
218 | * @param id (Optional)
219 | * To identify and easy removable of the style element
220 | * @param replace (Optional. defaults to TRUE)
221 | * Whether to remove or simply do nothing
222 | * @return HTMLLinkElement
223 | */
224 | base.toHEAD = function (data, id, replace) {
225 | var head = document.getElementsByTagName('head')[0];
226 | var xnode = document.getElementById(id);
227 | var _xnodeTest = (xnode !== null && xnode instanceof HTMLStyleElement);
228 |
229 | if (isEmpty(data) || !(head instanceof HTMLHeadElement)) return;
230 | if (_xnodeTest) {
231 | if (replace === true || isEmpty(replace)) {
232 | xnode.removeAttribute('id');
233 | } else return;
234 | }
235 | if (isCssJson(data)) {
236 | data = base.toCSS(data);
237 | }
238 |
239 | var node = document.createElement('style');
240 | node.type = 'text/css';
241 |
242 | if (!isEmpty(id)) {
243 | node.id = id;
244 | } else {
245 | node.id = 'cssjson_' + timestamp();
246 | }
247 | if (node.styleSheet) {
248 | node.styleSheet.cssText = data;
249 | } else {
250 | node.appendChild(document.createTextNode(data));
251 | }
252 |
253 | head.appendChild(node);
254 |
255 | if (isValidStyleNode(node)) {
256 | if (_xnodeTest) {
257 | xnode.parentNode.removeChild(xnode);
258 | }
259 | } else {
260 | node.parentNode.removeChild(node);
261 | if (_xnodeTest) {
262 | xnode.setAttribute('id', id);
263 | node = xnode;
264 | } else return;
265 | }
266 |
267 | return node;
268 | };
269 |
270 | // Alias
271 |
272 | if (typeof window != 'undefined') {
273 | window.createCSS = base.toHEAD;
274 | }
275 |
276 | // Helpers
277 |
278 | var isValidStyleNode = function (node) {
279 | return (node instanceof HTMLStyleElement) && node.sheet.cssRules.length > 0;
280 | };
281 |
282 | var timestamp = function () {
283 | return Date.now() || +new Date();
284 | };
285 |
286 | var strAttr = function (name, value, depth) {
287 | return '\t'.repeat(depth) + name + ': ' + value + ';\n';
288 | };
289 |
290 | var strNode = function (name, value, depth) {
291 | var cssString = '\t'.repeat(depth) + name + ' {\n';
292 | cssString += base.toCSS(value, depth + 1);
293 | cssString += '\t'.repeat(depth) + '}\n';
294 | return cssString;
295 | };
296 |
297 | };
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/js/filereader.js:
--------------------------------------------------------------------------------
1 | /*!
2 | FileReader.js - v0.99
3 | A lightweight wrapper for common FileReader usage.
4 | Copyright 2014 Brian Grinstead - MIT License.
5 | See http://github.com/bgrins/filereader.js for documentation.
6 | */
7 |
8 | (function(window, document) {
9 |
10 | var FileReader = window.FileReader;
11 | var FileReaderSyncSupport = false;
12 | var workerScript = "self.addEventListener('message', function(e) { var data=e.data; try { var reader = new FileReaderSync; postMessage({ result: reader[data.readAs](data.file), extra: data.extra, file: data.file})} catch(e){ postMessage({ result:'error', extra:data.extra, file:data.file}); } }, false);";
13 | var syncDetectionScript = "onmessage = function(e) { postMessage(!!FileReaderSync); };";
14 | var fileReaderEvents = ['loadstart', 'progress', 'load', 'abort', 'error', 'loadend'];
15 | var sync = false;
16 | var FileReaderJS = window.FileReaderJS = {
17 | enabled: false,
18 | setupInput: setupInput,
19 | setupDrop: setupDrop,
20 | setupClipboard: setupClipboard,
21 | setSync: function (value) {
22 | sync = value;
23 |
24 | if (sync && !FileReaderSyncSupport) {
25 | checkFileReaderSyncSupport();
26 | }
27 | },
28 | getSync: function() {
29 | return sync && FileReaderSyncSupport;
30 | },
31 | output: [],
32 | opts: {
33 | dragClass: "drag",
34 | accept: false,
35 | readAsDefault: 'DataURL',
36 | readAsMap: {
37 | },
38 | on: {
39 | loadstart: noop,
40 | progress: noop,
41 | load: noop,
42 | abort: noop,
43 | error: noop,
44 | loadend: noop,
45 | skip: noop,
46 | groupstart: noop,
47 | groupend: noop,
48 | beforestart: noop
49 | }
50 | }
51 | };
52 |
53 | // Setup jQuery plugin (if available)
54 | if (typeof(jQuery) !== "undefined") {
55 | jQuery.fn.fileReaderJS = function(opts) {
56 | return this.each(function() {
57 | if (jQuery(this).is("input")) {
58 | setupInput(this, opts);
59 | }
60 | else {
61 | setupDrop(this, opts);
62 | }
63 | });
64 | };
65 |
66 | jQuery.fn.fileClipboard = function(opts) {
67 | return this.each(function() {
68 | setupClipboard(this, opts);
69 | });
70 | };
71 | }
72 |
73 | // Not all browsers support the FileReader interface. Return with the enabled bit = false.
74 | if (!FileReader) {
75 | return;
76 | }
77 |
78 |
79 | // makeWorker is a little wrapper for generating web workers from strings
80 | function makeWorker(script) {
81 | var URL = window.URL || window.webkitURL;
82 | var Blob = window.Blob;
83 | var Worker = window.Worker;
84 |
85 | if (!URL || !Blob || !Worker || !script) {
86 | return null;
87 | }
88 |
89 | var blob = new Blob([script]);
90 | var worker = new Worker(URL.createObjectURL(blob));
91 | return worker;
92 | }
93 |
94 | // setupClipboard: bind to clipboard events (intended for document.body)
95 | function setupClipboard(element, opts) {
96 |
97 | if (!FileReaderJS.enabled) {
98 | return;
99 | }
100 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts);
101 |
102 | element.addEventListener("paste", onpaste, false);
103 |
104 | function onpaste(e) {
105 | var files = [];
106 | var clipboardData = e.clipboardData || {};
107 | var items = clipboardData.items || [];
108 |
109 | for (var i = 0; i < items.length; i++) {
110 | var file = items[i].getAsFile();
111 |
112 | if (file) {
113 |
114 | // Create a fake file name for images from clipboard, since this data doesn't get sent
115 | var matches = new RegExp("/\(.*\)").exec(file.type);
116 | if (!file.name && matches) {
117 | var extension = matches[1];
118 | file.name = "clipboard" + i + "." + extension;
119 | }
120 |
121 | files.push(file);
122 | }
123 | }
124 |
125 | if (files.length) {
126 | processFileList(e, files, instanceOptions);
127 | e.preventDefault();
128 | e.stopPropagation();
129 | }
130 | }
131 | }
132 |
133 | // setupInput: bind the 'change' event to an input[type=file]
134 | function setupInput(input, opts) {
135 |
136 | if (!FileReaderJS.enabled) {
137 | return;
138 | }
139 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts);
140 |
141 | input.addEventListener("change", inputChange, false);
142 | input.addEventListener("drop", inputDrop, false);
143 |
144 | function inputChange(e) {
145 | processFileList(e, input.files, instanceOptions);
146 | }
147 |
148 | function inputDrop(e) {
149 | e.stopPropagation();
150 | e.preventDefault();
151 | processFileList(e, e.dataTransfer.files, instanceOptions);
152 | }
153 | }
154 |
155 | // setupDrop: bind the 'drop' event for a DOM element
156 | function setupDrop(dropbox, opts) {
157 |
158 | if (!FileReaderJS.enabled) {
159 | return;
160 | }
161 | var instanceOptions = extend(extend({}, FileReaderJS.opts), opts);
162 | var dragClass = instanceOptions.dragClass;
163 | var initializedOnBody = false;
164 |
165 | // Bind drag events to the dropbox to add the class while dragging, and accept the drop data transfer.
166 | dropbox.addEventListener("dragenter", onlyWithFiles(dragenter), false);
167 | dropbox.addEventListener("dragleave", onlyWithFiles(dragleave), false);
168 | dropbox.addEventListener("dragover", onlyWithFiles(dragover), false);
169 | dropbox.addEventListener("drop", onlyWithFiles(drop), false);
170 |
171 | // Bind to body to prevent the dropbox events from firing when it was initialized on the page.
172 | document.body.addEventListener("dragstart", bodydragstart, true);
173 | document.body.addEventListener("dragend", bodydragend, true);
174 | document.body.addEventListener("drop", bodydrop, false);
175 |
176 | function bodydragend(e) {
177 | initializedOnBody = false;
178 | }
179 |
180 | function bodydragstart(e) {
181 | initializedOnBody = true;
182 | }
183 |
184 | function bodydrop(e) {
185 | if (e.dataTransfer.files && e.dataTransfer.files.length ){
186 | e.stopPropagation();
187 | e.preventDefault();
188 | }
189 | }
190 |
191 | function onlyWithFiles(fn) {
192 | return function() {
193 | if (!initializedOnBody) {
194 | fn.apply(this, arguments);
195 | }
196 | };
197 | }
198 |
199 | function drop(e) {
200 | e.stopPropagation();
201 | e.preventDefault();
202 | if (dragClass) {
203 | removeClass(dropbox, dragClass);
204 | }
205 | processFileList(e, e.dataTransfer.files, instanceOptions);
206 | }
207 |
208 | function dragenter(e) {
209 | e.stopPropagation();
210 | e.preventDefault();
211 | if (dragClass) {
212 | addClass(dropbox, dragClass);
213 | }
214 | }
215 |
216 | function dragleave(e) {
217 | if (dragClass) {
218 | removeClass(dropbox, dragClass);
219 | }
220 | }
221 |
222 | function dragover(e) {
223 | e.stopPropagation();
224 | e.preventDefault();
225 | if (dragClass) {
226 | addClass(dropbox, dragClass);
227 | }
228 | }
229 | }
230 |
231 | // setupCustomFileProperties: modify the file object with extra properties
232 | function setupCustomFileProperties(files, groupID) {
233 | for (var i = 0; i < files.length; i++) {
234 | var file = files[i];
235 | file.extra = {
236 | nameNoExtension: file.name.substring(0, file.name.lastIndexOf('.')),
237 | extension: file.name.substring(file.name.lastIndexOf('.') + 1),
238 | fileID: i,
239 | uniqueID: getUniqueID(),
240 | groupID: groupID,
241 | prettySize: prettySize(file.size)
242 | };
243 | }
244 | }
245 |
246 | // getReadAsMethod: return method name for 'readAs*' - http://www.w3.org/TR/FileAPI/#reading-a-file
247 | function getReadAsMethod(type, readAsMap, readAsDefault) {
248 | for (var r in readAsMap) {
249 | if (type.match(new RegExp(r))) {
250 | return 'readAs' + readAsMap[r];
251 | }
252 | }
253 | return 'readAs' + readAsDefault;
254 | }
255 |
256 | // processFileList: read the files with FileReader, send off custom events.
257 | function processFileList(e, files, opts) {
258 |
259 | var filesLeft = files.length;
260 | var group = {
261 | groupID: getGroupID(),
262 | files: files,
263 | started: new Date()
264 | };
265 |
266 | function groupEnd() {
267 | group.ended = new Date();
268 | opts.on.groupend(group);
269 | }
270 |
271 | function groupFileDone() {
272 | if (--filesLeft === 0) {
273 | groupEnd();
274 | }
275 | }
276 |
277 | FileReaderJS.output.push(group);
278 | setupCustomFileProperties(files, group.groupID);
279 |
280 | opts.on.groupstart(group);
281 |
282 | // No files in group - end immediately
283 | if (!files.length) {
284 | groupEnd();
285 | return;
286 | }
287 |
288 | var supportsSync = sync && FileReaderSyncSupport;
289 | var syncWorker;
290 |
291 | // Only initialize the synchronous worker if the option is enabled - to prevent the overhead
292 | if (supportsSync) {
293 | syncWorker = makeWorker(workerScript);
294 | syncWorker.onmessage = function(e) {
295 | var file = e.data.file;
296 | var result = e.data.result;
297 |
298 | // Workers seem to lose the custom property on the file object.
299 | if (!file.extra) {
300 | file.extra = e.data.extra;
301 | }
302 |
303 | file.extra.ended = new Date();
304 |
305 | // Call error or load event depending on success of the read from the worker.
306 | opts.on[result === "error" ? "error" : "load"]({ target: { result: result } }, file);
307 | groupFileDone();
308 | };
309 | }
310 |
311 | Array.prototype.forEach.call(files, function(file) {
312 |
313 | file.extra.started = new Date();
314 |
315 | if (opts.accept && !file.type.match(new RegExp(opts.accept))) {
316 | opts.on.skip(file);
317 | groupFileDone();
318 | return;
319 | }
320 |
321 | if (opts.on.beforestart(file) === false) {
322 | opts.on.skip(file);
323 | groupFileDone();
324 | return;
325 | }
326 |
327 | var readAs = getReadAsMethod(file.type, opts.readAsMap, opts.readAsDefault);
328 |
329 | if (syncWorker) {
330 | syncWorker.postMessage({
331 | file: file,
332 | extra: file.extra,
333 | readAs: readAs
334 | });
335 | }
336 | else {
337 |
338 | var reader = new FileReader();
339 | reader.originalEvent = e;
340 |
341 | fileReaderEvents.forEach(function(eventName) {
342 | reader['on' + eventName] = function(e) {
343 | if (eventName == 'load' || eventName == 'error') {
344 | file.extra.ended = new Date();
345 | }
346 | opts.on[eventName](e, file);
347 | if (eventName == 'loadend') {
348 | groupFileDone();
349 | }
350 | };
351 | });
352 | reader[readAs](file);
353 | }
354 | });
355 | }
356 |
357 | // checkFileReaderSyncSupport: Create a temporary worker and see if FileReaderSync exists
358 | function checkFileReaderSyncSupport() {
359 | var worker = makeWorker(syncDetectionScript);
360 | if (worker) {
361 | worker.onmessage =function(e) {
362 | FileReaderSyncSupport = e.data;
363 | };
364 | worker.postMessage({});
365 | }
366 | }
367 |
368 | // noop: do nothing
369 | function noop() {
370 |
371 | }
372 |
373 | // extend: used to make deep copies of options object
374 | function extend(destination, source) {
375 | for (var property in source) {
376 | if (source[property] && source[property].constructor &&
377 | source[property].constructor === Object) {
378 | destination[property] = destination[property] || {};
379 | arguments.callee(destination[property], source[property]);
380 | }
381 | else {
382 | destination[property] = source[property];
383 | }
384 | }
385 | return destination;
386 | }
387 |
388 | // hasClass: does an element have the css class?
389 | function hasClass(el, name) {
390 | return new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)").test(el.className);
391 | }
392 |
393 | // addClass: add the css class for the element.
394 | function addClass(el, name) {
395 | if (!hasClass(el, name)) {
396 | el.className = el.className ? [el.className, name].join(' ') : name;
397 | }
398 | }
399 |
400 | // removeClass: remove the css class from the element.
401 | function removeClass(el, name) {
402 | if (hasClass(el, name)) {
403 | var c = el.className;
404 | el.className = c.replace(new RegExp("(?:^|\\s+)" + name + "(?:\\s+|$)", "g"), " ").replace(/^\s\s*/, '').replace(/\s\s*$/, '');
405 | }
406 | }
407 |
408 | // prettySize: convert bytes to a more readable string.
409 | function prettySize(bytes) {
410 | var s = ['bytes', 'kb', 'MB', 'GB', 'TB', 'PB'];
411 | var e = Math.floor(Math.log(bytes)/Math.log(1024));
412 | return (bytes/Math.pow(1024, Math.floor(e))).toFixed(2)+" "+s[e];
413 | }
414 |
415 | // getGroupID: generate a unique int ID for groups.
416 | var getGroupID = (function(id) {
417 | return function() {
418 | return id++;
419 | };
420 | })(0);
421 |
422 | // getUniqueID: generate a unique int ID for files
423 | var getUniqueID = (function(id) {
424 | return function() {
425 | return id++;
426 | };
427 | })(0);
428 |
429 | // The interface is supported, bind the FileReaderJS callbacks
430 | FileReaderJS.enabled = true;
431 |
432 | })(this, document);
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | /* Helper methods */
2 | String.prototype.f = function () {
3 | var s = this,
4 | i = arguments.length;
5 |
6 | while (i--) {
7 | s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), arguments[i]);
8 | }
9 | return s;
10 | };
11 |
12 | String.prototype.repeat = function (num) {
13 | return new Array(num + 1).join(this);
14 | };
15 |
16 | function pushUnique(array, item) {
17 | if (array.indexOf(item) == -1) {
18 | array.push(item);
19 | return true;
20 | }
21 | return false;
22 | }
23 |
24 |
25 | function toBool(s, defValue) {
26 | if (typeof s === "undefined") {
27 | return typeof defValue !== "undefined" ? defValue : false;
28 | }
29 | return "false" !== s;
30 | }
31 |
32 | if (typeof String.prototype.endsWith !== 'function') {
33 | String.prototype.endsWith = function (suffix) {
34 | return this.indexOf(suffix, this.length - suffix.length) !== -1;
35 | };
36 | }
37 |
38 | if (typeof String.prototype.startsWith !== 'function') {
39 | String.prototype.startsWith = function (str) {
40 | return this.indexOf(str) == 0;
41 | };
42 | }
43 |
44 | jQuery.fn.reverse = [].reverse;
45 |
46 | /* ------ */
47 |
48 | var groupData = { groupSize:0, zip:null, log:[], errors:0, files:[]};
49 |
50 | var fileReaderOpts = {
51 | dragClass: "drag", readAsDefault: "Text", on: {
52 | load: function (e, file) {
53 | if (groupData.groupSize == 1) {
54 | loadFile(e, file, false)
55 | } else {
56 | groupData.files.push({e: e, file: file});
57 | }
58 | },
59 | groupstart: function (g) {
60 | resetGroupData();
61 | groupData.zip = new JSZip();
62 | groupData.groupSize = g.files.length;
63 | },
64 | groupend: function (g) {
65 | //Multiple files
66 | if (groupData.groupSize > 1) {
67 | groupData.groupSize = 0;
68 | var dlg = $('#dlg-files');
69 | var btnPrimary = dlg.find(".btn-primary");
70 |
71 | refreshSettings();
72 | dlg.find("#files-count").text(groupData.files.length);
73 | btnPrimary.text("Export");
74 | dlg.modal().on();
75 |
76 | btnPrimary.removeClass("disabled");
77 |
78 | btnPrimary.unbind("click");
79 | btnPrimary.on("click", function () {
80 | if (btnPrimary.hasClass("disabled")) return;
81 |
82 | btnPrimary.addClass("disabled");
83 | btnPrimary.text("Please wait ...");
84 |
85 | //To prevent UI lag
86 | setTimeout(function () {
87 | for (var i = 0; i < groupData.files.length; i++) {
88 | var f = groupData.files[i];
89 | loadFile(f.e, f.file, true);
90 | }
91 |
92 | groupData.log = "