├── CNAME ├── LICENSE ├── README.md ├── android-chrome-192x192.png ├── android-chrome-256x256.png ├── apple-touch-icon.png ├── browserconfig.xml ├── customizer.html ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── feather.png ├── index.html ├── ldom.dev.js ├── mstile-150x150.png ├── resources ├── FileSaver.js ├── blob.js ├── close.svg ├── main.css ├── menu.svg ├── prism │ ├── prism.css │ └── prism.js └── uglify │ ├── ast.js │ ├── compress.js │ ├── minify.js │ ├── output.js │ ├── parse.js │ ├── scope.js │ ├── transform.js │ └── utils.js ├── robots.txt ├── safari-pinned-tab.svg ├── site.webmanifest └── tests ├── index.html └── script.js /CNAME: -------------------------------------------------------------------------------- 1 | lightweightdom.com -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Creative Tech Guy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

LDOM

2 | 3 | A Lightweight JavaScript library to interact with the browser DOM. 4 | 5 | Full documentation, customizers, downloaders and examples available at [lightweightdom.com](https://lightweightdom.com). 6 | 7 |

Getting Started

8 | 9 | LDOM is a Lightweight (about 8% of the size of jQuery) way to interact with the browser DOM (Document Object Model). 10 | 11 | If you are familiar with jQuery, then LDOM will feel very similar. In many common use cases, LDOM can simply be a drop-in replacement to jQuery without any problems. One way that LDOM is so much smaller and faster than jQuery is that it doesn't support all of the ancient browsers that jQuery does. That being said, by using LDOM, here are the minimum browser versions that are supported. (Basically any browser since 2013) 12 | 13 | | Browser | Version | 14 | |---------|---------| 15 | | Chrome | 29 | 16 | | Firefox | 23 | 17 | | Safari | 6 | 18 | | IE | 9 | 19 | | Edge | all | 20 | | Opera | 12 | 21 | 22 |

Example Usage

23 |

Comparison between LDOM and Vanilla JavaScript

24 | 25 | --- 26 | 27 |

Set the text of all buttons.

28 | 29 | #### LDOM 30 | ```js 31 | $("button").text("I am a Button"); 32 | ``` 33 | #### Vanilla JavaScript 34 | ```js 35 | var buttons = document.querySelectorAll("button"); 36 | for (var i = 0; i < buttons.length; i++) { 37 | buttons[i].innerText = "I am a Button"; 38 | } 39 | ``` 40 | --- 41 | 42 |

Add a class to all parents of buttons.

43 | 44 | #### LDOM 45 | ```js 46 | $("button").parent().addClass("button-parent"); 47 | ``` 48 | #### Vanilla JavaScript 49 | ```js 50 | var buttons = document.querySelectorAll("button"); 51 | for (var i = 0; i < buttons.length; i++) { 52 | if (buttons[i].parentNode.className.split(" ").indexOf("button-parent") === -1) { 53 | buttons[i].parentNode.className += " " + "button-parent"; 54 | } 55 | } 56 | ``` 57 | --- 58 | 59 |

Add a click event to all buttons. Remove that event from a certain button.

60 | 61 | #### LDOM 62 | ```js 63 | var activateButtonEventId = $("button").on("click", function() { 64 | this.addClass("button-active"); 65 | }); 66 | $("button").eq(1).off(activateButtonEventId); 67 | ``` 68 | #### Vanilla JavaScript 69 | ```js 70 | var buttons = document.querySelectorAll("button"); 71 | for (var i = 0; i < buttons.length; i++) { 72 | buttons[i].addEventListener("click", addButtonActiveClass); 73 | } 74 | buttons[1].removeEventListener("click", addButtonActiveClass); 75 | 76 | function addButtonActiveClass(){ 77 | if (buttons[i].className.split(" ").indexOf("button-active") === -1) { 78 | buttons[i].className += " " + "button-active"; 79 | } 80 | } 81 | ``` 82 | --- 83 | 84 | 85 |

Create a text element and insert it after each button.

86 | 87 | #### LDOM 88 | ```js 89 | $("") 90 | .css("text-align", "center") 91 | .text("Click the button above!") 92 | .insertAfter($("button")); 93 | ``` 94 | #### Vanilla JavaScript 95 | ```js 96 | var textElem = document.createElement("text"); 97 | textElem.style.textAlign = "center"; 98 | textElem.innerText = "Click the button above!"; 99 | var buttons = document.querySelectorAll("button"); 100 | for (var i = 0; i < buttons.length; i++) { 101 | buttons[i].parentNode.insertBefore(textElem.cloneNode(true), buttons[i].nextSibling); 102 | } 103 | ``` 104 | --- 105 | 106 | ## Like what you see so far? 107 | ## Full documentation, customizers, downloaders and examples available at [lightweightdom.com](https://lightweightdom.com). 108 | -------------------------------------------------------------------------------- /android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreativeTechGuy/LDOM/8cddc30e94a7b6a01b1fcd39aa4c0ae5b43e25dc/android-chrome-192x192.png -------------------------------------------------------------------------------- /android-chrome-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreativeTechGuy/LDOM/8cddc30e94a7b6a01b1fcd39aa4c0ae5b43e25dc/android-chrome-256x256.png -------------------------------------------------------------------------------- /apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreativeTechGuy/LDOM/8cddc30e94a7b6a01b1fcd39aa4c0ae5b43e25dc/apple-touch-icon.png -------------------------------------------------------------------------------- /browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /customizer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | LDOM Customizer 32 | 33 | 34 | 35 | 77 | 78 | 111 | 112 | 113 | 118 |
119 |
120 |

LDOM Customizer

121 |
122 |
123 |

Customize LDOM Download

124 |
125 | After you have finished development, you can download a minified production version of LDOM here. You can run getLDOMFunctionUsage() in the browser console of your webpage to see which LDOM functions you don't use. Make sure you interact with all of the features of your webpage to get accurate usage results. 126 |
127 |
128 |
129 | 130 |
131 |
132 | Download ldom.min.js 133 |

Current Minified Size: Calculating...

134 |
135 | 136 |
137 |
138 |
139 | 140 | 141 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 258 | 259 | -------------------------------------------------------------------------------- /favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreativeTechGuy/LDOM/8cddc30e94a7b6a01b1fcd39aa4c0ae5b43e25dc/favicon-16x16.png -------------------------------------------------------------------------------- /favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreativeTechGuy/LDOM/8cddc30e94a7b6a01b1fcd39aa4c0ae5b43e25dc/favicon-32x32.png -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreativeTechGuy/LDOM/8cddc30e94a7b6a01b1fcd39aa4c0ae5b43e25dc/favicon.ico -------------------------------------------------------------------------------- /feather.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreativeTechGuy/LDOM/8cddc30e94a7b6a01b1fcd39aa4c0ae5b43e25dc/feather.png -------------------------------------------------------------------------------- /ldom.dev.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var LDOMCache = { 3 | eventListenerCounter: 0, 4 | eventListenerFunctions: {}, 5 | eventListenerFunctionsIds: {}, 6 | functionsUsed: {} 7 | }; 8 | 9 | function $(input) { 10 | if (typeof input === "string" && input[0] === "<" && input[input.length - 1] === ">") { 11 | return new LDOMObject(document.createElement(input.substring(1, input.length - 1))); 12 | } else if (input === null || !input) { 13 | return new LDOMObject([]); 14 | } else if (input._LDOM) { 15 | return input; 16 | } else if (input === window) { 17 | return new LDOMWindowObject(); 18 | } else if (input.nodeType > 0) { 19 | return new LDOMObject(input); 20 | } else if (typeof input !== "string" && typeof input.length !== "undefined") { 21 | var elements = []; 22 | for (var i = 0; i < input.length; i++) { 23 | var obj = $(input[i]); 24 | if (obj.length > 0) { 25 | elements.push(obj); 26 | } 27 | } 28 | return new LDOMObject(elements); 29 | } else { 30 | return $(document).find(input); 31 | } 32 | } 33 | window.LDOM = $; 34 | window.$ = window.$ || $; 35 | window.getLDOMFunctionUsage = function() { 36 | var obj = $(""); 37 | var keys = Object.keys(Object.getPrototypeOf(obj)); 38 | var unused = []; 39 | for (var i = 0; i < keys.length; i++) { 40 | if (keys[i][0] !== "_" && !LDOMCache.functionsUsed[keys[i]] && typeof obj[keys[i]] === "function") { 41 | unused.push(keys[i]); 42 | } 43 | } 44 | return { 45 | used: Object.keys(LDOMCache.functionsUsed), 46 | unused: unused 47 | }; 48 | }; 49 | 50 | function LDOMObject(elem) { 51 | this._LDOM = true; 52 | if (Array.isArray(elem)) { 53 | this.length = elem.length; 54 | this._elements = elem; 55 | } else { 56 | this.length = 1; 57 | this._node = elem; 58 | } 59 | } 60 | 61 | LDOMObject.prototype.each = each; 62 | LDOMObject.prototype.equals = equals; 63 | LDOMObject.prototype.find = find; 64 | LDOMObject.prototype.get = get; 65 | LDOMObject.prototype.on = on; 66 | LDOMObject.prototype.off = off; 67 | LDOMObject.prototype.trigger = trigger; 68 | LDOMObject.prototype.hide = hide; 69 | LDOMObject.prototype.show = show; 70 | LDOMObject.prototype.toggle = toggle; 71 | LDOMObject.prototype.css = css; 72 | LDOMObject.prototype.html = html; 73 | LDOMObject.prototype.outerHTML = outerHTML; 74 | LDOMObject.prototype.text = text; 75 | LDOMObject.prototype.prop = prop; 76 | LDOMObject.prototype.attr = attr; 77 | LDOMObject.prototype.removeAttr = removeAttr; 78 | LDOMObject.prototype.addClass = addClass; 79 | LDOMObject.prototype.removeClass = removeClass; 80 | LDOMObject.prototype.hasClass = hasClass; 81 | LDOMObject.prototype.parent = parent; 82 | LDOMObject.prototype.children = children; 83 | LDOMObject.prototype.filter = filter; 84 | LDOMObject.prototype.first = first; 85 | LDOMObject.prototype.last = last; 86 | LDOMObject.prototype.eq = eq; 87 | LDOMObject.prototype.insertAfter = insertAfter; 88 | LDOMObject.prototype.insertBefore = insertBefore; 89 | LDOMObject.prototype.appendChild = appendChild; 90 | LDOMObject.prototype.prependChild = prependChild; 91 | LDOMObject.prototype.remove = remove; 92 | 93 | function LDOMWindowObject() { 94 | this._LDOM = true; 95 | this.length = 1; 96 | this._node = window; 97 | } 98 | 99 | LDOMWindowObject.prototype.each = each; 100 | LDOMWindowObject.prototype.equals = equals; 101 | LDOMWindowObject.prototype.get = get; 102 | LDOMWindowObject.prototype.on = on; 103 | LDOMWindowObject.prototype.off = off; 104 | LDOMWindowObject.prototype.trigger = trigger; 105 | LDOMWindowObject.prototype.prop = prop; 106 | LDOMWindowObject.prototype.attr = attr; 107 | LDOMWindowObject.prototype.removeAttr = removeAttr; 108 | 109 | function each(funct, reverse) { 110 | LDOMCache.functionsUsed[arguments.callee.name] = true; 111 | var elementsArray = getElementsArray(this); 112 | var start = reverse ? elementsArray.length - 1 : 0; 113 | var change = reverse ? -1 : 1; 114 | var end = (reverse ? 0 : elementsArray.length - 1) + change; 115 | for (var i = start; i !== end; i += change) { 116 | var shouldContinue = funct.apply(elementsArray[i], [i]); 117 | if (shouldContinue === false) { 118 | break; 119 | } 120 | } 121 | return this; 122 | } 123 | 124 | function equals(ldomObject) { 125 | LDOMCache.functionsUsed[arguments.callee.name] = true; 126 | if (this.length !== ldomObject.length) { 127 | return false; 128 | } 129 | var thisElementsArray = getElementsArray(this); 130 | var otherElementsArray = getElementsArray(ldomObject); 131 | if (thisElementsArray.length !== otherElementsArray.length) { 132 | return false; 133 | } 134 | var otherNodes = []; 135 | for (var i = 0; i < otherElementsArray.length; i++) { 136 | otherNodes.push(otherElementsArray[i]._node); 137 | } 138 | for (var i = 0; i < thisElementsArray.length; i++) { 139 | if (otherNodes.indexOf(thisElementsArray[i]._node) === -1) { 140 | return false; 141 | } 142 | } 143 | return true; 144 | } 145 | 146 | function find(selector) { 147 | LDOMCache.functionsUsed[arguments.callee.name] = true; 148 | var output = []; 149 | this.each(function() { 150 | var elems = this._node.querySelectorAll(selector); 151 | for (var i = 0; i < elems.length; i++) { 152 | output.push(elems[i]); 153 | } 154 | }); 155 | return $(output); 156 | } 157 | 158 | function get(index) { 159 | LDOMCache.functionsUsed[arguments.callee.name] = true; 160 | if (!isDefined(index)) { 161 | var nodes = []; 162 | this.each(function() { 163 | nodes.push(this._node); 164 | }); 165 | return nodes; 166 | } else { 167 | var elementsArray = getElementsArray(this); 168 | if (index < 0) { 169 | index = elementsArray.length + index; 170 | } 171 | if (!isDefined(elementsArray[index])) { 172 | return null; 173 | } 174 | return elementsArray[index]._node; 175 | } 176 | } 177 | 178 | function on(eventName, handler) { 179 | LDOMCache.functionsUsed[arguments.callee.name] = true; 180 | var eventId = ++LDOMCache.eventListenerCounter; 181 | var handlerWrapper = function(evt) { 182 | handler.apply($(this), [evt]); 183 | }; 184 | this.each(function() { 185 | this._node.addEventListener(eventName, handlerWrapper); 186 | var eventIds = this._node._LDOMEvents || []; 187 | eventIds.push(eventId); 188 | this._node._LDOMEvents = eventIds; 189 | }); 190 | if (!LDOMCache.eventListenerFunctions[eventName]) { 191 | LDOMCache.eventListenerFunctions[eventName] = {}; 192 | } 193 | LDOMCache.eventListenerFunctions[eventName][eventId] = { 194 | funct: handlerWrapper, 195 | count: this.length 196 | }; 197 | LDOMCache.eventListenerFunctionsIds[eventId] = { 198 | name: eventName 199 | }; 200 | return eventId; 201 | } 202 | 203 | function off(eventName) { 204 | LDOMCache.functionsUsed[arguments.callee.name] = true; 205 | if (!isDefined(eventName)) { 206 | this.each(function() { 207 | var eventIds = this._node._LDOMEvents || []; 208 | for (var i = eventIds.length - 1; i >= 0; i--) { 209 | this.off(eventIds[i]); 210 | } 211 | }); 212 | } else if (typeof eventName === "string") { 213 | this.each(function() { 214 | if (!LDOMCache.eventListenerFunctions[eventName]) { 215 | return; 216 | } 217 | var eventIds = this._node._LDOMEvents || []; 218 | for (var i = eventIds.length - 1; i >= 0; i--) { 219 | if (LDOMCache.eventListenerFunctionsIds[eventIds[i]].name === eventName) { 220 | this.off(eventIds[i]); 221 | } 222 | } 223 | }); 224 | } else if (typeof eventName === "number") { 225 | var eventId = eventName; 226 | this.each(function() { 227 | if (!LDOMCache.eventListenerFunctionsIds[eventId]) { 228 | return; 229 | } 230 | var eventName = LDOMCache.eventListenerFunctionsIds[eventId].name; 231 | if (!LDOMCache.eventListenerFunctions[eventName][eventId]) { 232 | return; 233 | } 234 | var event = LDOMCache.eventListenerFunctions[eventName][eventId]; 235 | this._node.removeEventListener(eventName, event.funct); 236 | var eventIds = this._node._LDOMEvents || []; 237 | eventIds.splice(eventIds.indexOf(eventId), 1); 238 | if (eventIds.length === 0) { 239 | delete this._node._LDOMEvents; 240 | } else { 241 | this._node._LDOMEvents = eventIds; 242 | } 243 | if (--event.count === 0) { 244 | delete LDOMCache.eventListenerFunctions[eventName][eventId]; 245 | if (Object.keys(LDOMCache.eventListenerFunctions[eventName]).length === 0) { 246 | delete LDOMCache.eventListenerFunctions[eventName]; 247 | } 248 | delete LDOMCache.eventListenerFunctionsIds[eventId]; 249 | } 250 | }); 251 | } 252 | } 253 | 254 | function trigger(eventName, data) { 255 | LDOMCache.functionsUsed[arguments.callee.name] = true; 256 | var event = document.createEvent("Event"); 257 | event.initEvent(eventName, true, true); 258 | for (var key in (data || {})) { 259 | event[key] = data[key]; 260 | } 261 | this.each(function() { 262 | var that = this; 263 | setTimeout(function() { 264 | that._node.dispatchEvent(event); 265 | }, 0); 266 | }); 267 | return this; 268 | } 269 | 270 | function hide() { 271 | LDOMCache.functionsUsed[arguments.callee.name] = true; 272 | this.each(function() { 273 | if (this._node.style.display === "none") { 274 | return; 275 | } 276 | var isImportant = false; 277 | if (this._node.style.display !== "") { 278 | this._node.setAttribute("data-LDOM-hidden-previous-display", this._node.style.display); 279 | if (this._node.style.getPropertyPriority("display") === "important") { 280 | this._node.setAttribute("data-LDOM-hidden-previous-display-important", "true"); 281 | isImportant = true; 282 | } 283 | } 284 | this._node.style.setProperty("display", "none", isImportant ? "important" : ""); 285 | }); 286 | return this; 287 | } 288 | 289 | function show() { 290 | LDOMCache.functionsUsed[arguments.callee.name] = true; 291 | this.each(function() { 292 | if (this._node.hasAttribute("data-LDOM-hidden-previous-display")) { 293 | this._node.style.setProperty("display", this._node.getAttribute("data-LDOM-hidden-previous-display"), this._node.hasAttribute("data-LDOM-hidden-previous-display-important") ? "important" : ""); 294 | this._node.removeAttribute("data-LDOM-hidden-previous-display"); 295 | this._node.removeAttribute("data-LDOM-hidden-previous-display-important"); 296 | } else if (this._node.style.display === "none") { 297 | this._node.style.display = ""; 298 | } 299 | }); 300 | return this; 301 | } 302 | 303 | function toggle(show) { 304 | LDOMCache.functionsUsed[arguments.callee.name] = true; 305 | this.each(function() { 306 | var shouldShow = this._node.hasAttribute("data-LDOM-hidden-previous-display") || this._node.style.display === "none"; 307 | if (isDefined(show)) { 308 | shouldShow = show; 309 | } 310 | if (shouldShow) { 311 | this.show() 312 | } else { 313 | this.hide(); 314 | } 315 | }); 316 | return this; 317 | } 318 | 319 | function css(property, value, isImportant) { 320 | LDOMCache.functionsUsed[arguments.callee.name] = true; 321 | if (!isDefined(value)) { 322 | var elementsArray = getElementsArray(this); 323 | return elementsArray.length > 0 ? window.getComputedStyle(elementsArray[0]._node)[property] : ""; 324 | } else { 325 | this.each(function() { 326 | this._node.style.setProperty(property, value, isImportant ? "important" : ""); 327 | }); 328 | return this; 329 | } 330 | } 331 | 332 | function html(htmlString) { 333 | LDOMCache.functionsUsed[arguments.callee.name] = true; 334 | if (!isDefined(htmlString)) { 335 | var elementsArray = getElementsArray(this); 336 | return elementsArray.length > 0 ? elementsArray[0]._node.innerHTML : ""; 337 | } else { 338 | return setPropertyAndRemoveDetachedNodes(this, "innerHTML", htmlString); 339 | } 340 | } 341 | 342 | function outerHTML(htmlString) { 343 | LDOMCache.functionsUsed[arguments.callee.name] = true; 344 | if (!isDefined(htmlString)) { 345 | var elementsArray = getElementsArray(this); 346 | return elementsArray.length > 0 ? elementsArray[0]._node.outerHTML : ""; 347 | } else { 348 | return setPropertyAndRemoveDetachedNodes(this, "outerHTML", htmlString); 349 | } 350 | } 351 | 352 | function text(textString) { 353 | LDOMCache.functionsUsed[arguments.callee.name] = true; 354 | if (!isDefined(textString)) { 355 | var elementsArray = getElementsArray(this); 356 | return elementsArray.length > 0 ? elementsArray[0]._node.innerText : ""; 357 | } else { 358 | return setPropertyAndRemoveDetachedNodes(this, "innerText", textString); 359 | } 360 | } 361 | 362 | function prop(propertyName, value) { 363 | LDOMCache.functionsUsed[arguments.callee.name] = true; 364 | if (!isDefined(value)) { 365 | var elementsArray = getElementsArray(this); 366 | return elementsArray.length > 0 ? elementsArray[0]._node[propertyName] : ""; 367 | } else { 368 | this.each(function() { 369 | this._node[propertyName] = value; 370 | }); 371 | return this; 372 | } 373 | } 374 | 375 | function attr(attributeName, value) { 376 | LDOMCache.functionsUsed[arguments.callee.name] = true; 377 | if (!isDefined(value)) { 378 | var elementsArray = getElementsArray(this); 379 | return elementsArray.length > 0 ? elementsArray[0]._node.getAttribute(attributeName) : ""; 380 | } else { 381 | this.each(function() { 382 | this._node.setAttribute(attributeName, value); 383 | }); 384 | return this; 385 | } 386 | } 387 | 388 | function removeAttr(attributeName) { 389 | LDOMCache.functionsUsed[arguments.callee.name] = true; 390 | this.each(function() { 391 | this._node.removeAttribute(attributeName); 392 | }); 393 | return this; 394 | } 395 | 396 | function addClass(className) { 397 | LDOMCache.functionsUsed[arguments.callee.name] = true; 398 | this.each(function() { 399 | var classes = (this._node.getAttribute("class") || "").split(" "); 400 | var newClasses = className.split(" "); 401 | for (var i = 0; i < newClasses.length; i++) { 402 | if (classes.indexOf(newClasses[i]) === -1) { 403 | classes.push(newClasses[i]); 404 | } 405 | } 406 | this._node.setAttribute("class", classes.join(" ").trim()); 407 | }); 408 | return this; 409 | } 410 | 411 | function removeClass(className) { 412 | LDOMCache.functionsUsed[arguments.callee.name] = true; 413 | if (!isDefined(className)) { 414 | this.each(function() { 415 | this._node.removeAttribute("class"); 416 | }); 417 | } else { 418 | this.each(function() { 419 | var classes = (this._node.getAttribute("class") || "").split(" "); 420 | var newClasses = className.split(" "); 421 | for (var i = 0; i < newClasses.length; i++) { 422 | var classIndex = classes.indexOf(newClasses[i]); 423 | if (classIndex !== -1) { 424 | classes.splice(classIndex, 1); 425 | } 426 | } 427 | this._node.setAttribute("class", classes.join(" ").trim()); 428 | }); 429 | } 430 | return this; 431 | } 432 | 433 | function hasClass(className, all) { 434 | LDOMCache.functionsUsed[arguments.callee.name] = true; 435 | var doesHaveClass = false; 436 | this.each(function() { 437 | var classes = (this._node.getAttribute("class") || "").split(" "); 438 | if (classes.indexOf(className) !== -1) { 439 | doesHaveClass = true; 440 | if (!all) { 441 | return false; 442 | } 443 | } else { 444 | doesHaveClass = false; 445 | if (all) { 446 | return false; 447 | } 448 | } 449 | }); 450 | return doesHaveClass; 451 | } 452 | 453 | function parent() { 454 | LDOMCache.functionsUsed[arguments.callee.name] = true; 455 | var output = []; 456 | this.each(function() { 457 | if (output.indexOf(this._node.parentNode) === -1) { 458 | output.push(this._node.parentNode); 459 | } 460 | }); 461 | return $(output); 462 | } 463 | 464 | function children() { 465 | LDOMCache.functionsUsed[arguments.callee.name] = true; 466 | var output = []; 467 | this.each(function() { 468 | var elems = this._node.children; 469 | for (var i = 0; i < elems.length; i++) { 470 | output.push(elems[i]); 471 | } 472 | }); 473 | return $(output); 474 | } 475 | 476 | function filter(selector) { 477 | LDOMCache.functionsUsed[arguments.callee.name] = true; 478 | if (!selector) { 479 | return $([]); 480 | } 481 | var matchesMethod = "matches"; 482 | if (Element.prototype.matches) { 483 | matchesMethod = "matches"; 484 | } else if (Element.prototype.matchesSelector) { 485 | matchesMethod = "matchesSelector"; 486 | } else if (Element.prototype.msMatchesSelector) { 487 | matchesMethod = "msMatchesSelector"; 488 | } else if (Element.prototype.webkitMatchesSelector) { 489 | matchesMethod = "webkitMatchesSelector"; 490 | } 491 | var output = []; 492 | this.each(function() { 493 | if (this._node[matchesMethod](selector)) { 494 | output.push(this); 495 | } 496 | }); 497 | return $(output); 498 | } 499 | 500 | function first() { 501 | LDOMCache.functionsUsed[arguments.callee.name] = true; 502 | var elementsArray = getElementsArray(this); 503 | if (elementsArray.length === 0) { 504 | return $([]); 505 | } 506 | return elementsArray[0]; 507 | } 508 | 509 | function last() { 510 | LDOMCache.functionsUsed[arguments.callee.name] = true; 511 | var elementsArray = getElementsArray(this); 512 | if (elementsArray.length === 0) { 513 | return $([]); 514 | } 515 | return elementsArray[elementsArray.length - 1]; 516 | } 517 | 518 | function eq(index) { 519 | LDOMCache.functionsUsed[arguments.callee.name] = true; 520 | var elementsArray = getElementsArray(this); 521 | if (index < 0) { 522 | index = elementsArray.length + index; 523 | } 524 | if (!isDefined(elementsArray[index])) { 525 | return $([]); 526 | } 527 | return elementsArray[index]; 528 | } 529 | 530 | function insertAfter(ldomObjectTarget) { 531 | LDOMCache.functionsUsed[arguments.callee.name] = true; 532 | this.each(function() { 533 | var callingNode = this._node; 534 | ldomObjectTarget.each(function() { 535 | this._node.parentNode.insertBefore(callingNode.cloneNode(true), this._node.nextSibling); 536 | }); 537 | }, true); 538 | return this; 539 | } 540 | 541 | function insertBefore(ldomObjectTarget) { 542 | LDOMCache.functionsUsed[arguments.callee.name] = true; 543 | this.each(function() { 544 | var callingNode = this._node; 545 | ldomObjectTarget.each(function() { 546 | this._node.parentNode.insertBefore(callingNode.cloneNode(true), this._node); 547 | }); 548 | }); 549 | return this; 550 | } 551 | 552 | function appendChild(childElement) { 553 | LDOMCache.functionsUsed[arguments.callee.name] = true; 554 | this.each(function() { 555 | var callingNode = this._node; 556 | childElement.each(function() { 557 | callingNode.appendChild(this._node.cloneNode(true)); 558 | }); 559 | }); 560 | return this; 561 | } 562 | 563 | function prependChild(childElement) { 564 | LDOMCache.functionsUsed[arguments.callee.name] = true; 565 | this.each(function() { 566 | var callingNode = this._node; 567 | childElement.each(function() { 568 | callingNode.insertBefore(this._node.cloneNode(true), callingNode.firstChild); 569 | }, true); 570 | }); 571 | return this; 572 | } 573 | 574 | function remove() { 575 | LDOMCache.functionsUsed[arguments.callee.name] = true; 576 | this.each(function() { 577 | if (isDefined(this.off)) { 578 | this.off(); 579 | } 580 | this._node.parentNode.removeChild(this._node); 581 | }); 582 | } 583 | 584 | // Internal Helper Functions 585 | 586 | function setPropertyAndRemoveDetachedNodes(ldomObject, property, value) { 587 | ldomObject.each(function() { 588 | this.inPage = document.body.contains(this._node); 589 | }); 590 | ldomObject.each(function() { 591 | this._node[property] = value; 592 | }); 593 | var output = []; 594 | ldomObject.each(function() { 595 | if (!this.inPage || document.body.contains(this._node)) { 596 | output.push(this); 597 | } 598 | delete this.inPage; 599 | }); 600 | return $(output); 601 | } 602 | 603 | function getElementsArray(obj) { 604 | if (obj._elements) { 605 | return obj._elements; 606 | } else { 607 | return [obj]; 608 | } 609 | } 610 | 611 | function isDefined(obj) { 612 | if (typeof obj === "undefined") { 613 | return false; 614 | } 615 | return true; 616 | } 617 | })(); 618 | -------------------------------------------------------------------------------- /mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/CreativeTechGuy/LDOM/8cddc30e94a7b6a01b1fcd39aa4c0ae5b43e25dc/mstile-150x150.png -------------------------------------------------------------------------------- /resources/FileSaver.js: -------------------------------------------------------------------------------- 1 | /* FileSaver.js 2 | * A saveAs() FileSaver implementation. 3 | * 1.3.8 4 | * 2018-03-22 14:03:47 5 | * 6 | * By Eli Grey, https://eligrey.com 7 | * License: MIT 8 | * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md 9 | */ 10 | 11 | /*global self */ 12 | /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */ 13 | 14 | /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/src/FileSaver.js */ 15 | 16 | var saveAs = saveAs || (function(view) { 17 | "use strict"; 18 | // IE <10 is explicitly unsupported 19 | if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { 20 | return; 21 | } 22 | var 23 | doc = view.document 24 | // only get URL when necessary in case Blob.js hasn't overridden it yet 25 | , get_URL = function() { 26 | return view.URL || view.webkitURL || view; 27 | } 28 | , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") 29 | , can_use_save_link = "download" in save_link 30 | , click = function(node) { 31 | var event = new MouseEvent("click"); 32 | node.dispatchEvent(event); 33 | } 34 | , is_safari = /constructor/i.test(view.HTMLElement) || view.safari 35 | , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent) 36 | , setImmediate = view.setImmediate || view.setTimeout 37 | , throw_outside = function(ex) { 38 | setImmediate(function() { 39 | throw ex; 40 | }, 0); 41 | } 42 | , force_saveable_type = "application/octet-stream" 43 | // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to 44 | , arbitrary_revoke_timeout = 1000 * 40 // in ms 45 | , revoke = function(file) { 46 | var revoker = function() { 47 | if (typeof file === "string") { // file is an object URL 48 | get_URL().revokeObjectURL(file); 49 | } else { // file is a File 50 | file.remove(); 51 | } 52 | }; 53 | setTimeout(revoker, arbitrary_revoke_timeout); 54 | } 55 | , dispatch = function(filesaver, event_types, event) { 56 | event_types = [].concat(event_types); 57 | var i = event_types.length; 58 | while (i--) { 59 | var listener = filesaver["on" + event_types[i]]; 60 | if (typeof listener === "function") { 61 | try { 62 | listener.call(filesaver, event || filesaver); 63 | } catch (ex) { 64 | throw_outside(ex); 65 | } 66 | } 67 | } 68 | } 69 | , auto_bom = function(blob) { 70 | // prepend BOM for UTF-8 XML and text/* types (including HTML) 71 | // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF 72 | if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { 73 | return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}); 74 | } 75 | return blob; 76 | } 77 | , FileSaver = function(blob, name, no_auto_bom) { 78 | if (!no_auto_bom) { 79 | blob = auto_bom(blob); 80 | } 81 | // First try a.download, then web filesystem, then object URLs 82 | var 83 | filesaver = this 84 | , type = blob.type 85 | , force = type === force_saveable_type 86 | , object_url 87 | , dispatch_all = function() { 88 | dispatch(filesaver, "writestart progress write writeend".split(" ")); 89 | } 90 | // on any filesys errors revert to saving with object URLs 91 | , fs_error = function() { 92 | if ((is_chrome_ios || (force && is_safari)) && view.FileReader) { 93 | // Safari doesn't allow downloading of blob urls 94 | var reader = new FileReader(); 95 | reader.onloadend = function() { 96 | var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;'); 97 | var popup = view.open(url, '_blank'); 98 | if(!popup) view.location.href = url; 99 | url=undefined; // release reference before dispatching 100 | filesaver.readyState = filesaver.DONE; 101 | dispatch_all(); 102 | }; 103 | reader.readAsDataURL(blob); 104 | filesaver.readyState = filesaver.INIT; 105 | return; 106 | } 107 | // don't create more object URLs than needed 108 | if (!object_url) { 109 | object_url = get_URL().createObjectURL(blob); 110 | } 111 | if (force) { 112 | view.location.href = object_url; 113 | } else { 114 | var opened = view.open(object_url, "_blank"); 115 | if (!opened) { 116 | // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html 117 | view.location.href = object_url; 118 | } 119 | } 120 | filesaver.readyState = filesaver.DONE; 121 | dispatch_all(); 122 | revoke(object_url); 123 | } 124 | ; 125 | filesaver.readyState = filesaver.INIT; 126 | 127 | if (can_use_save_link) { 128 | object_url = get_URL().createObjectURL(blob); 129 | setImmediate(function() { 130 | save_link.href = object_url; 131 | save_link.download = name; 132 | click(save_link); 133 | dispatch_all(); 134 | revoke(object_url); 135 | filesaver.readyState = filesaver.DONE; 136 | }, 0); 137 | return; 138 | } 139 | 140 | fs_error(); 141 | } 142 | , FS_proto = FileSaver.prototype 143 | , saveAs = function(blob, name, no_auto_bom) { 144 | return new FileSaver(blob, name || blob.name || "download", no_auto_bom); 145 | } 146 | ; 147 | 148 | // IE 10+ (native saveAs) 149 | if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { 150 | return function(blob, name, no_auto_bom) { 151 | name = name || blob.name || "download"; 152 | 153 | if (!no_auto_bom) { 154 | blob = auto_bom(blob); 155 | } 156 | return navigator.msSaveOrOpenBlob(blob, name); 157 | }; 158 | } 159 | 160 | // todo: detect chrome extensions & packaged apps 161 | //save_link.target = "_blank"; 162 | 163 | FS_proto.abort = function(){}; 164 | FS_proto.readyState = FS_proto.INIT = 0; 165 | FS_proto.WRITING = 1; 166 | FS_proto.DONE = 2; 167 | 168 | FS_proto.error = 169 | FS_proto.onwritestart = 170 | FS_proto.onprogress = 171 | FS_proto.onwrite = 172 | FS_proto.onabort = 173 | FS_proto.onerror = 174 | FS_proto.onwriteend = 175 | null; 176 | 177 | return saveAs; 178 | }( 179 | typeof self !== "undefined" && self 180 | || typeof window !== "undefined" && window 181 | || this 182 | )); -------------------------------------------------------------------------------- /resources/blob.js: -------------------------------------------------------------------------------- 1 | /* Blob.js 2 | * A Blob, File, FileReader & URL implementation. 3 | * 2018-08-09 4 | * 5 | * By Eli Grey, http://eligrey.com 6 | * By Jimmy Wärting, https://github.com/jimmywarting 7 | * License: MIT 8 | * See https://github.com/eligrey/Blob.js/blob/master/LICENSE.md 9 | */ 10 | 11 | ;(function(){ 12 | 13 | var global = typeof window === 'object' 14 | ? window : typeof self === 'object' 15 | ? self : this 16 | 17 | var BlobBuilder = global.BlobBuilder 18 | || global.WebKitBlobBuilder 19 | || global.MSBlobBuilder 20 | || global.MozBlobBuilder; 21 | 22 | global.URL = global.URL || global.webkitURL || function(href, a) { 23 | a = document.createElement('a') 24 | a.href = href 25 | return a 26 | } 27 | 28 | var origBlob = global.Blob 29 | var createObjectURL = URL.createObjectURL 30 | var revokeObjectURL = URL.revokeObjectURL 31 | var strTag = global.Symbol && global.Symbol.toStringTag 32 | var blobSupported = false 33 | var blobSupportsArrayBufferView = false 34 | var arrayBufferSupported = !!global.ArrayBuffer 35 | var blobBuilderSupported = BlobBuilder 36 | && BlobBuilder.prototype.append 37 | && BlobBuilder.prototype.getBlob; 38 | 39 | try { 40 | // Check if Blob constructor is supported 41 | blobSupported = new Blob(['ä']).size === 2 42 | 43 | // Check if Blob constructor supports ArrayBufferViews 44 | // Fails in Safari 6, so we need to map to ArrayBuffers there. 45 | blobSupportsArrayBufferView = new Blob([new Uint8Array([1,2])]).size === 2 46 | } catch(e) {} 47 | 48 | /** 49 | * Helper function that maps ArrayBufferViews to ArrayBuffers 50 | * Used by BlobBuilder constructor and old browsers that didn't 51 | * support it in the Blob constructor. 52 | */ 53 | function mapArrayBufferViews(ary) { 54 | return ary.map(function(chunk) { 55 | if (chunk.buffer instanceof ArrayBuffer) { 56 | var buf = chunk.buffer; 57 | 58 | // if this is a subarray, make a copy so we only 59 | // include the subarray region from the underlying buffer 60 | if (chunk.byteLength !== buf.byteLength) { 61 | var copy = new Uint8Array(chunk.byteLength); 62 | copy.set(new Uint8Array(buf, chunk.byteOffset, chunk.byteLength)); 63 | buf = copy.buffer; 64 | } 65 | 66 | return buf; 67 | } 68 | 69 | return chunk; 70 | }); 71 | } 72 | 73 | function BlobBuilderConstructor(ary, options) { 74 | options = options || {}; 75 | 76 | var bb = new BlobBuilder(); 77 | mapArrayBufferViews(ary).forEach(function(part) { 78 | bb.append(part); 79 | }); 80 | 81 | return options.type ? bb.getBlob(options.type) : bb.getBlob(); 82 | }; 83 | 84 | function BlobConstructor(ary, options) { 85 | return new origBlob(mapArrayBufferViews(ary), options || {}); 86 | }; 87 | 88 | if (global.Blob) { 89 | BlobBuilderConstructor.prototype = Blob.prototype; 90 | BlobConstructor.prototype = Blob.prototype; 91 | } 92 | 93 | function FakeBlobBuilder() { 94 | function toUTF8Array(str) { 95 | var utf8 = []; 96 | for (var i=0; i < str.length; i++) { 97 | var charcode = str.charCodeAt(i); 98 | if (charcode < 0x80) utf8.push(charcode); 99 | else if (charcode < 0x800) { 100 | utf8.push(0xc0 | (charcode >> 6), 101 | 0x80 | (charcode & 0x3f)); 102 | } 103 | else if (charcode < 0xd800 || charcode >= 0xe000) { 104 | utf8.push(0xe0 | (charcode >> 12), 105 | 0x80 | ((charcode>>6) & 0x3f), 106 | 0x80 | (charcode & 0x3f)); 107 | } 108 | // surrogate pair 109 | else { 110 | i++; 111 | // UTF-16 encodes 0x10000-0x10FFFF by 112 | // subtracting 0x10000 and splitting the 113 | // 20 bits of 0x0-0xFFFFF into two halves 114 | charcode = 0x10000 + (((charcode & 0x3ff)<<10) 115 | | (str.charCodeAt(i) & 0x3ff)); 116 | utf8.push(0xf0 | (charcode >>18), 117 | 0x80 | ((charcode>>12) & 0x3f), 118 | 0x80 | ((charcode>>6) & 0x3f), 119 | 0x80 | (charcode & 0x3f)); 120 | } 121 | } 122 | return utf8; 123 | } 124 | function fromUtf8Array(array) { 125 | var out, i, len, c; 126 | var char2, char3; 127 | 128 | out = ""; 129 | len = array.length; 130 | i = 0; 131 | while (i < len) { 132 | c = array[i++]; 133 | switch (c >> 4) 134 | { 135 | case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: 136 | // 0xxxxxxx 137 | out += String.fromCharCode(c); 138 | break; 139 | case 12: case 13: 140 | // 110x xxxx 10xx xxxx 141 | char2 = array[i++]; 142 | out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); 143 | break; 144 | case 14: 145 | // 1110 xxxx 10xx xxxx 10xx xxxx 146 | char2 = array[i++]; 147 | char3 = array[i++]; 148 | out += String.fromCharCode(((c & 0x0F) << 12) | 149 | ((char2 & 0x3F) << 6) | 150 | ((char3 & 0x3F) << 0)); 151 | break; 152 | } 153 | } 154 | return out; 155 | } 156 | function isDataView(obj) { 157 | return obj && DataView.prototype.isPrototypeOf(obj) 158 | } 159 | function bufferClone(buf) { 160 | var view = new Array(buf.byteLength) 161 | var array = new Uint8Array(buf) 162 | var i = view.length 163 | while(i--) { 164 | view[i] = array[i] 165 | } 166 | return view 167 | } 168 | function encodeByteArray(input) { 169 | var byteToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' 170 | 171 | var output = []; 172 | 173 | for (var i = 0; i < input.length; i += 3) { 174 | var byte1 = input[i]; 175 | var haveByte2 = i + 1 < input.length; 176 | var byte2 = haveByte2 ? input[i + 1] : 0; 177 | var haveByte3 = i + 2 < input.length; 178 | var byte3 = haveByte3 ? input[i + 2] : 0; 179 | 180 | var outByte1 = byte1 >> 2; 181 | var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4); 182 | var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6); 183 | var outByte4 = byte3 & 0x3F; 184 | 185 | if (!haveByte3) { 186 | outByte4 = 64; 187 | 188 | if (!haveByte2) { 189 | outByte3 = 64; 190 | } 191 | } 192 | 193 | output.push( 194 | byteToCharMap[outByte1], byteToCharMap[outByte2], 195 | byteToCharMap[outByte3], byteToCharMap[outByte4]) 196 | } 197 | 198 | return output.join('') 199 | } 200 | 201 | var create = Object.create || function (a) { 202 | function c() {} 203 | c.prototype = a; 204 | return new c 205 | } 206 | 207 | if (arrayBufferSupported) { 208 | var viewClasses = [ 209 | '[object Int8Array]', 210 | '[object Uint8Array]', 211 | '[object Uint8ClampedArray]', 212 | '[object Int16Array]', 213 | '[object Uint16Array]', 214 | '[object Int32Array]', 215 | '[object Uint32Array]', 216 | '[object Float32Array]', 217 | '[object Float64Array]' 218 | ] 219 | 220 | var isArrayBufferView = ArrayBuffer.isView || function(obj) { 221 | return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1 222 | } 223 | } 224 | 225 | 226 | 227 | /********************************************************/ 228 | /* Blob constructor */ 229 | /********************************************************/ 230 | function Blob(chunks, opts) { 231 | chunks = chunks || [] 232 | for (var i = 0, len = chunks.length; i < len; i++) { 233 | var chunk = chunks[i] 234 | if (chunk instanceof Blob) { 235 | chunks[i] = chunk._buffer 236 | } else if (typeof chunk === 'string') { 237 | chunks[i] = toUTF8Array(chunk) 238 | } else if (arrayBufferSupported && (ArrayBuffer.prototype.isPrototypeOf(chunk) || isArrayBufferView(chunk))) { 239 | chunks[i] = bufferClone(chunk) 240 | } else if (arrayBufferSupported && isDataView(chunk)) { 241 | chunks[i] = bufferClone(chunk.buffer) 242 | } else { 243 | chunks[i] = toUTF8Array(String(chunk)) 244 | } 245 | } 246 | 247 | this._buffer = [].concat.apply([], chunks) 248 | this.size = this._buffer.length 249 | this.type = opts ? opts.type || '' : '' 250 | } 251 | 252 | Blob.prototype.slice = function(start, end, type) { 253 | var slice = this._buffer.slice(start || 0, end || this._buffer.length) 254 | return new Blob([slice], {type: type}) 255 | } 256 | 257 | Blob.prototype.toString = function() { 258 | return '[object Blob]' 259 | } 260 | 261 | 262 | 263 | /********************************************************/ 264 | /* File constructor */ 265 | /********************************************************/ 266 | function File(chunks, name, opts) { 267 | opts = opts || {} 268 | var a = Blob.call(this, chunks, opts) || this 269 | a.name = name 270 | a.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date 271 | a.lastModified = +a.lastModifiedDate 272 | 273 | return a 274 | } 275 | 276 | File.prototype = create(Blob.prototype); 277 | File.prototype.constructor = File; 278 | 279 | if (Object.setPrototypeOf) 280 | Object.setPrototypeOf(File, Blob); 281 | else { 282 | try {File.__proto__ = Blob} catch (e) {} 283 | } 284 | 285 | File.prototype.toString = function() { 286 | return '[object File]' 287 | } 288 | 289 | 290 | /********************************************************/ 291 | /* FileReader constructor */ 292 | /********************************************************/ 293 | function FileReader() { 294 | if (!(this instanceof FileReader)) 295 | throw new TypeError("Failed to construct 'FileReader': Please use the 'new' operator, this DOM object constructor cannot be called as a function.") 296 | 297 | var delegate = document.createDocumentFragment() 298 | this.addEventListener = delegate.addEventListener 299 | this.dispatchEvent = function(evt) { 300 | var local = this['on' + evt.type] 301 | if (typeof local === 'function') local(evt) 302 | delegate.dispatchEvent(evt) 303 | } 304 | this.removeEventListener = delegate.removeEventListener 305 | } 306 | 307 | function _read(fr, blob, kind) { 308 | if (!(blob instanceof Blob)) 309 | throw new TypeError("Failed to execute '" + kind + "' on 'FileReader': parameter 1 is not of type 'Blob'.") 310 | 311 | fr.result = '' 312 | 313 | setTimeout(function(){ 314 | this.readyState = FileReader.LOADING 315 | fr.dispatchEvent(new Event('load')) 316 | fr.dispatchEvent(new Event('loadend')) 317 | }) 318 | } 319 | 320 | FileReader.EMPTY = 0 321 | FileReader.LOADING = 1 322 | FileReader.DONE = 2 323 | FileReader.prototype.error = null 324 | FileReader.prototype.onabort = null 325 | FileReader.prototype.onerror = null 326 | FileReader.prototype.onload = null 327 | FileReader.prototype.onloadend = null 328 | FileReader.prototype.onloadstart = null 329 | FileReader.prototype.onprogress = null 330 | 331 | FileReader.prototype.readAsDataURL = function(blob) { 332 | _read(this, blob, 'readAsDataURL') 333 | this.result = 'data:' + blob.type + ';base64,' + encodeByteArray(blob._buffer) 334 | } 335 | 336 | FileReader.prototype.readAsText = function(blob) { 337 | _read(this, blob, 'readAsText') 338 | this.result = fromUtf8Array(blob._buffer) 339 | } 340 | 341 | FileReader.prototype.readAsArrayBuffer = function(blob) { 342 | _read(this, blob, 'readAsText') 343 | this.result = blob._buffer.slice() 344 | } 345 | 346 | FileReader.prototype.abort = function() {} 347 | 348 | 349 | /********************************************************/ 350 | /* URL */ 351 | /********************************************************/ 352 | URL.createObjectURL = function(blob) { 353 | return blob instanceof Blob 354 | ? 'data:' + blob.type + ';base64,' + encodeByteArray(blob._buffer) 355 | : createObjectURL.call(URL, blob) 356 | } 357 | 358 | URL.revokeObjectURL = function(url) { 359 | revokeObjectURL && revokeObjectURL.call(URL, url) 360 | } 361 | 362 | /********************************************************/ 363 | /* XHR */ 364 | /********************************************************/ 365 | var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send 366 | if (_send) { 367 | XMLHttpRequest.prototype.send = function(data) { 368 | if (data instanceof Blob) { 369 | this.setRequestHeader('Content-Type', data.type) 370 | _send.call(this, fromUtf8Array(data._buffer)) 371 | } else { 372 | _send.call(this, data) 373 | } 374 | } 375 | } 376 | 377 | global.FileReader = FileReader 378 | global.File = File 379 | global.Blob = Blob 380 | } 381 | 382 | if (strTag) { 383 | File.prototype[strTag] = 'File' 384 | Blob.prototype[strTag] = 'Blob' 385 | FileReader.prototype[strTag] = 'FileReader' 386 | } 387 | 388 | function fixFileAndXHR() { 389 | var isIE = !!global.ActiveXObject || ( 390 | '-ms-scroll-limit' in document.documentElement.style && 391 | '-ms-ime-align' in document.documentElement.style 392 | ) 393 | 394 | // Monkey patched 395 | // IE don't set Content-Type header on XHR whose body is a typed Blob 396 | // https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/6047383 397 | var _send = global.XMLHttpRequest && global.XMLHttpRequest.prototype.send 398 | if (isIE && _send) { 399 | XMLHttpRequest.prototype.send = function(data) { 400 | if (data instanceof Blob) { 401 | this.setRequestHeader('Content-Type', data.type) 402 | _send.call(this, data) 403 | } else { 404 | _send.call(this, data) 405 | } 406 | } 407 | } 408 | 409 | try { 410 | new File([], '') 411 | } catch(e) { 412 | try { 413 | var klass = new Function('class File extends Blob {' + 414 | 'constructor(chunks, name, opts) {' + 415 | 'opts = opts || {};' + 416 | 'super(chunks, opts || {});' + 417 | 'this.name = name;' + 418 | 'this.lastModifiedDate = opts.lastModified ? new Date(opts.lastModified) : new Date;' + 419 | 'this.lastModified = +this.lastModifiedDate;' + 420 | '}};' + 421 | 'return new File([], ""), File' 422 | )() 423 | global.File = klass 424 | } catch(e) { 425 | var klass = function(b, d, c) { 426 | var blob = new Blob(b, c) 427 | var t = c && void 0 !== c.lastModified ? new Date(c.lastModified) : new Date 428 | 429 | blob.name = d 430 | blob.lastModifiedDate = t 431 | blob.lastModified = +t 432 | blob.toString = function() { 433 | return '[object File]' 434 | } 435 | 436 | if (strTag) 437 | blob[strTag] = 'File' 438 | 439 | return blob 440 | } 441 | global.File = klass 442 | } 443 | } 444 | } 445 | 446 | if (blobSupported) { 447 | fixFileAndXHR() 448 | global.Blob = blobSupportsArrayBufferView ? global.Blob : BlobConstructor 449 | } else if (blobBuilderSupported) { 450 | fixFileAndXHR() 451 | global.Blob = BlobBuilderConstructor; 452 | } else { 453 | FakeBlobBuilder() 454 | } 455 | 456 | })(); -------------------------------------------------------------------------------- /resources/close.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/main.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | body { 6 | font-size: 1.2em; 7 | overflow-y: scroll; 8 | margin: 0; 9 | } 10 | 11 | button { 12 | font-family: "Times New Roman", Times, serif; 13 | } 14 | 15 | h1, 16 | h2, 17 | h3, 18 | h4, 19 | h5, 20 | h6 { 21 | text-align: center; 22 | } 23 | 24 | a { 25 | color: #0000ee; 26 | } 27 | 28 | .p-like { 29 | display: block; 30 | margin: 1em 0; 31 | } 32 | 33 | nav { 34 | width: 13rem; 35 | height: 100%; 36 | border: 1px solid #000000; 37 | display: block; 38 | position: fixed; 39 | overflow-y: auto; 40 | background: #ffefdc; 41 | padding-bottom: 5vh; 42 | } 43 | 44 | .title-link { 45 | color: #ff8800; 46 | text-decoration: none; 47 | } 48 | 49 | .nav-link { 50 | display: block; 51 | color: #0000ee; 52 | padding: 0.25em 0.75em; 53 | text-decoration: none; 54 | } 55 | 56 | .nav-link:nth-child(2n) { 57 | background: #ffe1be; 58 | } 59 | 60 | .nav-link:hover { 61 | background: #ffc98b; 62 | } 63 | 64 | main { 65 | display: block; 66 | padding: 0.5em 1em; 67 | } 68 | 69 | nav ~ main { 70 | width: calc(100% - 13rem - 1rem); 71 | margin-left: calc(13.5rem); 72 | } 73 | 74 | section { 75 | margin: auto; 76 | margin-bottom: 1.5em; 77 | max-width: 60rem; 78 | padding: 0.5em; 79 | border: 1px solid #000000; 80 | } 81 | 82 | .section-title { 83 | margin: 0.5em 0; 84 | } 85 | 86 | .section-title > a { 87 | color: #000000; 88 | text-decoration: none; 89 | } 90 | 91 | .section-title > a::after, 92 | .section-title > a::before { 93 | content: "¶"; 94 | padding: 0.5em; 95 | vertical-align: text-top; 96 | opacity: 0; 97 | } 98 | 99 | .section-title > a:hover::after { 100 | opacity: 0.3; 101 | } 102 | 103 | code[class*="language-"] { 104 | font-size: 0.9em; 105 | } 106 | 107 | .large-button { 108 | position: relative; 109 | display: block; 110 | margin: 0.5em auto; 111 | padding: 0.5em 1em; 112 | font-size: 1.25em; 113 | background: #f0c18b; 114 | border: 0; 115 | box-shadow: 2px 2px 5px #000000; 116 | cursor: pointer; 117 | -webkit-transition: background 0.25s; 118 | transition: background 0.25s; 119 | text-decoration: none; 120 | width: 10em; 121 | text-align: center; 122 | color: #000000; 123 | } 124 | 125 | .large-button:hover { 126 | background: #ff8800; 127 | } 128 | 129 | .large-button:active { 130 | box-shadow: none; 131 | top: 2px; 132 | left: 2px; 133 | } 134 | 135 | #menu { 136 | display: none; 137 | position: fixed; 138 | height: 1.25em; 139 | width: 1.25em; 140 | margin: 0.7em; 141 | z-index: 998; 142 | cursor: pointer; 143 | } 144 | 145 | #close { 146 | display: none; 147 | position: fixed; 148 | height: 1.25em; 149 | width: 1.25em; 150 | margin: 0.7em; 151 | z-index: 1000; 152 | cursor: pointer; 153 | } 154 | 155 | table { 156 | margin: 0.4em auto; 157 | border: 1px solid #000000; 158 | } 159 | 160 | table, 161 | tbody, 162 | tr, 163 | th, 164 | td { 165 | border: 1px solid #000000; 166 | border-collapse: collapse; 167 | padding: 0.1em 0.4em; 168 | } 169 | 170 | th { 171 | border-bottom: 2px solid #000000; 172 | } 173 | 174 | .menu-viewing-highlight { 175 | -webkit-transition: background 0.5s; 176 | transition: background 0.5s; 177 | border: 1px solid #ff8800; 178 | background: rgba(255, 119, 0, 0.445) !important; 179 | } 180 | 181 | .doc-title { 182 | text-align: start; 183 | margin: 0; 184 | } 185 | 186 | .doc-box { 187 | border-radius: 1em; 188 | border: 1px dashed #000000; 189 | padding: 0.5em; 190 | margin: 0.5em; 191 | } 192 | 193 | dl { 194 | margin: 0; 195 | } 196 | 197 | dt { 198 | font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; 199 | background: #d6d6d6; 200 | display: inline-block; 201 | } 202 | 203 | main > footer { 204 | text-align: center; 205 | margin-bottom: 5em; 206 | } 207 | 208 | nav > footer { 209 | display: none; 210 | text-align: center; 211 | margin: 1em 0; 212 | } 213 | 214 | @media screen and (max-width: 600px) { 215 | nav ~ main { 216 | width: 100%; 217 | margin-left: 0; 218 | } 219 | 220 | .large-button { 221 | font-size: 1em; 222 | } 223 | 224 | nav { 225 | display: none; 226 | width: 100%; 227 | z-index: 999; 228 | } 229 | 230 | #menu { 231 | display: block; 232 | } 233 | 234 | #close { 235 | display: block; 236 | } 237 | 238 | .nav-link { 239 | text-align: center; 240 | } 241 | 242 | .nav-title::before { 243 | content: "LDOM "; 244 | } 245 | 246 | .section-title > a::before { 247 | content: ""; 248 | } 249 | 250 | .section-title > a::after { 251 | opacity: 0.3; 252 | } 253 | 254 | nav > footer { 255 | display: block; 256 | } 257 | 258 | section { 259 | padding: 0.15em; 260 | } 261 | 262 | .doc-box { 263 | padding: 0.5em; 264 | margin: 0.15em; 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /resources/menu.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /resources/prism/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.15.0 2 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 3 | /** 4 | * prism.js default theme for JavaScript, CSS and HTML 5 | * Based on dabblet (http://dabblet.com) 6 | * @author Lea Verou 7 | */ 8 | 9 | code[class*="language-"], 10 | pre[class*="language-"] { 11 | color: black; 12 | background: none; 13 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 14 | text-align: left; 15 | white-space: pre; 16 | word-spacing: normal; 17 | word-break: normal; 18 | word-wrap: normal; 19 | line-height: 1.5; 20 | 21 | -moz-tab-size: 4; 22 | -o-tab-size: 4; 23 | tab-size: 4; 24 | 25 | -webkit-hyphens: none; 26 | -moz-hyphens: none; 27 | -ms-hyphens: none; 28 | hyphens: none; 29 | } 30 | 31 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 32 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 33 | text-shadow: none; 34 | background: #b3d4fc; 35 | } 36 | 37 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 38 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 39 | text-shadow: none; 40 | background: #b3d4fc; 41 | } 42 | 43 | @media print { 44 | code[class*="language-"], 45 | pre[class*="language-"] { 46 | text-shadow: none; 47 | } 48 | } 49 | 50 | /* Code blocks */ 51 | pre[class*="language-"] { 52 | padding: 1em; 53 | margin: .5em 0; 54 | overflow: auto; 55 | } 56 | 57 | :not(pre) > code[class*="language-"], 58 | pre[class*="language-"] { 59 | background: #d6d6d6; 60 | border-radius: .3em; 61 | } 62 | 63 | /* Inline code */ 64 | :not(pre) > code[class*="language-"] { 65 | padding: .1em; 66 | white-space: normal; 67 | } 68 | 69 | .token.comment, 70 | .token.prolog, 71 | .token.doctype, 72 | .token.cdata { 73 | color: slategray; 74 | } 75 | 76 | .token.punctuation { 77 | color: #000000; 78 | } 79 | 80 | .namespace { 81 | opacity: .7; 82 | } 83 | 84 | .token.property, 85 | .token.tag, 86 | .token.boolean, 87 | .token.number, 88 | .token.constant, 89 | .token.symbol, 90 | .token.deleted { 91 | color: #905; 92 | } 93 | 94 | .token.selector, 95 | .token.attr-name, 96 | .token.string, 97 | .token.char, 98 | .token.builtin, 99 | .token.inserted { 100 | color: #170099; 101 | } 102 | 103 | .token.operator, 104 | .token.entity, 105 | .token.url, 106 | .language-css .token.string, 107 | .style .token.string { 108 | color: #9a6e3a; 109 | } 110 | 111 | .token.atrule, 112 | .token.attr-value, 113 | .token.keyword { 114 | color: #07a; 115 | } 116 | 117 | .token.function, 118 | .token.class-name { 119 | color: #ff3b0a; 120 | } 121 | 122 | .token.regex, 123 | .token.important, 124 | .token.variable { 125 | color: #e90; 126 | } 127 | 128 | .token.important, 129 | .token.bold { 130 | font-weight: bold; 131 | } 132 | .token.italic { 133 | font-style: italic; 134 | } 135 | 136 | .token.entity { 137 | cursor: help; 138 | } 139 | 140 | -------------------------------------------------------------------------------- /resources/prism/prism.js: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.15.0 2 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */ 3 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-([\w-]+)\b/i,t=0,n=_self.Prism={manual:_self.Prism&&_self.Prism.manual,disableWorkerMessageHandler:_self.Prism&&_self.Prism.disableWorkerMessageHandler,util:{encode:function(e){return e instanceof r?new r(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)return;if(!(w instanceof s)){if(m&&b!=t.length-1){h.lastIndex=k;var _=h.exec(e);if(!_)break;for(var j=_.index+(d?_[1].length:0),P=_.index+_[0].length,A=b,x=k,O=t.length;O>A&&(P>x||!t[A].type&&!t[A-1].greedy);++A)x+=t[A].length,j>=x&&(++b,k=x);if(t[b]instanceof s)continue;I=A-b,w=e.slice(k,x),_.index-=k}else{h.lastIndex=0;var _=h.exec(w),I=1}if(_){d&&(p=_[1]?_[1].length:0);var j=_.index+p,_=_[0].slice(p),P=j+_.length,N=w.slice(0,j),S=w.slice(P),C=[b,I];N&&(++b,k+=N.length,C.push(N));var E=new s(u,f?n.tokenize(_,f):_,y,_,m);if(C.push(E),S&&C.push(S),Array.prototype.splice.apply(t,C),1!=I&&n.matchGrammar(e,t,r,b,k,!0,u),i)break}else if(i)break}}}}},tokenize:function(e,t){var r=[e],a=t.rest;if(a){for(var l in a)t[l]=a[l];delete t.rest}return n.matchGrammar(e,r,t,0,0,!1),r},hooks:{all:{},add:function(e,t){var r=n.hooks.all;r[e]=r[e]||[],r[e].push(t)},run:function(e,t){var r=n.hooks.all[e];if(r&&r.length)for(var a,l=0;a=r[l++];)a(t)}}},r=n.Token=function(e,t,n,r,a){this.type=e,this.content=t,this.alias=n,this.length=0|(r||"").length,this.greedy=!!a};if(r.stringify=function(e,t,a){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return r.stringify(n,t,e)}).join("");var l={type:e.type,content:r.stringify(e.content,t,a),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:a};if(e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o=Object.keys(l.attributes).map(function(e){return e+'="'+(l.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+l.tag+' class="'+l.classes.join(" ")+'"'+(o?" "+o:"")+">"+l.content+""},!_self.document)return _self.addEventListener?(n.disableWorkerMessageHandler||_self.addEventListener("message",function(e){var t=JSON.parse(e.data),r=t.language,a=t.code,l=t.immediateClose;_self.postMessage(n.highlight(a,n.languages[r],r)),l&&_self.close()},!1),_self.Prism):_self.Prism;var a=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return a&&(n.filename=a.src,n.manual||a.hasAttribute("data-manual")||("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism); 4 | Prism.languages.markup={comment://,prolog:/<\?[\s\S]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,greedy:!0,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,inside:{punctuation:[/^=/,{pattern:/(^|[^\\])["']/,lookbehind:!0}]}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/&#?[\da-z]{1,8};/i},Prism.languages.markup.tag.inside["attr-value"].inside.entity=Prism.languages.markup.entity,Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup; 5 | Prism.languages.css={comment:/\/\*[\s\S]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(?:;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^{}\s][^{};]*?(?=\s*\{)/,string:{pattern:/("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.languages.css,Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/()[\s\S]*?(?=<\/style>)/i,lookbehind:!0,inside:Prism.languages.css,alias:"language-css",greedy:!0}}),Prism.languages.insertBefore("inside","attr-value",{"style-attr":{pattern:/\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,inside:{"attr-name":{pattern:/^\s*style/i,inside:Prism.languages.markup.tag.inside},punctuation:/^\s*=\s*['"]|['"]\s*$/,"attr-value":{pattern:/.+/i,inside:Prism.languages.css}},alias:"language-css"}},Prism.languages.markup.tag)); 6 | Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*/,lookbehind:!0,greedy:!0}],string:{pattern:/(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,greedy:!0},"class-name":{pattern:/((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,lookbehind:!0,inside:{punctuation:/[.\\]/}},keyword:/\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,"boolean":/\b(?:true|false)\b/,"function":/[a-z0-9_]+(?=\()/i,number:/\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,operator:/--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,punctuation:/[{}[\];(),.:]/}; 7 | Prism.languages.javascript=Prism.languages.extend("clike",{"class-name":[Prism.languages.clike["class-name"],/[_$A-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\.(?:prototype|constructor))/],keyword:[{pattern:/((?:^|})\s*)(?:catch|finally)\b/,lookbehind:!0},/\b(?:as|async|await|break|case|class|const|continue|debugger|default|delete|do|else|enum|export|extends|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/],number:/\b(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/,"function":/[_$a-zA-Z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\(|\.(?:apply|bind|call)\()/,operator:/-[-=]?|\+[+=]?|!=?=?|<>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/}),Prism.languages.javascript["class-name"][0].pattern=/(\b(?:class|interface|extends|implements|instanceof|new)\s+)[\w.\\]+/,Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^\/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,lookbehind:!0,greedy:!0},"function-variable":{pattern:/[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*[=:]\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i,alias:"function"},constant:/\b[A-Z][A-Z\d_]*\b/}),Prism.languages.insertBefore("javascript","string",{"template-string":{pattern:/`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,greedy:!0,inside:{interpolation:{pattern:/\${[^}]+}/,inside:{"interpolation-punctuation":{pattern:/^\${|}$/,alias:"punctuation"},rest:Prism.languages.javascript}},string:/[\s\S]+/}}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/()[\s\S]*?(?=<\/script>)/i,lookbehind:!0,inside:Prism.languages.javascript,alias:"language-javascript",greedy:!0}}),Prism.languages.js=Prism.languages.javascript; 8 | -------------------------------------------------------------------------------- /resources/uglify/ast.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | function DEFNODE(type, props, methods, base) { 47 | if (typeof base === "undefined") base = AST_Node; 48 | props = props ? props.split(/\s+/) : []; 49 | var self_props = props; 50 | if (base && base.PROPS) props = props.concat(base.PROPS); 51 | var code = [ 52 | "return function AST_", type, "(props){", 53 | "if(props){", 54 | ]; 55 | props.forEach(function(prop) { 56 | code.push("this.", prop, "=props.", prop, ";"); 57 | }); 58 | var proto = base && new base; 59 | if (proto && proto.initialize || methods && methods.initialize) code.push("this.initialize();"); 60 | code.push("}}"); 61 | var ctor = new Function(code.join(""))(); 62 | if (proto) { 63 | ctor.prototype = proto; 64 | ctor.BASE = base; 65 | } 66 | if (base) base.SUBCLASSES.push(ctor); 67 | ctor.prototype.CTOR = ctor; 68 | ctor.PROPS = props || null; 69 | ctor.SELF_PROPS = self_props; 70 | ctor.SUBCLASSES = []; 71 | if (type) { 72 | ctor.prototype.TYPE = ctor.TYPE = type; 73 | } 74 | if (methods) for (var name in methods) if (HOP(methods, name)) { 75 | if (/^\$/.test(name)) { 76 | ctor[name.substr(1)] = methods[name]; 77 | } else { 78 | ctor.prototype[name] = methods[name]; 79 | } 80 | } 81 | ctor.DEFMETHOD = function(name, method) { 82 | this.prototype[name] = method; 83 | }; 84 | if (typeof exports !== "undefined") { 85 | exports["AST_" + type] = ctor; 86 | } 87 | return ctor; 88 | } 89 | 90 | var AST_Token = DEFNODE("Token", "type value line col pos endline endcol endpos nlb comments_before comments_after file raw", { 91 | }, null); 92 | 93 | var AST_Node = DEFNODE("Node", "start end", { 94 | _clone: function(deep) { 95 | if (deep) { 96 | var self = this.clone(); 97 | return self.transform(new TreeTransformer(function(node) { 98 | if (node !== self) { 99 | return node.clone(true); 100 | } 101 | })); 102 | } 103 | return new this.CTOR(this); 104 | }, 105 | clone: function(deep) { 106 | return this._clone(deep); 107 | }, 108 | $documentation: "Base class of all AST nodes", 109 | $propdoc: { 110 | start: "[AST_Token] The first token of this node", 111 | end: "[AST_Token] The last token of this node" 112 | }, 113 | _walk: function(visitor) { 114 | return visitor._visit(this); 115 | }, 116 | walk: function(visitor) { 117 | return this._walk(visitor); // not sure the indirection will be any help 118 | } 119 | }, null); 120 | 121 | AST_Node.warn = function(txt, props) { 122 | if (AST_Node.warn_function) AST_Node.warn_function(string_template(txt, props)); 123 | }; 124 | 125 | /* -----[ statements ]----- */ 126 | 127 | var AST_Statement = DEFNODE("Statement", null, { 128 | $documentation: "Base class of all statements", 129 | }); 130 | 131 | var AST_Debugger = DEFNODE("Debugger", null, { 132 | $documentation: "Represents a debugger statement", 133 | }, AST_Statement); 134 | 135 | var AST_Directive = DEFNODE("Directive", "value quote", { 136 | $documentation: "Represents a directive, like \"use strict\";", 137 | $propdoc: { 138 | value: "[string] The value of this directive as a plain string (it's not an AST_String!)", 139 | quote: "[string] the original quote character" 140 | }, 141 | }, AST_Statement); 142 | 143 | var AST_SimpleStatement = DEFNODE("SimpleStatement", "body", { 144 | $documentation: "A statement consisting of an expression, i.e. a = 1 + 2", 145 | $propdoc: { 146 | body: "[AST_Node] an expression node (should not be instanceof AST_Statement)" 147 | }, 148 | _walk: function(visitor) { 149 | return visitor._visit(this, function() { 150 | this.body._walk(visitor); 151 | }); 152 | } 153 | }, AST_Statement); 154 | 155 | function walk_body(node, visitor) { 156 | var body = node.body; 157 | if (body instanceof AST_Statement) { 158 | body._walk(visitor); 159 | } else body.forEach(function(node) { 160 | node._walk(visitor); 161 | }); 162 | } 163 | 164 | var AST_Block = DEFNODE("Block", "body", { 165 | $documentation: "A body of statements (usually braced)", 166 | $propdoc: { 167 | body: "[AST_Statement*] an array of statements" 168 | }, 169 | _walk: function(visitor) { 170 | return visitor._visit(this, function() { 171 | walk_body(this, visitor); 172 | }); 173 | } 174 | }, AST_Statement); 175 | 176 | var AST_BlockStatement = DEFNODE("BlockStatement", null, { 177 | $documentation: "A block statement", 178 | }, AST_Block); 179 | 180 | var AST_EmptyStatement = DEFNODE("EmptyStatement", null, { 181 | $documentation: "The empty statement (empty block or simply a semicolon)" 182 | }, AST_Statement); 183 | 184 | var AST_StatementWithBody = DEFNODE("StatementWithBody", "body", { 185 | $documentation: "Base class for all statements that contain one nested body: `For`, `ForIn`, `Do`, `While`, `With`", 186 | $propdoc: { 187 | body: "[AST_Statement] the body; this should always be present, even if it's an AST_EmptyStatement" 188 | } 189 | }, AST_Statement); 190 | 191 | var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", { 192 | $documentation: "Statement with a label", 193 | $propdoc: { 194 | label: "[AST_Label] a label definition" 195 | }, 196 | _walk: function(visitor) { 197 | return visitor._visit(this, function() { 198 | this.label._walk(visitor); 199 | this.body._walk(visitor); 200 | }); 201 | }, 202 | clone: function(deep) { 203 | var node = this._clone(deep); 204 | if (deep) { 205 | var label = node.label; 206 | var def = this.label; 207 | node.walk(new TreeWalker(function(node) { 208 | if (node instanceof AST_LoopControl && node.label && node.label.thedef === def) { 209 | node.label.thedef = label; 210 | label.references.push(node); 211 | } 212 | })); 213 | } 214 | return node; 215 | } 216 | }, AST_StatementWithBody); 217 | 218 | var AST_IterationStatement = DEFNODE("IterationStatement", null, { 219 | $documentation: "Internal class. All loops inherit from it." 220 | }, AST_StatementWithBody); 221 | 222 | var AST_DWLoop = DEFNODE("DWLoop", "condition", { 223 | $documentation: "Base class for do/while statements", 224 | $propdoc: { 225 | condition: "[AST_Node] the loop condition. Should not be instanceof AST_Statement" 226 | } 227 | }, AST_IterationStatement); 228 | 229 | var AST_Do = DEFNODE("Do", null, { 230 | $documentation: "A `do` statement", 231 | _walk: function(visitor) { 232 | return visitor._visit(this, function() { 233 | this.body._walk(visitor); 234 | this.condition._walk(visitor); 235 | }); 236 | } 237 | }, AST_DWLoop); 238 | 239 | var AST_While = DEFNODE("While", null, { 240 | $documentation: "A `while` statement", 241 | _walk: function(visitor) { 242 | return visitor._visit(this, function() { 243 | this.condition._walk(visitor); 244 | this.body._walk(visitor); 245 | }); 246 | } 247 | }, AST_DWLoop); 248 | 249 | var AST_For = DEFNODE("For", "init condition step", { 250 | $documentation: "A `for` statement", 251 | $propdoc: { 252 | init: "[AST_Node?] the `for` initialization code, or null if empty", 253 | condition: "[AST_Node?] the `for` termination clause, or null if empty", 254 | step: "[AST_Node?] the `for` update clause, or null if empty" 255 | }, 256 | _walk: function(visitor) { 257 | return visitor._visit(this, function() { 258 | if (this.init) this.init._walk(visitor); 259 | if (this.condition) this.condition._walk(visitor); 260 | if (this.step) this.step._walk(visitor); 261 | this.body._walk(visitor); 262 | }); 263 | } 264 | }, AST_IterationStatement); 265 | 266 | var AST_ForIn = DEFNODE("ForIn", "init object", { 267 | $documentation: "A `for ... in` statement", 268 | $propdoc: { 269 | init: "[AST_Node] the `for/in` initialization code", 270 | object: "[AST_Node] the object that we're looping through" 271 | }, 272 | _walk: function(visitor) { 273 | return visitor._visit(this, function() { 274 | this.init._walk(visitor); 275 | this.object._walk(visitor); 276 | this.body._walk(visitor); 277 | }); 278 | } 279 | }, AST_IterationStatement); 280 | 281 | var AST_With = DEFNODE("With", "expression", { 282 | $documentation: "A `with` statement", 283 | $propdoc: { 284 | expression: "[AST_Node] the `with` expression" 285 | }, 286 | _walk: function(visitor) { 287 | return visitor._visit(this, function() { 288 | this.expression._walk(visitor); 289 | this.body._walk(visitor); 290 | }); 291 | } 292 | }, AST_StatementWithBody); 293 | 294 | /* -----[ scope and functions ]----- */ 295 | 296 | var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", { 297 | $documentation: "Base class for all statements introducing a lexical scope", 298 | $propdoc: { 299 | variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope", 300 | functions: "[Object/S] like `variables`, but only lists function declarations", 301 | uses_with: "[boolean/S] tells whether this scope uses the `with` statement", 302 | uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`", 303 | parent_scope: "[AST_Scope?/S] link to the parent scope", 304 | enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes", 305 | cname: "[integer/S] current index for mangling variables (used internally by the mangler)", 306 | }, 307 | clone: function(deep) { 308 | var node = this._clone(deep); 309 | if (this.variables) node.variables = this.variables.clone(); 310 | if (this.functions) node.functions = this.functions.clone(); 311 | if (this.enclosed) node.enclosed = this.enclosed.slice(); 312 | return node; 313 | }, 314 | pinned: function() { 315 | return this.uses_eval || this.uses_with; 316 | } 317 | }, AST_Block); 318 | 319 | var AST_Toplevel = DEFNODE("Toplevel", "globals", { 320 | $documentation: "The toplevel scope", 321 | $propdoc: { 322 | globals: "[Object/S] a map of name -> SymbolDef for all undeclared names", 323 | }, 324 | wrap_commonjs: function(name) { 325 | var body = this.body; 326 | var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");"; 327 | wrapped_tl = parse(wrapped_tl); 328 | wrapped_tl = wrapped_tl.transform(new TreeTransformer(function(node) { 329 | if (node instanceof AST_Directive && node.value == "$ORIG") { 330 | return MAP.splice(body); 331 | } 332 | })); 333 | return wrapped_tl; 334 | }, 335 | wrap_enclose: function(args_values) { 336 | if (typeof args_values != "string") args_values = ""; 337 | var index = args_values.indexOf(":"); 338 | if (index < 0) index = args_values.length; 339 | var body = this.body; 340 | return parse([ 341 | "(function(", 342 | args_values.slice(0, index), 343 | '){"$ORIG"})(', 344 | args_values.slice(index + 1), 345 | ")" 346 | ].join("")).transform(new TreeTransformer(function(node) { 347 | if (node instanceof AST_Directive && node.value == "$ORIG") { 348 | return MAP.splice(body); 349 | } 350 | })); 351 | } 352 | }, AST_Scope); 353 | 354 | var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", { 355 | $documentation: "Base class for functions", 356 | $propdoc: { 357 | name: "[AST_SymbolDeclaration?] the name of this function", 358 | argnames: "[AST_SymbolFunarg*] array of function arguments", 359 | uses_arguments: "[boolean/S] tells whether this function accesses the arguments array" 360 | }, 361 | _walk: function(visitor) { 362 | return visitor._visit(this, function() { 363 | if (this.name) this.name._walk(visitor); 364 | this.argnames.forEach(function(argname) { 365 | argname._walk(visitor); 366 | }); 367 | walk_body(this, visitor); 368 | }); 369 | } 370 | }, AST_Scope); 371 | 372 | var AST_Accessor = DEFNODE("Accessor", null, { 373 | $documentation: "A setter/getter function. The `name` property is always null." 374 | }, AST_Lambda); 375 | 376 | var AST_Function = DEFNODE("Function", "inlined", { 377 | $documentation: "A function expression" 378 | }, AST_Lambda); 379 | 380 | var AST_Defun = DEFNODE("Defun", "inlined", { 381 | $documentation: "A function definition" 382 | }, AST_Lambda); 383 | 384 | /* -----[ JUMPS ]----- */ 385 | 386 | var AST_Jump = DEFNODE("Jump", null, { 387 | $documentation: "Base class for “jumps” (for now that's `return`, `throw`, `break` and `continue`)" 388 | }, AST_Statement); 389 | 390 | var AST_Exit = DEFNODE("Exit", "value", { 391 | $documentation: "Base class for “exits” (`return` and `throw`)", 392 | $propdoc: { 393 | value: "[AST_Node?] the value returned or thrown by this statement; could be null for AST_Return" 394 | }, 395 | _walk: function(visitor) { 396 | return visitor._visit(this, this.value && function() { 397 | this.value._walk(visitor); 398 | }); 399 | } 400 | }, AST_Jump); 401 | 402 | var AST_Return = DEFNODE("Return", null, { 403 | $documentation: "A `return` statement" 404 | }, AST_Exit); 405 | 406 | var AST_Throw = DEFNODE("Throw", null, { 407 | $documentation: "A `throw` statement" 408 | }, AST_Exit); 409 | 410 | var AST_LoopControl = DEFNODE("LoopControl", "label", { 411 | $documentation: "Base class for loop control statements (`break` and `continue`)", 412 | $propdoc: { 413 | label: "[AST_LabelRef?] the label, or null if none", 414 | }, 415 | _walk: function(visitor) { 416 | return visitor._visit(this, this.label && function() { 417 | this.label._walk(visitor); 418 | }); 419 | } 420 | }, AST_Jump); 421 | 422 | var AST_Break = DEFNODE("Break", null, { 423 | $documentation: "A `break` statement" 424 | }, AST_LoopControl); 425 | 426 | var AST_Continue = DEFNODE("Continue", null, { 427 | $documentation: "A `continue` statement" 428 | }, AST_LoopControl); 429 | 430 | /* -----[ IF ]----- */ 431 | 432 | var AST_If = DEFNODE("If", "condition alternative", { 433 | $documentation: "A `if` statement", 434 | $propdoc: { 435 | condition: "[AST_Node] the `if` condition", 436 | alternative: "[AST_Statement?] the `else` part, or null if not present" 437 | }, 438 | _walk: function(visitor) { 439 | return visitor._visit(this, function() { 440 | this.condition._walk(visitor); 441 | this.body._walk(visitor); 442 | if (this.alternative) this.alternative._walk(visitor); 443 | }); 444 | } 445 | }, AST_StatementWithBody); 446 | 447 | /* -----[ SWITCH ]----- */ 448 | 449 | var AST_Switch = DEFNODE("Switch", "expression", { 450 | $documentation: "A `switch` statement", 451 | $propdoc: { 452 | expression: "[AST_Node] the `switch` “discriminant”" 453 | }, 454 | _walk: function(visitor) { 455 | return visitor._visit(this, function() { 456 | this.expression._walk(visitor); 457 | walk_body(this, visitor); 458 | }); 459 | } 460 | }, AST_Block); 461 | 462 | var AST_SwitchBranch = DEFNODE("SwitchBranch", null, { 463 | $documentation: "Base class for `switch` branches", 464 | }, AST_Block); 465 | 466 | var AST_Default = DEFNODE("Default", null, { 467 | $documentation: "A `default` switch branch", 468 | }, AST_SwitchBranch); 469 | 470 | var AST_Case = DEFNODE("Case", "expression", { 471 | $documentation: "A `case` switch branch", 472 | $propdoc: { 473 | expression: "[AST_Node] the `case` expression" 474 | }, 475 | _walk: function(visitor) { 476 | return visitor._visit(this, function() { 477 | this.expression._walk(visitor); 478 | walk_body(this, visitor); 479 | }); 480 | } 481 | }, AST_SwitchBranch); 482 | 483 | /* -----[ EXCEPTIONS ]----- */ 484 | 485 | var AST_Try = DEFNODE("Try", "bcatch bfinally", { 486 | $documentation: "A `try` statement", 487 | $propdoc: { 488 | bcatch: "[AST_Catch?] the catch block, or null if not present", 489 | bfinally: "[AST_Finally?] the finally block, or null if not present" 490 | }, 491 | _walk: function(visitor) { 492 | return visitor._visit(this, function() { 493 | walk_body(this, visitor); 494 | if (this.bcatch) this.bcatch._walk(visitor); 495 | if (this.bfinally) this.bfinally._walk(visitor); 496 | }); 497 | } 498 | }, AST_Block); 499 | 500 | var AST_Catch = DEFNODE("Catch", "argname", { 501 | $documentation: "A `catch` node; only makes sense as part of a `try` statement", 502 | $propdoc: { 503 | argname: "[AST_SymbolCatch] symbol for the exception" 504 | }, 505 | _walk: function(visitor) { 506 | return visitor._visit(this, function() { 507 | this.argname._walk(visitor); 508 | walk_body(this, visitor); 509 | }); 510 | } 511 | }, AST_Block); 512 | 513 | var AST_Finally = DEFNODE("Finally", null, { 514 | $documentation: "A `finally` node; only makes sense as part of a `try` statement" 515 | }, AST_Block); 516 | 517 | /* -----[ VAR ]----- */ 518 | 519 | var AST_Definitions = DEFNODE("Definitions", "definitions", { 520 | $documentation: "Base class for `var` nodes (variable declarations/initializations)", 521 | $propdoc: { 522 | definitions: "[AST_VarDef*] array of variable definitions" 523 | }, 524 | _walk: function(visitor) { 525 | return visitor._visit(this, function() { 526 | this.definitions.forEach(function(defn) { 527 | defn._walk(visitor); 528 | }); 529 | }); 530 | } 531 | }, AST_Statement); 532 | 533 | var AST_Var = DEFNODE("Var", null, { 534 | $documentation: "A `var` statement" 535 | }, AST_Definitions); 536 | 537 | var AST_VarDef = DEFNODE("VarDef", "name value", { 538 | $documentation: "A variable declaration; only appears in a AST_Definitions node", 539 | $propdoc: { 540 | name: "[AST_SymbolVar] name of the variable", 541 | value: "[AST_Node?] initializer, or null of there's no initializer" 542 | }, 543 | _walk: function(visitor) { 544 | return visitor._visit(this, function() { 545 | this.name._walk(visitor); 546 | if (this.value) this.value._walk(visitor); 547 | }); 548 | } 549 | }); 550 | 551 | /* -----[ OTHER ]----- */ 552 | 553 | var AST_Call = DEFNODE("Call", "expression args", { 554 | $documentation: "A function call expression", 555 | $propdoc: { 556 | expression: "[AST_Node] expression to invoke as function", 557 | args: "[AST_Node*] array of arguments" 558 | }, 559 | _walk: function(visitor) { 560 | return visitor._visit(this, function() { 561 | this.expression._walk(visitor); 562 | this.args.forEach(function(node) { 563 | node._walk(visitor); 564 | }); 565 | }); 566 | } 567 | }); 568 | 569 | var AST_New = DEFNODE("New", null, { 570 | $documentation: "An object instantiation. Derives from a function call since it has exactly the same properties" 571 | }, AST_Call); 572 | 573 | var AST_Sequence = DEFNODE("Sequence", "expressions", { 574 | $documentation: "A sequence expression (comma-separated expressions)", 575 | $propdoc: { 576 | expressions: "[AST_Node*] array of expressions (at least two)" 577 | }, 578 | _walk: function(visitor) { 579 | return visitor._visit(this, function() { 580 | this.expressions.forEach(function(node) { 581 | node._walk(visitor); 582 | }); 583 | }); 584 | } 585 | }); 586 | 587 | var AST_PropAccess = DEFNODE("PropAccess", "expression property", { 588 | $documentation: "Base class for property access expressions, i.e. `a.foo` or `a[\"foo\"]`", 589 | $propdoc: { 590 | expression: "[AST_Node] the “container” expression", 591 | property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node" 592 | } 593 | }); 594 | 595 | var AST_Dot = DEFNODE("Dot", null, { 596 | $documentation: "A dotted property access expression", 597 | _walk: function(visitor) { 598 | return visitor._visit(this, function() { 599 | this.expression._walk(visitor); 600 | }); 601 | } 602 | }, AST_PropAccess); 603 | 604 | var AST_Sub = DEFNODE("Sub", null, { 605 | $documentation: "Index-style property access, i.e. `a[\"foo\"]`", 606 | _walk: function(visitor) { 607 | return visitor._visit(this, function() { 608 | this.expression._walk(visitor); 609 | this.property._walk(visitor); 610 | }); 611 | } 612 | }, AST_PropAccess); 613 | 614 | var AST_Unary = DEFNODE("Unary", "operator expression", { 615 | $documentation: "Base class for unary expressions", 616 | $propdoc: { 617 | operator: "[string] the operator", 618 | expression: "[AST_Node] expression that this unary operator applies to" 619 | }, 620 | _walk: function(visitor) { 621 | return visitor._visit(this, function() { 622 | this.expression._walk(visitor); 623 | }); 624 | } 625 | }); 626 | 627 | var AST_UnaryPrefix = DEFNODE("UnaryPrefix", null, { 628 | $documentation: "Unary prefix expression, i.e. `typeof i` or `++i`" 629 | }, AST_Unary); 630 | 631 | var AST_UnaryPostfix = DEFNODE("UnaryPostfix", null, { 632 | $documentation: "Unary postfix expression, i.e. `i++`" 633 | }, AST_Unary); 634 | 635 | var AST_Binary = DEFNODE("Binary", "operator left right", { 636 | $documentation: "Binary expression, i.e. `a + b`", 637 | $propdoc: { 638 | left: "[AST_Node] left-hand side expression", 639 | operator: "[string] the operator", 640 | right: "[AST_Node] right-hand side expression" 641 | }, 642 | _walk: function(visitor) { 643 | return visitor._visit(this, function() { 644 | this.left._walk(visitor); 645 | this.right._walk(visitor); 646 | }); 647 | } 648 | }); 649 | 650 | var AST_Conditional = DEFNODE("Conditional", "condition consequent alternative", { 651 | $documentation: "Conditional expression using the ternary operator, i.e. `a ? b : c`", 652 | $propdoc: { 653 | condition: "[AST_Node]", 654 | consequent: "[AST_Node]", 655 | alternative: "[AST_Node]" 656 | }, 657 | _walk: function(visitor) { 658 | return visitor._visit(this, function() { 659 | this.condition._walk(visitor); 660 | this.consequent._walk(visitor); 661 | this.alternative._walk(visitor); 662 | }); 663 | } 664 | }); 665 | 666 | var AST_Assign = DEFNODE("Assign", null, { 667 | $documentation: "An assignment expression — `a = b + 5`", 668 | }, AST_Binary); 669 | 670 | /* -----[ LITERALS ]----- */ 671 | 672 | var AST_Array = DEFNODE("Array", "elements", { 673 | $documentation: "An array literal", 674 | $propdoc: { 675 | elements: "[AST_Node*] array of elements" 676 | }, 677 | _walk: function(visitor) { 678 | return visitor._visit(this, function() { 679 | this.elements.forEach(function(element) { 680 | element._walk(visitor); 681 | }); 682 | }); 683 | } 684 | }); 685 | 686 | var AST_Object = DEFNODE("Object", "properties", { 687 | $documentation: "An object literal", 688 | $propdoc: { 689 | properties: "[AST_ObjectProperty*] array of properties" 690 | }, 691 | _walk: function(visitor) { 692 | return visitor._visit(this, function() { 693 | this.properties.forEach(function(prop) { 694 | prop._walk(visitor); 695 | }); 696 | }); 697 | } 698 | }); 699 | 700 | var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", { 701 | $documentation: "Base class for literal object properties", 702 | $propdoc: { 703 | key: "[string|AST_SymbolAccessor] property name. For ObjectKeyVal this is a string. For getters and setters this is an AST_SymbolAccessor.", 704 | value: "[AST_Node] property value. For getters and setters this is an AST_Accessor." 705 | }, 706 | _walk: function(visitor) { 707 | return visitor._visit(this, function() { 708 | this.value._walk(visitor); 709 | }); 710 | } 711 | }); 712 | 713 | var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", { 714 | $documentation: "A key: value object property", 715 | $propdoc: { 716 | quote: "[string] the original quote character" 717 | } 718 | }, AST_ObjectProperty); 719 | 720 | var AST_ObjectSetter = DEFNODE("ObjectSetter", null, { 721 | $documentation: "An object setter property", 722 | }, AST_ObjectProperty); 723 | 724 | var AST_ObjectGetter = DEFNODE("ObjectGetter", null, { 725 | $documentation: "An object getter property", 726 | }, AST_ObjectProperty); 727 | 728 | var AST_Symbol = DEFNODE("Symbol", "scope name thedef", { 729 | $propdoc: { 730 | name: "[string] name of this symbol", 731 | scope: "[AST_Scope/S] the current scope (not necessarily the definition scope)", 732 | thedef: "[SymbolDef/S] the definition of this symbol" 733 | }, 734 | $documentation: "Base class for all symbols", 735 | }); 736 | 737 | var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, { 738 | $documentation: "The name of a property accessor (setter/getter function)" 739 | }, AST_Symbol); 740 | 741 | var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", { 742 | $documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)", 743 | }, AST_Symbol); 744 | 745 | var AST_SymbolVar = DEFNODE("SymbolVar", null, { 746 | $documentation: "Symbol defining a variable", 747 | }, AST_SymbolDeclaration); 748 | 749 | var AST_SymbolFunarg = DEFNODE("SymbolFunarg", null, { 750 | $documentation: "Symbol naming a function argument", 751 | }, AST_SymbolVar); 752 | 753 | var AST_SymbolDefun = DEFNODE("SymbolDefun", null, { 754 | $documentation: "Symbol defining a function", 755 | }, AST_SymbolDeclaration); 756 | 757 | var AST_SymbolLambda = DEFNODE("SymbolLambda", null, { 758 | $documentation: "Symbol naming a function expression", 759 | }, AST_SymbolDeclaration); 760 | 761 | var AST_SymbolCatch = DEFNODE("SymbolCatch", null, { 762 | $documentation: "Symbol naming the exception in catch", 763 | }, AST_SymbolDeclaration); 764 | 765 | var AST_Label = DEFNODE("Label", "references", { 766 | $documentation: "Symbol naming a label (declaration)", 767 | $propdoc: { 768 | references: "[AST_LoopControl*] a list of nodes referring to this label" 769 | }, 770 | initialize: function() { 771 | this.references = []; 772 | this.thedef = this; 773 | } 774 | }, AST_Symbol); 775 | 776 | var AST_SymbolRef = DEFNODE("SymbolRef", null, { 777 | $documentation: "Reference to some symbol (not definition/declaration)", 778 | }, AST_Symbol); 779 | 780 | var AST_LabelRef = DEFNODE("LabelRef", null, { 781 | $documentation: "Reference to a label symbol", 782 | }, AST_Symbol); 783 | 784 | var AST_This = DEFNODE("This", null, { 785 | $documentation: "The `this` symbol", 786 | }, AST_Symbol); 787 | 788 | var AST_Constant = DEFNODE("Constant", null, { 789 | $documentation: "Base class for all constants", 790 | getValue: function() { 791 | return this.value; 792 | } 793 | }); 794 | 795 | var AST_String = DEFNODE("String", "value quote", { 796 | $documentation: "A string literal", 797 | $propdoc: { 798 | value: "[string] the contents of this string", 799 | quote: "[string] the original quote character" 800 | } 801 | }, AST_Constant); 802 | 803 | var AST_Number = DEFNODE("Number", "value literal", { 804 | $documentation: "A number literal", 805 | $propdoc: { 806 | value: "[number] the numeric value", 807 | literal: "[string] numeric value as string (optional)" 808 | } 809 | }, AST_Constant); 810 | 811 | var AST_RegExp = DEFNODE("RegExp", "value", { 812 | $documentation: "A regexp literal", 813 | $propdoc: { 814 | value: "[RegExp] the actual regexp" 815 | } 816 | }, AST_Constant); 817 | 818 | var AST_Atom = DEFNODE("Atom", null, { 819 | $documentation: "Base class for atoms", 820 | }, AST_Constant); 821 | 822 | var AST_Null = DEFNODE("Null", null, { 823 | $documentation: "The `null` atom", 824 | value: null 825 | }, AST_Atom); 826 | 827 | var AST_NaN = DEFNODE("NaN", null, { 828 | $documentation: "The impossible value", 829 | value: 0/0 830 | }, AST_Atom); 831 | 832 | var AST_Undefined = DEFNODE("Undefined", null, { 833 | $documentation: "The `undefined` value", 834 | value: function(){}() 835 | }, AST_Atom); 836 | 837 | var AST_Hole = DEFNODE("Hole", null, { 838 | $documentation: "A hole in an array", 839 | value: function(){}() 840 | }, AST_Atom); 841 | 842 | var AST_Infinity = DEFNODE("Infinity", null, { 843 | $documentation: "The `Infinity` value", 844 | value: 1/0 845 | }, AST_Atom); 846 | 847 | var AST_Boolean = DEFNODE("Boolean", null, { 848 | $documentation: "Base class for booleans", 849 | }, AST_Atom); 850 | 851 | var AST_False = DEFNODE("False", null, { 852 | $documentation: "The `false` atom", 853 | value: false 854 | }, AST_Boolean); 855 | 856 | var AST_True = DEFNODE("True", null, { 857 | $documentation: "The `true` atom", 858 | value: true 859 | }, AST_Boolean); 860 | 861 | /* -----[ TreeWalker ]----- */ 862 | 863 | function TreeWalker(callback) { 864 | this.visit = callback; 865 | this.stack = []; 866 | this.directives = Object.create(null); 867 | } 868 | TreeWalker.prototype = { 869 | _visit: function(node, descend) { 870 | this.push(node); 871 | var ret = this.visit(node, descend ? function() { 872 | descend.call(node); 873 | } : noop); 874 | if (!ret && descend) { 875 | descend.call(node); 876 | } 877 | this.pop(); 878 | return ret; 879 | }, 880 | parent: function(n) { 881 | return this.stack[this.stack.length - 2 - (n || 0)]; 882 | }, 883 | push: function(node) { 884 | if (node instanceof AST_Lambda) { 885 | this.directives = Object.create(this.directives); 886 | } else if (node instanceof AST_Directive && !this.directives[node.value]) { 887 | this.directives[node.value] = node; 888 | } 889 | this.stack.push(node); 890 | }, 891 | pop: function() { 892 | if (this.stack.pop() instanceof AST_Lambda) { 893 | this.directives = Object.getPrototypeOf(this.directives); 894 | } 895 | }, 896 | self: function() { 897 | return this.stack[this.stack.length - 1]; 898 | }, 899 | find_parent: function(type) { 900 | var stack = this.stack; 901 | for (var i = stack.length; --i >= 0;) { 902 | var x = stack[i]; 903 | if (x instanceof type) return x; 904 | } 905 | }, 906 | has_directive: function(type) { 907 | var dir = this.directives[type]; 908 | if (dir) return dir; 909 | var node = this.stack[this.stack.length - 1]; 910 | if (node instanceof AST_Scope) { 911 | for (var i = 0; i < node.body.length; ++i) { 912 | var st = node.body[i]; 913 | if (!(st instanceof AST_Directive)) break; 914 | if (st.value == type) return st; 915 | } 916 | } 917 | }, 918 | loopcontrol_target: function(node) { 919 | var stack = this.stack; 920 | if (node.label) for (var i = stack.length; --i >= 0;) { 921 | var x = stack[i]; 922 | if (x instanceof AST_LabeledStatement && x.label.name == node.label.name) 923 | return x.body; 924 | } else for (var i = stack.length; --i >= 0;) { 925 | var x = stack[i]; 926 | if (x instanceof AST_IterationStatement 927 | || node instanceof AST_Break && x instanceof AST_Switch) 928 | return x; 929 | } 930 | }, 931 | in_boolean_context: function() { 932 | var self = this.self(); 933 | for (var i = 0, p; p = this.parent(i); i++) { 934 | if (p instanceof AST_SimpleStatement 935 | || p instanceof AST_Conditional && p.condition === self 936 | || p instanceof AST_DWLoop && p.condition === self 937 | || p instanceof AST_For && p.condition === self 938 | || p instanceof AST_If && p.condition === self 939 | || p instanceof AST_UnaryPrefix && p.operator == "!" && p.expression === self) { 940 | return true; 941 | } 942 | if (p instanceof AST_Binary && (p.operator == "&&" || p.operator == "||") 943 | || p instanceof AST_Conditional 944 | || p.tail_node() === self) { 945 | self = p; 946 | } else { 947 | return false; 948 | } 949 | } 950 | } 951 | }; 952 | -------------------------------------------------------------------------------- /resources/uglify/minify.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var to_ascii = typeof atob == "undefined" ? function(b64) { 4 | return new Buffer(b64, "base64").toString(); 5 | } : atob; 6 | var to_base64 = typeof btoa == "undefined" ? function(str) { 7 | return new Buffer(str).toString("base64"); 8 | } : btoa; 9 | 10 | function read_source_map(name, code) { 11 | var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code); 12 | if (!match) { 13 | AST_Node.warn("inline source map not found: " + name); 14 | return null; 15 | } 16 | return to_ascii(match[2]); 17 | } 18 | 19 | function parse_source_map(content) { 20 | try { 21 | return JSON.parse(content); 22 | } catch (ex) { 23 | throw new Error("invalid input source map: " + content); 24 | } 25 | } 26 | 27 | function set_shorthand(name, options, keys) { 28 | if (options[name]) { 29 | keys.forEach(function(key) { 30 | if (options[key]) { 31 | if (typeof options[key] != "object") options[key] = {}; 32 | if (!(name in options[key])) options[key][name] = options[name]; 33 | } 34 | }); 35 | } 36 | } 37 | 38 | function init_cache(cache) { 39 | if (!cache) return; 40 | if (!("props" in cache)) { 41 | cache.props = new Dictionary(); 42 | } else if (!(cache.props instanceof Dictionary)) { 43 | cache.props = Dictionary.fromObject(cache.props); 44 | } 45 | } 46 | 47 | function to_json(cache) { 48 | return { 49 | props: cache.props.toObject() 50 | }; 51 | } 52 | 53 | function minify(files, options) { 54 | var warn_function = AST_Node.warn_function; 55 | try { 56 | options = defaults(options, { 57 | compress: {}, 58 | enclose: false, 59 | ie8: false, 60 | keep_fnames: false, 61 | mangle: {}, 62 | nameCache: null, 63 | output: {}, 64 | parse: {}, 65 | rename: undefined, 66 | sourceMap: false, 67 | timings: false, 68 | toplevel: false, 69 | warnings: false, 70 | wrap: false, 71 | }, true); 72 | var timings = options.timings && { 73 | start: Date.now() 74 | }; 75 | if (options.rename === undefined) { 76 | options.rename = options.compress && options.mangle; 77 | } 78 | set_shorthand("ie8", options, [ "compress", "mangle", "output" ]); 79 | set_shorthand("keep_fnames", options, [ "compress", "mangle" ]); 80 | set_shorthand("toplevel", options, [ "compress", "mangle" ]); 81 | set_shorthand("warnings", options, [ "compress" ]); 82 | var quoted_props; 83 | if (options.mangle) { 84 | options.mangle = defaults(options.mangle, { 85 | cache: options.nameCache && (options.nameCache.vars || {}), 86 | eval: false, 87 | ie8: false, 88 | keep_fnames: false, 89 | properties: false, 90 | reserved: [], 91 | toplevel: false, 92 | }, true); 93 | if (options.mangle.properties) { 94 | if (typeof options.mangle.properties != "object") { 95 | options.mangle.properties = {}; 96 | } 97 | if (options.mangle.properties.keep_quoted) { 98 | quoted_props = options.mangle.properties.reserved; 99 | if (!Array.isArray(quoted_props)) quoted_props = []; 100 | options.mangle.properties.reserved = quoted_props; 101 | } 102 | if (options.nameCache && !("cache" in options.mangle.properties)) { 103 | options.mangle.properties.cache = options.nameCache.props || {}; 104 | } 105 | } 106 | init_cache(options.mangle.cache); 107 | init_cache(options.mangle.properties.cache); 108 | } 109 | if (options.sourceMap) { 110 | options.sourceMap = defaults(options.sourceMap, { 111 | content: null, 112 | filename: null, 113 | includeSources: false, 114 | root: null, 115 | url: null, 116 | }, true); 117 | } 118 | var warnings = []; 119 | if (options.warnings && !AST_Node.warn_function) { 120 | AST_Node.warn_function = function(warning) { 121 | warnings.push(warning); 122 | }; 123 | } 124 | if (timings) timings.parse = Date.now(); 125 | var source_maps, toplevel; 126 | if (files instanceof AST_Toplevel) { 127 | toplevel = files; 128 | } else { 129 | if (typeof files == "string") { 130 | files = [ files ]; 131 | } 132 | options.parse = options.parse || {}; 133 | options.parse.toplevel = null; 134 | var source_map_content = options.sourceMap && options.sourceMap.content; 135 | if (typeof source_map_content == "string" && source_map_content != "inline") { 136 | source_map_content = parse_source_map(source_map_content); 137 | } 138 | source_maps = source_map_content && Object.create(null); 139 | for (var name in files) if (HOP(files, name)) { 140 | options.parse.filename = name; 141 | options.parse.toplevel = parse(files[name], options.parse); 142 | if (source_maps) { 143 | if (source_map_content == "inline") { 144 | var inlined_content = read_source_map(name, files[name]); 145 | if (inlined_content) { 146 | source_maps[name] = parse_source_map(inlined_content); 147 | } 148 | } else { 149 | source_maps[name] = source_map_content; 150 | } 151 | } 152 | } 153 | toplevel = options.parse.toplevel; 154 | } 155 | if (quoted_props) { 156 | reserve_quoted_keys(toplevel, quoted_props); 157 | } 158 | if (options.wrap) { 159 | toplevel = toplevel.wrap_commonjs(options.wrap); 160 | } 161 | if (options.enclose) { 162 | toplevel = toplevel.wrap_enclose(options.enclose); 163 | } 164 | if (timings) timings.rename = Date.now(); 165 | if (options.rename) { 166 | toplevel.figure_out_scope(options.mangle); 167 | toplevel.expand_names(options.mangle); 168 | } 169 | if (timings) timings.compress = Date.now(); 170 | if (options.compress) toplevel = new Compressor(options.compress).compress(toplevel); 171 | if (timings) timings.scope = Date.now(); 172 | if (options.mangle) toplevel.figure_out_scope(options.mangle); 173 | if (timings) timings.mangle = Date.now(); 174 | if (options.mangle) { 175 | toplevel.compute_char_frequency(options.mangle); 176 | toplevel.mangle_names(options.mangle); 177 | } 178 | if (timings) timings.properties = Date.now(); 179 | if (options.mangle && options.mangle.properties) { 180 | toplevel = mangle_properties(toplevel, options.mangle.properties); 181 | } 182 | if (timings) timings.output = Date.now(); 183 | var result = {}; 184 | if (options.output.ast) { 185 | result.ast = toplevel; 186 | } 187 | if (!HOP(options.output, "code") || options.output.code) { 188 | if (options.sourceMap) { 189 | options.output.source_map = SourceMap({ 190 | file: options.sourceMap.filename, 191 | orig: source_maps, 192 | root: options.sourceMap.root 193 | }); 194 | if (options.sourceMap.includeSources) { 195 | if (files instanceof AST_Toplevel) { 196 | throw new Error("original source content unavailable"); 197 | } else for (var name in files) if (HOP(files, name)) { 198 | options.output.source_map.get().setSourceContent(name, files[name]); 199 | } 200 | } else { 201 | options.output.source_map.get()._sourcesContents = null; 202 | } 203 | } 204 | delete options.output.ast; 205 | delete options.output.code; 206 | var stream = OutputStream(options.output); 207 | toplevel.print(stream); 208 | result.code = stream.get(); 209 | if (options.sourceMap) { 210 | result.map = options.output.source_map.toString(); 211 | if (options.sourceMap.url == "inline") { 212 | result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map); 213 | } else if (options.sourceMap.url) { 214 | result.code += "\n//# sourceMappingURL=" + options.sourceMap.url; 215 | } 216 | } 217 | } 218 | if (options.nameCache && options.mangle) { 219 | if (options.mangle.cache) options.nameCache.vars = to_json(options.mangle.cache); 220 | if (options.mangle.properties && options.mangle.properties.cache) { 221 | options.nameCache.props = to_json(options.mangle.properties.cache); 222 | } 223 | } 224 | if (timings) { 225 | timings.end = Date.now(); 226 | result.timings = { 227 | parse: 1e-3 * (timings.rename - timings.parse), 228 | rename: 1e-3 * (timings.compress - timings.rename), 229 | compress: 1e-3 * (timings.scope - timings.compress), 230 | scope: 1e-3 * (timings.mangle - timings.scope), 231 | mangle: 1e-3 * (timings.properties - timings.mangle), 232 | properties: 1e-3 * (timings.output - timings.properties), 233 | output: 1e-3 * (timings.end - timings.output), 234 | total: 1e-3 * (timings.end - timings.start) 235 | } 236 | } 237 | if (warnings.length) { 238 | result.warnings = warnings; 239 | } 240 | return result; 241 | } catch (ex) { 242 | return { error: ex }; 243 | } finally { 244 | AST_Node.warn_function = warn_function; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /resources/uglify/scope.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | function SymbolDef(scope, orig, init) { 47 | this.name = orig.name; 48 | this.orig = [ orig ]; 49 | this.init = init; 50 | this.eliminated = 0; 51 | this.scope = scope; 52 | this.references = []; 53 | this.replaced = 0; 54 | this.global = false; 55 | this.mangled_name = null; 56 | this.undeclared = false; 57 | this.id = SymbolDef.next_id++; 58 | } 59 | 60 | SymbolDef.next_id = 1; 61 | 62 | SymbolDef.prototype = { 63 | unmangleable: function(options) { 64 | if (!options) options = {}; 65 | 66 | return this.global && !options.toplevel 67 | || this.undeclared 68 | || !options.eval && this.scope.pinned() 69 | || options.keep_fnames 70 | && (this.orig[0] instanceof AST_SymbolLambda 71 | || this.orig[0] instanceof AST_SymbolDefun); 72 | }, 73 | mangle: function(options) { 74 | var cache = options.cache && options.cache.props; 75 | if (this.global && cache && cache.has(this.name)) { 76 | this.mangled_name = cache.get(this.name); 77 | } else if (!this.mangled_name && !this.unmangleable(options)) { 78 | var def; 79 | if (def = this.redefined()) { 80 | this.mangled_name = def.mangled_name || def.name; 81 | } else { 82 | this.mangled_name = next_mangled_name(this.scope, options, this); 83 | } 84 | if (this.global && cache) { 85 | cache.set(this.name, this.mangled_name); 86 | } 87 | } 88 | }, 89 | redefined: function() { 90 | return this.defun && this.defun.variables.get(this.name); 91 | } 92 | }; 93 | 94 | AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) { 95 | options = defaults(options, { 96 | cache: null, 97 | ie8: false, 98 | }); 99 | 100 | // pass 1: setup scope chaining and handle definitions 101 | var self = this; 102 | var scope = self.parent_scope = null; 103 | var defun = null; 104 | var tw = new TreeWalker(function(node, descend) { 105 | if (node instanceof AST_Catch) { 106 | var save_scope = scope; 107 | scope = new AST_Scope(node); 108 | scope.init_scope_vars(save_scope); 109 | descend(); 110 | scope = save_scope; 111 | return true; 112 | } 113 | if (node instanceof AST_Scope) { 114 | node.init_scope_vars(scope); 115 | var save_scope = scope; 116 | var save_defun = defun; 117 | defun = scope = node; 118 | descend(); 119 | scope = save_scope; 120 | defun = save_defun; 121 | return true; 122 | } 123 | if (node instanceof AST_With) { 124 | for (var s = scope; s; s = s.parent_scope) s.uses_with = true; 125 | return; 126 | } 127 | if (node instanceof AST_Symbol) { 128 | node.scope = scope; 129 | } 130 | if (node instanceof AST_Label) { 131 | node.thedef = node; 132 | node.references = []; 133 | } 134 | if (node instanceof AST_SymbolDefun) { 135 | // This should be defined in the parent scope, as we encounter the 136 | // AST_Defun node before getting to its AST_Symbol. 137 | (node.scope = defun.parent_scope.resolve()).def_function(node, defun); 138 | } else if (node instanceof AST_SymbolLambda) { 139 | var def = defun.def_function(node, node.name == "arguments" ? undefined : defun); 140 | if (options.ie8) def.defun = defun.parent_scope.resolve(); 141 | } else if (node instanceof AST_SymbolVar) { 142 | defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined); 143 | if (defun !== scope) { 144 | node.mark_enclosed(options); 145 | var def = scope.find_variable(node); 146 | if (node.thedef !== def) { 147 | node.thedef = def; 148 | } 149 | node.reference(options); 150 | } 151 | } else if (node instanceof AST_SymbolCatch) { 152 | scope.def_variable(node).defun = defun; 153 | } 154 | }); 155 | self.walk(tw); 156 | 157 | // pass 2: find back references and eval 158 | self.globals = new Dictionary(); 159 | var tw = new TreeWalker(function(node) { 160 | if (node instanceof AST_LoopControl) { 161 | if (node.label) node.label.thedef.references.push(node); 162 | return true; 163 | } 164 | if (node instanceof AST_SymbolRef) { 165 | var name = node.name; 166 | if (name == "eval" && tw.parent() instanceof AST_Call) { 167 | for (var s = node.scope; s && !s.uses_eval; s = s.parent_scope) { 168 | s.uses_eval = true; 169 | } 170 | } 171 | var sym = node.scope.find_variable(name); 172 | if (!sym) { 173 | sym = self.def_global(node); 174 | } else if (sym.scope instanceof AST_Lambda && name == "arguments") { 175 | sym.scope.uses_arguments = true; 176 | } 177 | node.thedef = sym; 178 | node.reference(options); 179 | return true; 180 | } 181 | // ensure mangling works if catch reuses a scope variable 182 | if (node instanceof AST_SymbolCatch) { 183 | var def = node.definition().redefined(); 184 | if (def) for (var s = node.scope; s; s = s.parent_scope) { 185 | push_uniq(s.enclosed, def); 186 | if (s === def.scope) break; 187 | } 188 | return true; 189 | } 190 | }); 191 | self.walk(tw); 192 | 193 | // pass 3: fix up any scoping issue with IE8 194 | if (options.ie8) self.walk(new TreeWalker(function(node) { 195 | if (node instanceof AST_SymbolCatch) { 196 | redefine(node, node.thedef.defun); 197 | return true; 198 | } 199 | if (node instanceof AST_SymbolLambda) { 200 | var def = node.thedef; 201 | if (def.orig.length == 1) { 202 | redefine(node, node.scope.parent_scope); 203 | node.thedef.init = def.init; 204 | } 205 | return true; 206 | } 207 | })); 208 | 209 | function redefine(node, scope) { 210 | var name = node.name; 211 | var refs = node.thedef.references; 212 | var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node); 213 | refs.forEach(function(ref) { 214 | ref.thedef = def; 215 | ref.reference(options); 216 | }); 217 | node.thedef = def; 218 | node.reference(options); 219 | } 220 | }); 221 | 222 | AST_Toplevel.DEFMETHOD("def_global", function(node) { 223 | var globals = this.globals, name = node.name; 224 | if (globals.has(name)) { 225 | return globals.get(name); 226 | } else { 227 | var g = new SymbolDef(this, node); 228 | g.undeclared = true; 229 | g.global = true; 230 | globals.set(name, g); 231 | return g; 232 | } 233 | }); 234 | 235 | AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) { 236 | this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions) 237 | this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope) 238 | this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement 239 | this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval` 240 | this.parent_scope = parent_scope; // the parent scope 241 | this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes 242 | this.cname = -1; // the current index for mangling functions/variables 243 | }); 244 | 245 | AST_Lambda.DEFMETHOD("init_scope_vars", function() { 246 | AST_Scope.prototype.init_scope_vars.apply(this, arguments); 247 | this.uses_arguments = false; 248 | this.def_variable(new AST_SymbolFunarg({ 249 | name: "arguments", 250 | start: this.start, 251 | end: this.end 252 | })); 253 | }); 254 | 255 | AST_Symbol.DEFMETHOD("mark_enclosed", function(options) { 256 | var def = this.definition(); 257 | for (var s = this.scope; s; s = s.parent_scope) { 258 | push_uniq(s.enclosed, def); 259 | if (options.keep_fnames) { 260 | s.functions.each(function(d) { 261 | push_uniq(def.scope.enclosed, d); 262 | }); 263 | } 264 | if (s === def.scope) break; 265 | } 266 | }); 267 | 268 | AST_Symbol.DEFMETHOD("reference", function(options) { 269 | this.definition().references.push(this); 270 | this.mark_enclosed(options); 271 | }); 272 | 273 | AST_Scope.DEFMETHOD("find_variable", function(name) { 274 | if (name instanceof AST_Symbol) name = name.name; 275 | return this.variables.get(name) 276 | || (this.parent_scope && this.parent_scope.find_variable(name)); 277 | }); 278 | 279 | AST_Scope.DEFMETHOD("def_function", function(symbol, init) { 280 | var def = this.def_variable(symbol, init); 281 | if (!def.init || def.init instanceof AST_Defun) def.init = init; 282 | this.functions.set(symbol.name, def); 283 | return def; 284 | }); 285 | 286 | AST_Scope.DEFMETHOD("def_variable", function(symbol, init) { 287 | var def = this.variables.get(symbol.name); 288 | if (def) { 289 | def.orig.push(symbol); 290 | if (def.init && (def.scope !== symbol.scope || def.init instanceof AST_Function)) { 291 | def.init = init; 292 | } 293 | } else { 294 | def = new SymbolDef(this, symbol, init); 295 | this.variables.set(symbol.name, def); 296 | def.global = !this.parent_scope; 297 | } 298 | return symbol.thedef = def; 299 | }); 300 | 301 | AST_Lambda.DEFMETHOD("resolve", return_this); 302 | AST_Scope.DEFMETHOD("resolve", function() { 303 | return this.parent_scope; 304 | }); 305 | AST_Toplevel.DEFMETHOD("resolve", return_this); 306 | 307 | function names_in_use(scope, options) { 308 | var names = scope.names_in_use; 309 | if (!names) { 310 | scope.names_in_use = names = Object.create(scope.mangled_names || null); 311 | scope.cname_holes = []; 312 | scope.enclosed.forEach(function(def) { 313 | if (def.unmangleable(options)) names[def.name] = true; 314 | }); 315 | } 316 | return names; 317 | } 318 | 319 | function next_mangled_name(scope, options, def) { 320 | var in_use = names_in_use(scope, options); 321 | var holes = scope.cname_holes; 322 | var names = Object.create(null); 323 | var scopes = [ scope ]; 324 | def.references.forEach(function(sym) { 325 | var scope = sym.scope; 326 | do { 327 | if (scopes.indexOf(scope) < 0) { 328 | for (var name in names_in_use(scope, options)) { 329 | names[name] = true; 330 | } 331 | scopes.push(scope); 332 | } else break; 333 | } while (scope = scope.parent_scope); 334 | }); 335 | var name; 336 | for (var i = 0, len = holes.length; i < len; i++) { 337 | name = base54(holes[i]); 338 | if (names[name]) continue; 339 | holes.splice(i, 1); 340 | scope.names_in_use[name] = true; 341 | return name; 342 | } 343 | while (true) { 344 | name = base54(++scope.cname); 345 | if (in_use[name] || !is_identifier(name) || options.reserved.has[name]) continue; 346 | if (!names[name]) break; 347 | holes.push(scope.cname); 348 | } 349 | scope.names_in_use[name] = true; 350 | return name; 351 | } 352 | 353 | AST_Symbol.DEFMETHOD("unmangleable", function(options) { 354 | var def = this.definition(); 355 | return !def || def.unmangleable(options); 356 | }); 357 | 358 | // labels are always mangleable 359 | AST_Label.DEFMETHOD("unmangleable", return_false); 360 | 361 | AST_Symbol.DEFMETHOD("unreferenced", function() { 362 | return !this.definition().references.length && !this.scope.pinned(); 363 | }); 364 | 365 | AST_Symbol.DEFMETHOD("definition", function() { 366 | return this.thedef; 367 | }); 368 | 369 | AST_Symbol.DEFMETHOD("global", function() { 370 | return this.definition().global; 371 | }); 372 | 373 | function _default_mangler_options(options) { 374 | options = defaults(options, { 375 | eval : false, 376 | ie8 : false, 377 | keep_fnames : false, 378 | reserved : [], 379 | toplevel : false, 380 | }); 381 | if (!Array.isArray(options.reserved)) options.reserved = []; 382 | // Never mangle arguments 383 | push_uniq(options.reserved, "arguments"); 384 | options.reserved.has = makePredicate(options.reserved); 385 | return options; 386 | } 387 | 388 | AST_Toplevel.DEFMETHOD("mangle_names", function(options) { 389 | options = _default_mangler_options(options); 390 | 391 | // We only need to mangle declaration nodes. Special logic wired 392 | // into the code generator will display the mangled name if it's 393 | // present (and for AST_SymbolRef-s it'll use the mangled name of 394 | // the AST_SymbolDeclaration that it points to). 395 | var lname = -1; 396 | 397 | if (options.cache && options.cache.props) { 398 | var mangled_names = this.mangled_names = Object.create(null); 399 | options.cache.props.each(function(mangled_name) { 400 | mangled_names[mangled_name] = true; 401 | }); 402 | } 403 | 404 | var redefined = []; 405 | var tw = new TreeWalker(function(node, descend) { 406 | if (node instanceof AST_LabeledStatement) { 407 | // lname is incremented when we get to the AST_Label 408 | var save_nesting = lname; 409 | descend(); 410 | lname = save_nesting; 411 | return true; 412 | } 413 | if (node instanceof AST_Scope) { 414 | descend(); 415 | if (options.cache && node instanceof AST_Toplevel) { 416 | node.globals.each(mangle); 417 | } 418 | node.variables.each(mangle); 419 | return true; 420 | } 421 | if (node instanceof AST_Label) { 422 | var name; 423 | do { 424 | name = base54(++lname); 425 | } while (!is_identifier(name)); 426 | node.mangled_name = name; 427 | return true; 428 | } 429 | if (!options.ie8 && node instanceof AST_Catch) { 430 | var def = node.argname.definition(); 431 | var redef = def.redefined(); 432 | if (redef) { 433 | redefined.push(def); 434 | reference(node.argname); 435 | def.references.forEach(reference); 436 | } 437 | descend(); 438 | if (!redef) mangle(def); 439 | return true; 440 | } 441 | 442 | function reference(sym) { 443 | sym.thedef = redef; 444 | sym.reference(options); 445 | sym.thedef = def; 446 | } 447 | }); 448 | this.walk(tw); 449 | redefined.forEach(mangle); 450 | 451 | function mangle(def) { 452 | if (options.reserved.has[def.name]) return; 453 | def.mangle(options); 454 | } 455 | }); 456 | 457 | AST_Toplevel.DEFMETHOD("find_colliding_names", function(options) { 458 | var cache = options.cache && options.cache.props; 459 | var avoid = Object.create(null); 460 | options.reserved.forEach(to_avoid); 461 | this.globals.each(add_def); 462 | this.walk(new TreeWalker(function(node) { 463 | if (node instanceof AST_Scope) node.variables.each(add_def); 464 | if (node instanceof AST_SymbolCatch) add_def(node.definition()); 465 | })); 466 | return avoid; 467 | 468 | function to_avoid(name) { 469 | avoid[name] = true; 470 | } 471 | 472 | function add_def(def) { 473 | var name = def.name; 474 | if (def.global && cache && cache.has(name)) name = cache.get(name); 475 | else if (!def.unmangleable(options)) return; 476 | to_avoid(name); 477 | } 478 | }); 479 | 480 | AST_Toplevel.DEFMETHOD("expand_names", function(options) { 481 | base54.reset(); 482 | base54.sort(); 483 | options = _default_mangler_options(options); 484 | var avoid = this.find_colliding_names(options); 485 | var cname = 0; 486 | this.globals.each(rename); 487 | this.walk(new TreeWalker(function(node) { 488 | if (node instanceof AST_Scope) node.variables.each(rename); 489 | if (node instanceof AST_SymbolCatch) rename(node.definition()); 490 | })); 491 | 492 | function next_name() { 493 | var name; 494 | do { 495 | name = base54(cname++); 496 | } while (avoid[name] || !is_identifier(name)); 497 | return name; 498 | } 499 | 500 | function rename(def) { 501 | if (def.global && options.cache) return; 502 | if (def.unmangleable(options)) return; 503 | if (options.reserved.has[def.name]) return; 504 | var d = def.redefined(); 505 | def.name = d ? d.name : next_name(); 506 | def.orig.forEach(function(sym) { 507 | sym.name = def.name; 508 | }); 509 | def.references.forEach(function(sym) { 510 | sym.name = def.name; 511 | }); 512 | } 513 | }); 514 | 515 | AST_Node.DEFMETHOD("tail_node", return_this); 516 | AST_Sequence.DEFMETHOD("tail_node", function() { 517 | return this.expressions[this.expressions.length - 1]; 518 | }); 519 | 520 | AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) { 521 | options = _default_mangler_options(options); 522 | base54.reset(); 523 | try { 524 | AST_Node.prototype.print = function(stream, force_parens) { 525 | this._print(stream, force_parens); 526 | if (this instanceof AST_Symbol && !this.unmangleable(options)) { 527 | base54.consider(this.name, -1); 528 | } else if (options.properties) { 529 | if (this instanceof AST_Dot) { 530 | base54.consider(this.property, -1); 531 | } else if (this instanceof AST_Sub) { 532 | skip_string(this.property); 533 | } 534 | } 535 | }; 536 | base54.consider(this.print_to_string(), 1); 537 | } finally { 538 | AST_Node.prototype.print = AST_Node.prototype._print; 539 | } 540 | base54.sort(); 541 | 542 | function skip_string(node) { 543 | if (node instanceof AST_String) { 544 | base54.consider(node.value, -1); 545 | } else if (node instanceof AST_Conditional) { 546 | skip_string(node.consequent); 547 | skip_string(node.alternative); 548 | } else if (node instanceof AST_Sequence) { 549 | skip_string(node.tail_node()); 550 | } 551 | } 552 | }); 553 | 554 | var base54 = (function() { 555 | var freq = Object.create(null); 556 | function init(chars) { 557 | var array = []; 558 | for (var i = 0, len = chars.length; i < len; i++) { 559 | var ch = chars[i]; 560 | array.push(ch); 561 | freq[ch] = -1e-2 * i; 562 | } 563 | return array; 564 | } 565 | var digits = init("0123456789"); 566 | var leading = init("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_"); 567 | var chars, frequency; 568 | function reset() { 569 | frequency = Object.create(freq); 570 | } 571 | base54.consider = function(str, delta) { 572 | for (var i = str.length; --i >= 0;) { 573 | frequency[str[i]] += delta; 574 | } 575 | }; 576 | function compare(a, b) { 577 | return frequency[b] - frequency[a]; 578 | } 579 | base54.sort = function() { 580 | chars = leading.sort(compare).concat(digits.sort(compare)); 581 | }; 582 | base54.reset = reset; 583 | reset(); 584 | function base54(num) { 585 | var ret = "", base = 54; 586 | num++; 587 | do { 588 | num--; 589 | ret += chars[num % base]; 590 | num = Math.floor(num / base); 591 | base = 64; 592 | } while (num > 0); 593 | return ret; 594 | } 595 | return base54; 596 | })(); 597 | -------------------------------------------------------------------------------- /resources/uglify/transform.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | function TreeTransformer(before, after) { 47 | TreeWalker.call(this); 48 | this.before = before; 49 | this.after = after; 50 | } 51 | TreeTransformer.prototype = new TreeWalker; 52 | 53 | (function(DEF) { 54 | function do_list(list, tw) { 55 | return MAP(list, function(node) { 56 | return node.transform(tw, true); 57 | }); 58 | } 59 | 60 | DEF(AST_Node, noop); 61 | DEF(AST_LabeledStatement, function(self, tw) { 62 | self.label = self.label.transform(tw); 63 | self.body = self.body.transform(tw); 64 | }); 65 | DEF(AST_SimpleStatement, function(self, tw) { 66 | self.body = self.body.transform(tw); 67 | }); 68 | DEF(AST_Block, function(self, tw) { 69 | self.body = do_list(self.body, tw); 70 | }); 71 | DEF(AST_Do, function(self, tw) { 72 | self.body = self.body.transform(tw); 73 | self.condition = self.condition.transform(tw); 74 | }); 75 | DEF(AST_While, function(self, tw) { 76 | self.condition = self.condition.transform(tw); 77 | self.body = self.body.transform(tw); 78 | }); 79 | DEF(AST_For, function(self, tw) { 80 | if (self.init) self.init = self.init.transform(tw); 81 | if (self.condition) self.condition = self.condition.transform(tw); 82 | if (self.step) self.step = self.step.transform(tw); 83 | self.body = self.body.transform(tw); 84 | }); 85 | DEF(AST_ForIn, function(self, tw) { 86 | self.init = self.init.transform(tw); 87 | self.object = self.object.transform(tw); 88 | self.body = self.body.transform(tw); 89 | }); 90 | DEF(AST_With, function(self, tw) { 91 | self.expression = self.expression.transform(tw); 92 | self.body = self.body.transform(tw); 93 | }); 94 | DEF(AST_Exit, function(self, tw) { 95 | if (self.value) self.value = self.value.transform(tw); 96 | }); 97 | DEF(AST_LoopControl, function(self, tw) { 98 | if (self.label) self.label = self.label.transform(tw); 99 | }); 100 | DEF(AST_If, function(self, tw) { 101 | self.condition = self.condition.transform(tw); 102 | self.body = self.body.transform(tw); 103 | if (self.alternative) self.alternative = self.alternative.transform(tw); 104 | }); 105 | DEF(AST_Switch, function(self, tw) { 106 | self.expression = self.expression.transform(tw); 107 | self.body = do_list(self.body, tw); 108 | }); 109 | DEF(AST_Case, function(self, tw) { 110 | self.expression = self.expression.transform(tw); 111 | self.body = do_list(self.body, tw); 112 | }); 113 | DEF(AST_Try, function(self, tw) { 114 | self.body = do_list(self.body, tw); 115 | if (self.bcatch) self.bcatch = self.bcatch.transform(tw); 116 | if (self.bfinally) self.bfinally = self.bfinally.transform(tw); 117 | }); 118 | DEF(AST_Catch, function(self, tw) { 119 | self.argname = self.argname.transform(tw); 120 | self.body = do_list(self.body, tw); 121 | }); 122 | DEF(AST_Definitions, function(self, tw) { 123 | self.definitions = do_list(self.definitions, tw); 124 | }); 125 | DEF(AST_VarDef, function(self, tw) { 126 | self.name = self.name.transform(tw); 127 | if (self.value) self.value = self.value.transform(tw); 128 | }); 129 | DEF(AST_Lambda, function(self, tw) { 130 | if (self.name) self.name = self.name.transform(tw); 131 | self.argnames = do_list(self.argnames, tw); 132 | self.body = do_list(self.body, tw); 133 | }); 134 | DEF(AST_Call, function(self, tw) { 135 | self.expression = self.expression.transform(tw); 136 | self.args = do_list(self.args, tw); 137 | }); 138 | DEF(AST_Sequence, function(self, tw) { 139 | self.expressions = do_list(self.expressions, tw); 140 | }); 141 | DEF(AST_Dot, function(self, tw) { 142 | self.expression = self.expression.transform(tw); 143 | }); 144 | DEF(AST_Sub, function(self, tw) { 145 | self.expression = self.expression.transform(tw); 146 | self.property = self.property.transform(tw); 147 | }); 148 | DEF(AST_Unary, function(self, tw) { 149 | self.expression = self.expression.transform(tw); 150 | }); 151 | DEF(AST_Binary, function(self, tw) { 152 | self.left = self.left.transform(tw); 153 | self.right = self.right.transform(tw); 154 | }); 155 | DEF(AST_Conditional, function(self, tw) { 156 | self.condition = self.condition.transform(tw); 157 | self.consequent = self.consequent.transform(tw); 158 | self.alternative = self.alternative.transform(tw); 159 | }); 160 | DEF(AST_Array, function(self, tw) { 161 | self.elements = do_list(self.elements, tw); 162 | }); 163 | DEF(AST_Object, function(self, tw) { 164 | self.properties = do_list(self.properties, tw); 165 | }); 166 | DEF(AST_ObjectProperty, function(self, tw) { 167 | self.value = self.value.transform(tw); 168 | }); 169 | })(function(node, descend) { 170 | node.DEFMETHOD("transform", function(tw, in_list) { 171 | var x, y; 172 | tw.push(this); 173 | if (tw.before) x = tw.before(this, descend, in_list); 174 | if (typeof x === "undefined") { 175 | x = this; 176 | descend(x, tw); 177 | if (tw.after) { 178 | y = tw.after(x, in_list); 179 | if (typeof y !== "undefined") x = y; 180 | } 181 | } 182 | tw.pop(); 183 | return x; 184 | }); 185 | }); 186 | -------------------------------------------------------------------------------- /resources/uglify/utils.js: -------------------------------------------------------------------------------- 1 | /*********************************************************************** 2 | 3 | A JavaScript tokenizer / parser / beautifier / compressor. 4 | https://github.com/mishoo/UglifyJS2 5 | 6 | -------------------------------- (C) --------------------------------- 7 | 8 | Author: Mihai Bazon 9 | 10 | http://mihai.bazon.net/blog 11 | 12 | Distributed under the BSD license: 13 | 14 | Copyright 2012 (c) Mihai Bazon 15 | 16 | Redistribution and use in source and binary forms, with or without 17 | modification, are permitted provided that the following conditions 18 | are met: 19 | 20 | * Redistributions of source code must retain the above 21 | copyright notice, this list of conditions and the following 22 | disclaimer. 23 | 24 | * Redistributions in binary form must reproduce the above 25 | copyright notice, this list of conditions and the following 26 | disclaimer in the documentation and/or other materials 27 | provided with the distribution. 28 | 29 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY 30 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 31 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE 33 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 34 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 | PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 | PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 37 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 38 | TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 39 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 40 | SUCH DAMAGE. 41 | 42 | ***********************************************************************/ 43 | 44 | "use strict"; 45 | 46 | function characters(str) { 47 | return str.split(""); 48 | } 49 | 50 | function member(name, array) { 51 | return array.indexOf(name) >= 0; 52 | } 53 | 54 | function find_if(func, array) { 55 | for (var i = array.length; --i >= 0;) if (func(array[i])) return array[i]; 56 | } 57 | 58 | function repeat_string(str, i) { 59 | if (i <= 0) return ""; 60 | if (i == 1) return str; 61 | var d = repeat_string(str, i >> 1); 62 | d += d; 63 | return i & 1 ? d + str : d; 64 | } 65 | 66 | function configure_error_stack(fn) { 67 | Object.defineProperty(fn.prototype, "stack", { 68 | get: function() { 69 | var err = new Error(this.message); 70 | err.name = this.name; 71 | try { 72 | throw err; 73 | } catch(e) { 74 | return e.stack; 75 | } 76 | } 77 | }); 78 | } 79 | 80 | function DefaultsError(msg, defs) { 81 | this.message = msg; 82 | this.defs = defs; 83 | } 84 | DefaultsError.prototype = Object.create(Error.prototype); 85 | DefaultsError.prototype.constructor = DefaultsError; 86 | DefaultsError.prototype.name = "DefaultsError"; 87 | configure_error_stack(DefaultsError); 88 | 89 | function defaults(args, defs, croak) { 90 | if (args === true) args = {}; 91 | var ret = args || {}; 92 | if (croak) for (var i in ret) if (HOP(ret, i) && !HOP(defs, i)) { 93 | throw new DefaultsError("`" + i + "` is not a supported option", defs); 94 | } 95 | for (var i in defs) if (HOP(defs, i)) { 96 | ret[i] = (args && HOP(args, i)) ? args[i] : defs[i]; 97 | } 98 | return ret; 99 | } 100 | 101 | function merge(obj, ext) { 102 | var count = 0; 103 | for (var i in ext) if (HOP(ext, i)) { 104 | obj[i] = ext[i]; 105 | count++; 106 | } 107 | return count; 108 | } 109 | 110 | function noop() {} 111 | function return_false() { return false; } 112 | function return_true() { return true; } 113 | function return_this() { return this; } 114 | function return_null() { return null; } 115 | 116 | var MAP = (function() { 117 | function MAP(a, f, backwards) { 118 | var ret = [], top = [], i; 119 | function doit() { 120 | var val = f(a[i], i); 121 | var is_last = val instanceof Last; 122 | if (is_last) val = val.v; 123 | if (val instanceof AtTop) { 124 | val = val.v; 125 | if (val instanceof Splice) { 126 | top.push.apply(top, backwards ? val.v.slice().reverse() : val.v); 127 | } else { 128 | top.push(val); 129 | } 130 | } 131 | else if (val !== skip) { 132 | if (val instanceof Splice) { 133 | ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v); 134 | } else { 135 | ret.push(val); 136 | } 137 | } 138 | return is_last; 139 | } 140 | if (Array.isArray(a)) { 141 | if (backwards) { 142 | for (i = a.length; --i >= 0;) if (doit()) break; 143 | ret.reverse(); 144 | top.reverse(); 145 | } else { 146 | for (i = 0; i < a.length; ++i) if (doit()) break; 147 | } 148 | } 149 | else { 150 | for (i in a) if (HOP(a, i)) if (doit()) break; 151 | } 152 | return top.concat(ret); 153 | } 154 | MAP.at_top = function(val) { return new AtTop(val) }; 155 | MAP.splice = function(val) { return new Splice(val) }; 156 | MAP.last = function(val) { return new Last(val) }; 157 | var skip = MAP.skip = {}; 158 | function AtTop(val) { this.v = val } 159 | function Splice(val) { this.v = val } 160 | function Last(val) { this.v = val } 161 | return MAP; 162 | })(); 163 | 164 | function push_uniq(array, el) { 165 | if (array.indexOf(el) < 0) return array.push(el); 166 | } 167 | 168 | function string_template(text, props) { 169 | return text.replace(/\{(.+?)\}/g, function(str, p) { 170 | return props && props[p]; 171 | }); 172 | } 173 | 174 | function remove(array, el) { 175 | var index = array.indexOf(el); 176 | if (index >= 0) array.splice(index, 1); 177 | } 178 | 179 | function makePredicate(words) { 180 | if (!Array.isArray(words)) words = words.split(" "); 181 | var map = Object.create(null); 182 | words.forEach(function(word) { 183 | map[word] = true; 184 | }); 185 | return map; 186 | } 187 | 188 | function all(array, predicate) { 189 | for (var i = array.length; --i >= 0;) 190 | if (!predicate(array[i])) 191 | return false; 192 | return true; 193 | } 194 | 195 | function Dictionary() { 196 | this._values = Object.create(null); 197 | this._size = 0; 198 | } 199 | Dictionary.prototype = { 200 | set: function(key, val) { 201 | if (!this.has(key)) ++this._size; 202 | this._values["$" + key] = val; 203 | return this; 204 | }, 205 | add: function(key, val) { 206 | if (this.has(key)) { 207 | this.get(key).push(val); 208 | } else { 209 | this.set(key, [ val ]); 210 | } 211 | return this; 212 | }, 213 | get: function(key) { return this._values["$" + key] }, 214 | del: function(key) { 215 | if (this.has(key)) { 216 | --this._size; 217 | delete this._values["$" + key]; 218 | } 219 | return this; 220 | }, 221 | has: function(key) { return ("$" + key) in this._values }, 222 | each: function(f) { 223 | for (var i in this._values) 224 | f(this._values[i], i.substr(1)); 225 | }, 226 | size: function() { 227 | return this._size; 228 | }, 229 | map: function(f) { 230 | var ret = []; 231 | for (var i in this._values) 232 | ret.push(f(this._values[i], i.substr(1))); 233 | return ret; 234 | }, 235 | clone: function() { 236 | var ret = new Dictionary(); 237 | for (var i in this._values) 238 | ret._values[i] = this._values[i]; 239 | ret._size = this._size; 240 | return ret; 241 | }, 242 | toObject: function() { return this._values } 243 | }; 244 | Dictionary.fromObject = function(obj) { 245 | var dict = new Dictionary(); 246 | dict._size = merge(dict._values, obj); 247 | return dict; 248 | }; 249 | 250 | function HOP(obj, prop) { 251 | return Object.prototype.hasOwnProperty.call(obj, prop); 252 | } 253 | 254 | // return true if the node at the top of the stack (that means the 255 | // innermost node in the current output) is lexically the first in 256 | // a statement. 257 | function first_in_statement(stack) { 258 | var node = stack.parent(-1); 259 | for (var i = 0, p; p = stack.parent(i++); node = p) { 260 | if (p.TYPE == "Call") { 261 | if (p.expression === node) continue; 262 | } else if (p instanceof AST_Binary) { 263 | if (p.left === node) continue; 264 | } else if (p instanceof AST_Conditional) { 265 | if (p.condition === node) continue; 266 | } else if (p instanceof AST_PropAccess) { 267 | if (p.expression === node) continue; 268 | } else if (p instanceof AST_Sequence) { 269 | if (p.expressions[0] === node) continue; 270 | } else if (p instanceof AST_Statement) { 271 | return p.body === node; 272 | } else if (p instanceof AST_UnaryPostfix) { 273 | if (p.expression === node) continue; 274 | } 275 | return false; 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * -------------------------------------------------------------------------------- /safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "LDOM", 3 | "short_name": "LDOM", 4 | "icons": [ 5 | { 6 | "src": "/android-chrome-192x192.png?v=1", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/android-chrome-256x256.png?v=1", 12 | "sizes": "256x256", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#ff8800", 17 | "background_color": "#ff8800" 18 | } 19 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | LDOM Tests 8 | 9 | 49 | 50 | 51 |

LDOM Tests

52 |
53 |
Aggregate Tests Results:
54 | 55 | - / - 56 | 57 |
58 |
59 |
60 |
61 |
62 |
63 | aBc 64 |
65 |
Some text
66 |
67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 |
Head 1Item 0
Row 1Item 1
77 |
78 |
79 |
80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /tests/script.js: -------------------------------------------------------------------------------- 1 | /* globals $ */ 2 | 3 | var nullElement = $(""); 4 | var keys = Object.keys(Object.getPrototypeOf(nullElement)); 5 | var methods = ["constructor"]; 6 | 7 | for (var i = 0; i < keys.length; i++) { 8 | if (keys[i][0] !== "_" && typeof nullElement[keys[i]] === "function") { 9 | methods.push(keys[i]); 10 | } 11 | } 12 | 13 | var testContainer = $("#testContainer"); 14 | var testContainerHTMLBackup = testContainer.html(); 15 | var tests = {}; 16 | for (var i = 0; i < methods.length; i++) { 17 | tests[methods[i]] = []; 18 | $("#testOutput").appendChild($("
").appendChild($("
").attr("id", "function-" + methods[i]).appendChild($("").text(methods[i] + ":").addClass("method-name")))); 19 | } 20 | function addTest(methodName, testName, run) { 21 | var testObj = { 22 | testName: testName.length > 0 ? testName + ": " : "", 23 | run: function(successCallback, failureCallback) { 24 | function assertMethodSkeleton(assertMethod, message, a, b) { 25 | var failureCallbackId; 26 | var errorObj = null; 27 | if (failureCallback) { 28 | failureCallbackId = setTimeout(function() { 29 | failureCallback(errorObj); 30 | }, 1); 31 | } 32 | try { 33 | assertMethod(); 34 | } catch (e) { 35 | errorObj = e; 36 | return; 37 | } 38 | if (failureCallback) { 39 | clearTimeout(failureCallbackId); 40 | } 41 | if (successCallback) { 42 | setTimeout(function() { 43 | successCallback(message); 44 | }, 1); 45 | } 46 | } 47 | testContainer.html(testContainerHTMLBackup); 48 | run({ 49 | equal: function(message, a, b) { 50 | assertMethodSkeleton(function() { 51 | if (a !== b) { 52 | message = message.trim() + " "; 53 | throw new Error("Assertion failed. " + message + "(Expected '" + a + "' === '" + b + "')"); 54 | } 55 | }, message, a, b); 56 | }, 57 | notEqual: function(message, a, b) { 58 | assertMethodSkeleton(function() { 59 | if (a === b) { 60 | message = message.trim() + " "; 61 | throw new Error("Assertion failed. " + message + "(Expected '" + a + "' !== '" + b + "')"); 62 | } 63 | }, message, a, b); 64 | }, 65 | greaterThan: function(message, a, b) { 66 | assertMethodSkeleton(function() { 67 | if (a < b) { 68 | message = message.trim() + " "; 69 | throw new Error("Assertion failed. " + message + "(Expected '" + a + "' > " + b + ")"); 70 | } 71 | }, message, a, b); 72 | }, 73 | lessThan: function(message, a, b) { 74 | assertMethodSkeleton(function() { 75 | if (a > b) { 76 | message = message.trim() + " "; 77 | throw new Error("Assertion failed. " + message + "(Expected '" + a + "' < '" + b + "')"); 78 | } 79 | }, message, a, b); 80 | } 81 | }); 82 | } 83 | }; 84 | tests[methodName].push(testObj); 85 | } 86 | 87 | 88 | 89 | // ------------------------------ Tests ------------------------------ 90 | 91 | 92 | // constructor 93 | addTest("constructor", "Aliased global variable", function(assert) { 94 | // eslint-disable-next-line no-undef 95 | assert.equal("LDOM aliased", $, LDOM); 96 | }); 97 | addTest("constructor", "Create element", function(assert) { 98 | assert.equal("Created object contains 1 element", $("").length, 1); 99 | assert.equal("Correct tag created", $("").get(0).tagName, "INPUT"); 100 | assert.equal("Correct custom tag created", $("").get(0).tagName, "DATA-TEST-123"); 101 | assert.equal("Selector doesn't create element", $("randomTag").length, 0); 102 | }); 103 | addTest("constructor", "Window constructor", function(assert) { 104 | assert.equal("Window object created", $(window).length, 1); 105 | assert.notEqual("Window has `on` method", typeof $(window).on, "undefined"); 106 | assert.equal("Window doesn't have `html` method", typeof $(window).html, "undefined"); 107 | }); 108 | addTest("constructor", "Null input", function(assert) { 109 | assert.equal("No args constructor contains no elements", $().length, 0); 110 | assert.equal("Null arg constructor contains no elements", $(null).length, 0); 111 | }); 112 | addTest("constructor", "LDOM object input", function(assert) { 113 | var input = $("
"); 114 | assert.equal("Returns same input", $(input), input); 115 | input = $(window); 116 | assert.equal("Returns same window", $(input), input); 117 | input = $("body"); 118 | assert.notEqual("Returns different object", $("body"), input); 119 | }); 120 | addTest("constructor", "DOM node input", function(assert) { 121 | var input = $(document.createElement("video")); 122 | assert.equal("Returns same input", $(input), input); 123 | assert.equal("Correct tag created", input.get(0).tagName, "VIDEO"); 124 | }); 125 | addTest("constructor", "DOM node array input", function(assert) { 126 | assert.equal("Empty array contains 0 elements", $([]).length, 0); 127 | var containerContents = document.querySelectorAll("#testContainer *"); 128 | assert.equal("Builds object with correct number of elements", $(containerContents).length, containerContents.length); 129 | assert.equal("Preserves first item", $(containerContents).get(0), containerContents[0]); 130 | assert.equal("Preserves last item", $(containerContents).get(-1), containerContents[containerContents.length - 1]); 131 | assert.equal("Array literal", $([document.createElement("div"), document.createElement("img")]).length, 2); 132 | }); 133 | addTest("constructor", "CSS selector string", function(assert) { 134 | assert.equal("Selector matches", $("#testContainer").length, 1); 135 | assert.equal("Complex selector", $("meta[charset]").attr("charset"), "UTF-8"); 136 | }); 137 | 138 | 139 | // each 140 | addTest("each", "Iterates through all elements", function(assert) { 141 | var elementsRemaining = document.querySelectorAll("#testContainer div").length; 142 | $("#testContainer div").each(function(i) { 143 | elementsRemaining--; 144 | assert.equal("Element correct", this.get(0), document.querySelectorAll("#testContainer div")[i]); 145 | }); 146 | assert.equal("All elements seen", elementsRemaining, 0); 147 | 148 | elementsRemaining = document.querySelectorAll("#testContainer div").length; 149 | $("#testContainer div").each(function(i) { 150 | elementsRemaining--; 151 | assert.equal("Element correct", this.get(0), document.querySelectorAll("#testContainer div")[i]); 152 | }, false); 153 | assert.equal("All elements seen", elementsRemaining, 0); 154 | }); 155 | addTest("each", "`this` is an LDOM object", function(assert) { 156 | $("#testContainer div").each(function() { 157 | assert.equal("this is LDOM object", this, $(this)); 158 | assert.equal("object contains one element", this.length, 1); 159 | }); 160 | }); 161 | addTest("each", "Iterates in reverse", function(assert) { 162 | var elementsRemaining = document.querySelectorAll("#testContainer div").length; 163 | $("#testContainer div").each(function(i) { 164 | elementsRemaining--; 165 | assert.equal("Element correct", this.get(0), document.querySelectorAll("#testContainer div")[i]); 166 | }, true); 167 | assert.equal("All elements seen", elementsRemaining, 0); 168 | }); 169 | addTest("each", "Callback argument is the index", function(assert) { 170 | var lastIndex = -1; 171 | $("#testContainer div").each(function(index) { 172 | assert.equal("index greater than last", index, lastIndex + 1); 173 | lastIndex = index; 174 | }); 175 | var lastIndex = $("#testContainer div").length; 176 | $("#testContainer div").each(function(index) { 177 | assert.equal("index less than last", index, lastIndex - 1); 178 | lastIndex = index; 179 | }, true); 180 | }); 181 | 182 | 183 | // equals 184 | addTest("equals", "", function(assert) { 185 | assert.equal("Objects are equal", $("#testContainer div").equals($("#testContainer").find("div")), true); 186 | assert.equal("Unordered objects are equal", $("#testContainer div").equals($(Array.prototype.slice.call(document.querySelectorAll("#testContainer div")).reverse())), true); 187 | assert.equal("Objects are not equal", $("#testContainer div").equals($("#testContainer").find("tr")), false); 188 | }); 189 | 190 | 191 | // find 192 | addTest("find", "", function(assert) { 193 | assert.equal("Find one element", $("#testContainer").find("#tableID").length, 1); 194 | assert.equal("Find all elements", $("#testContainer").find("tr").length, document.querySelectorAll("#testContainer tr").length); 195 | assert.equal("Find all elements of all objects", $("#testContainer tr").find("td").length, document.querySelectorAll("#testContainer td").length); 196 | }); 197 | 198 | 199 | // get 200 | addTest("get", "No arg", function(assert) { 201 | assert.equal("Returns array type", Array.isArray($("#testContainer tr").get()), true); 202 | assert.equal("Returns correct length array", $("#testContainer tr").get().length, document.querySelectorAll("#testContainer tr").length); 203 | assert.equal("Returns empty array", $("#noMatches").get().length, 0); 204 | }); 205 | addTest("get", "Positive index", function(assert) { 206 | assert.equal("Returns null if no elements", $("#noMatches").get(0), null); 207 | assert.equal("Returns null if out of bounds", $("#testContainer tr").get(1000), null); 208 | var elements = document.querySelectorAll("#testContainer div"); 209 | assert.equal("Returns correct element", $(elements).get(1), elements[1]); 210 | }); 211 | addTest("get", "Negative index", function(assert) { 212 | assert.equal("Returns null if out of bounds", $("#testContainer tr").get(-1000), null); 213 | var elements = document.querySelectorAll("#testContainer div"); 214 | assert.equal("Returns correct element", $(elements).get(-2), elements[elements.length - 2]); 215 | }); 216 | 217 | 218 | // on 219 | function noop() { 220 | 0; 221 | } 222 | addTest("on", "Event ID increments", function(assert) { 223 | var id = $("#testContainer").on("change", noop); 224 | assert.equal("Event ID is one more than previously", $("#testContainer").on("input", noop), id + 1); 225 | }); 226 | addTest("on", "Adds to all elements", function(assert) { 227 | var obj = $("#testContainer div"); 228 | var eventCount = []; 229 | obj.each(function() { 230 | eventCount.push(typeof this.get(0)._LDOMEvents !== "undefined" ? this.get(0)._LDOMEvents.length : 0); 231 | }); 232 | obj.on("load", noop); 233 | obj.each(function(i) { 234 | assert.equal("All elements have new event listeners", this.get(0)._LDOMEvents.length, eventCount[i] + 1); 235 | }); 236 | obj.filter(".abc"); 237 | var eventCount = []; 238 | obj.each(function() { 239 | eventCount.push(typeof this.get(0)._LDOMEvents !== "undefined" ? this.get(0)._LDOMEvents.length : 0); 240 | }); 241 | obj.on("custom-event", noop); 242 | obj.each(function(i) { 243 | assert.equal("All elements have more new event listeners", this.get(0)._LDOMEvents.length, eventCount[i] + 1); 244 | }); 245 | }); 246 | addTest("on", "Event handler `this` is LDOM node", function(assert) { 247 | $("#testContainer").on("mouseover", function() { 248 | assert.equal("`this` is the correct LDOM node", this.get(0), $("#testContainer").get(0)); 249 | }); 250 | $("#testContainer").trigger("mouseover"); 251 | }); 252 | addTest("on", "Event handler arg is event data", function(assert) { 253 | $("#testContainer").on("mouseover", function(evt) { 254 | assert.equal("Event data type is correct", evt.type, "mouseover"); 255 | }); 256 | $("#testContainer").trigger("mouseover"); 257 | var customEventData = { 258 | x: 10, 259 | y: 60, 260 | windowNode: window, 261 | nested: { 262 | custom: [1, 3, "hello"] 263 | } 264 | }; 265 | $("#testContainer").on("custom-event", function(evt) { 266 | assert.equal("Custom Event Data Present", customEventData.nested.custom, evt.nested.custom); 267 | }); 268 | $("#testContainer").trigger("custom-event", customEventData); 269 | }); 270 | addTest("on", "Stores metadata on node", function(assert) { 271 | $("#testContainer table").on("metadata-evt", noop); 272 | assert.notEqual("Metadata present", document.querySelector("#testContainer table")._LDOMEvents, undefined); 273 | assert.greaterThan("Metadata length greater than 0", document.querySelector("#testContainer table")._LDOMEvents.length, 0); 274 | assert.equal("Metadata present is array", Array.isArray(document.querySelector("#testContainer table")._LDOMEvents), true); 275 | }); 276 | 277 | 278 | //off 279 | addTest("off", "Removes all event listeners on element", function(assert) { 280 | $("#testContainer").on("test-event-1", noop); 281 | $("#testContainer").on("test-event-2", noop); 282 | assert.greaterThan("Event listeners list contains IDs", $("#testContainer").get(0)._LDOMEvents.length, 1); 283 | $("#testContainer").off(); 284 | assert.equal("Event listeners list undefined", $("#testContainer").get(0)._LDOMEvents, undefined); 285 | }); 286 | addTest("off", "Removes by event name", function(assert) { 287 | $("#testContainer").off(); 288 | $("#testContainer").on("test-event-3", noop); 289 | $("#testContainer").on("test-event-4", noop); 290 | assert.equal("Event listeners added successfully", $("#testContainer").get(0)._LDOMEvents.length, 2); 291 | $("#testContainer").off("test-event-3"); 292 | assert.equal("Event listener removed successfully", $("#testContainer").get(0)._LDOMEvents.length, 1); 293 | $("#testContainer").off("test-event-3"); 294 | }); 295 | addTest("off", "Removes by event ID", function(assert) { 296 | $("#testContainer").off(); 297 | $("#testContainer").on("test-event-3", noop); 298 | var evtId = $("#testContainer").on("test-event-4", noop); 299 | $("#testContainer").on("test-event-5", noop); 300 | assert.equal("Event listeners added successfully", $("#testContainer").get(0)._LDOMEvents.length, 3); 301 | $("#testContainer").off(evtId); 302 | assert.equal("Event listener removed successfully", $("#testContainer").get(0)._LDOMEvents.length, 2); 303 | $("#testContainer").off(evtId); 304 | }); 305 | addTest("off", "Removed function doesn't execute", function(assert) { 306 | var evtId = $("#testContainer").on("change", function() { 307 | assert.equal("This handler shouldn't execute", true, false); 308 | }); 309 | $("#testContainer").on("change", function() { 310 | assert.equal("This handler should execute", true, true); 311 | }); 312 | $("#testContainer").off(evtId); 313 | $("#testContainer").trigger("change"); 314 | }); 315 | 316 | 317 | //trigger 318 | addTest("trigger", "Send event", function(assert) { 319 | var eventTriggers = 0; 320 | $("#testContainer tr").on("input", function() { 321 | eventTriggers++; 322 | }); 323 | $("#testContainer tr").trigger("input"); 324 | setTimeout(function() { 325 | assert.greaterThan("Received Events", eventTriggers, 1); 326 | }, 1); 327 | }); 328 | addTest("trigger", "Triggers native Event handlers", function(assert) { 329 | var eventTriggers = 0; 330 | document.querySelector("#testContainer tr").addEventListener("abc", function() { 331 | eventTriggers++; 332 | }); 333 | $("#testContainer tr").trigger("abc"); 334 | setTimeout(function() { 335 | assert.equal("Received Events", eventTriggers, 1); 336 | }, 1); 337 | }); 338 | addTest("trigger", "Send custom event data", function(assert) { 339 | var customData = { 340 | id: 10, 341 | str: "Hello", 342 | arr: [1, 5, "hello"], 343 | obj: { 344 | person: { 345 | age: 10 346 | } 347 | } 348 | }; 349 | $("#testContainer").on("input", function(evt) { 350 | assert.equal("Custom number data", customData.id, evt.id); 351 | assert.equal("Custom string data", customData.str, evt.str); 352 | assert.equal("Custom array data", customData.arr, evt.arr); 353 | assert.equal("Custom object data", customData.obj, evt.obj); 354 | }); 355 | $("#testContainer").trigger("input", customData); 356 | }); 357 | 358 | 359 | //hide 360 | addTest("hide", "Sets display to none", function(assert) { 361 | $("#testContainer tr").hide(); 362 | $("#testContainer tr").each(function() { 363 | assert.equal("Display is none", this.get(0).style.display, "none"); 364 | }); 365 | }); 366 | addTest("hide", "Sets node attributes", function(assert) { 367 | $("#testContainer tr").css("display", "inline-block").hide(); 368 | $("#testContainer tr").each(function() { 369 | assert.equal("Previous style attribute", this.attr("data-LDOM-hidden-previous-display"), "inline-block"); 370 | }); 371 | }); 372 | addTest("hide", "Sets important", function(assert) { 373 | $("#testContainer tr").css("display", "inline-block", true).hide(); 374 | $("#testContainer tr").each(function() { 375 | assert.equal("Display is none", this.get(0).style.display, "none"); 376 | assert.equal("Display important", this.get(0).style.getPropertyPriority("display"), "important"); 377 | }); 378 | }); 379 | 380 | 381 | //show 382 | addTest("show", "Sets display to emptystring", function(assert) { 383 | $("#testContainer tr").css("display", "none").show(); 384 | $("#testContainer tr").each(function() { 385 | assert.equal("Element is visible", this.get(0).style.display, ""); 386 | }); 387 | }); 388 | addTest("show", "Sets display back to previous", function(assert) { 389 | $("#testContainer tr").css("display", "inline-block").hide().show(); 390 | $("#testContainer tr").each(function() { 391 | assert.equal("Element style is reverted", this.get(0).style.display, "inline-block"); 392 | }); 393 | }); 394 | addTest("show", "Sets important", function(assert) { 395 | $("#testContainer tr").css("display", "inline-block", true).hide().show(); 396 | $("#testContainer tr").each(function() { 397 | assert.equal("Display is reverted", this.get(0).style.display, "inline-block"); 398 | assert.equal("Display important", this.get(0).style.getPropertyPriority("display"), "important"); 399 | }); 400 | }); 401 | 402 | 403 | //toggle 404 | addTest("toggle", "Alternates show/hide - show", function(assert) { 405 | $("#testContainer tr").css("display", "none").toggle(); 406 | $("#testContainer tr").each(function() { 407 | assert.equal("Element is visible", this.get(0).style.display, ""); 408 | }); 409 | }); 410 | addTest("toggle", "Alternates show/hide - hide", function(assert) { 411 | $("#testContainer tr").css("display", "inline-block").toggle(); 412 | $("#testContainer tr").each(function() { 413 | assert.equal("Element is hidden", this.get(0).style.display, "none"); 414 | }); 415 | }); 416 | addTest("toggle", "Show - true", function(assert) { 417 | $("#testContainer tr").css("display", "inline-block").toggle(true); 418 | $("#testContainer tr").each(function() { 419 | assert.equal("Element is still shown", this.get(0).style.display, "inline-block"); 420 | }); 421 | }); 422 | addTest("toggle", "Show - false", function(assert) { 423 | $("#testContainer tr").css("display", "inline-block").toggle(false); 424 | $("#testContainer tr").each(function() { 425 | assert.equal("Element is hidden", this.get(0).style.display, "none"); 426 | }); 427 | }); 428 | addTest("toggle", "Show - false - already hidden", function(assert) { 429 | $("#testContainer tr").hide().toggle(false); 430 | $("#testContainer tr").each(function() { 431 | assert.equal("Element is still hidden", this.get(0).style.display, "none"); 432 | }); 433 | }); 434 | 435 | 436 | //css 437 | addTest("css", "Gets computed style", function(assert) { 438 | assert.equal("Font-size is correct", $("#testContainer td").css("font-size"), "15px"); 439 | }); 440 | addTest("css", "Gets computed style of first element", function(assert) { 441 | $("#testContainer td").eq(1).css("font-size", "20px"); 442 | assert.equal("First elem font-size is correct", $("#testContainer td").css("font-size"), "15px"); 443 | }); 444 | addTest("css", "Set style", function(assert) { 445 | $("#testContainer td").css("font-size", "19px"); 446 | $("#testContainer td").each(function() { 447 | assert.equal("Font-size is correct", this.css("font-size"), "19px"); 448 | }); 449 | }); 450 | addTest("css", "Set style important", function(assert) { 451 | $("#testContainer td").css("font-size", "19px", true); 452 | $("#testContainer td").each(function() { 453 | assert.equal("Font-size is correct", this.css("font-size"), "19px"); 454 | assert.equal("Set important", this.get(0).style.getPropertyPriority("font-size"), "important"); 455 | }); 456 | }); 457 | addTest("css", "Set style important - false", function(assert) { 458 | $("#testContainer td").css("font-size", "19px", false); 459 | $("#testContainer td").each(function() { 460 | assert.equal("Font-size is correct", this.css("font-size"), "19px"); 461 | assert.equal("Not important", this.get(0).style.getPropertyPriority("font-size"), ""); 462 | }); 463 | }); 464 | 465 | 466 | //html 467 | addTest("html", "Get innerHTML of first element", function(assert) { 468 | assert.equal("innerHTML matches", $("#testContainer div").html(), document.querySelector("#testContainer div").innerHTML); 469 | }); 470 | addTest("html", "Sets innerHTML", function(assert) { 471 | $("#testContainer div").html("123"); 472 | assert.equal("innerHTML matches", $("#testContainer div").html(), "123"); 473 | }); 474 | addTest("html", "Remove elements which have become detatched from DOM", function(assert) { 475 | var remainingElements = $("#testContainer div").html("123"); 476 | assert.equal("Elements removed", remainingElements.length, document.querySelectorAll("#testContainer div").length); 477 | }); 478 | 479 | 480 | //outerHTML 481 | addTest("outerHTML", "Get outerHTML of first element", function(assert) { 482 | assert.equal("outerHTML matches", $("#testContainer div").outerHTML(), document.querySelector("#testContainer div").outerHTML); 483 | }); 484 | addTest("outerHTML", "Sets outerHTML", function(assert) { 485 | $("#testContainer div").eq(0).outerHTML("123"); 486 | assert.equal("Element tag changed", $("#testContainer test").length, 1); 487 | }); 488 | addTest("outerHTML", "Remove elements which have become detatched from DOM", function(assert) { 489 | var remainingElements = $("#testContainer div").outerHTML("123"); 490 | assert.equal("Elements removed", remainingElements.length, document.querySelectorAll("#testContainer div").length); 491 | }); 492 | 493 | 494 | //text 495 | addTest("text", "Get innerText of first element", function(assert) { 496 | assert.equal("innerText matches", $("#testContainer div").text(), document.querySelector("#testContainer div").innerText); 497 | }); 498 | addTest("text", "Sets innerText", function(assert) { 499 | $("#testContainer div").text("Happy"); 500 | assert.equal("innerText matches", $("#testContainer div").text(), "Happy"); 501 | }); 502 | addTest("text", "Remove elements which have become detatched from DOM", function(assert) { 503 | var remainingElements = $("#testContainer div").text("Happy"); 504 | assert.equal("Elements removed", remainingElements.length, document.querySelectorAll("#testContainer div").length); 505 | }); 506 | 507 | 508 | //prop 509 | addTest("prop", "Get prop", function(assert) { 510 | assert.equal("Matches", $("#testContainer table").prop("nodeName"), document.querySelector("#testContainer table").nodeName); 511 | }); 512 | addTest("prop", "Set prop", function(assert) { 513 | $("#testContainer table").prop("hidden", true); 514 | assert.equal("Matches", document.querySelector("#testContainer table").hidden, true); 515 | }); 516 | 517 | 518 | //attr 519 | addTest("attr", "Get attr", function(assert) { 520 | assert.equal("Matches", $("#testContainer").attr("id"), document.querySelector("#testContainer").id); 521 | }); 522 | addTest("attr", "Set attr", function(assert) { 523 | $("#testContainer table").attr("id", "aTable"); 524 | assert.equal("Matches", document.querySelector("#testContainer table").id, "aTable"); 525 | }); 526 | 527 | 528 | //removeAttr 529 | addTest("removeAttr", "Attribute removed", function(assert) { 530 | $("#tableID").removeAttr("id"); 531 | assert.equal("Removed", $("#tableID").length, 0); 532 | }); 533 | 534 | 535 | //addClass 536 | addTest("addClass", "Add new class from nothing", function(assert) { 537 | $("#testContainer td").addClass("row-class-name"); 538 | $("#testContainer td").each(function() { 539 | assert.equal("Class is set", this.attr("class"), "row-class-name"); 540 | }); 541 | }); 542 | addTest("addClass", "Add additional class", function(assert) { 543 | $("#testContainer td").addClass("row-class-name").addClass("class-2"); 544 | $("#testContainer td").each(function() { 545 | assert.equal("Class is set", this.attr("class"), "row-class-name class-2"); 546 | }); 547 | }); 548 | addTest("addClass", "Add duplicate class", function(assert) { 549 | $("#testContainer td").addClass("row-class-name").addClass("row-class-name"); 550 | $("#testContainer td").each(function() { 551 | assert.equal("Class is set", this.attr("class"), "row-class-name"); 552 | }); 553 | }); 554 | addTest("addClass", "Add multiple classes", function(assert) { 555 | $("#testContainer td").addClass("row-class-name class-2"); 556 | $("#testContainer td").each(function() { 557 | assert.equal("Class is set", this.attr("class"), "row-class-name class-2"); 558 | }); 559 | }); 560 | 561 | 562 | //removeClass 563 | addTest("removeClass", "Remove all classes", function(assert) { 564 | $("#testContainer td").addClass("row-class-name class-2 class-3"); 565 | $("#testContainer td").removeClass(); 566 | $("#testContainer td").each(function() { 567 | assert.equal("Class is removed", this.prop("className"), ""); 568 | }); 569 | }); 570 | addTest("removeClass", "Remove class that exists", function(assert) { 571 | $("#testContainer td").addClass("row-class-name class-2 class-3"); 572 | $("#testContainer td").removeClass("class-2"); 573 | $("#testContainer td").each(function() { 574 | assert.equal("Class is removed", this.prop("className"), "row-class-name class-3"); 575 | }); 576 | }); 577 | addTest("removeClass", "Remove class that doesn't exist", function(assert) { 578 | $("#testContainer td").addClass("row-class-name class-2 class-3"); 579 | $("#testContainer td").removeClass("class-4"); 580 | $("#testContainer td").each(function() { 581 | assert.equal("Class is not removed", this.prop("className"), "row-class-name class-2 class-3"); 582 | }); 583 | }); 584 | addTest("removeClass", "Remove multiple classes", function(assert) { 585 | $("#testContainer td").addClass("row-class-name class-2 class-3"); 586 | $("#testContainer td").removeClass("row-class-name class-3"); 587 | $("#testContainer td").each(function() { 588 | assert.equal("Classes are removed", this.prop("className"), "class-2"); 589 | }); 590 | }); 591 | 592 | 593 | //hasClass 594 | addTest("hasClass", "Does any element have class", function(assert) { 595 | $("#testContainer td").addClass("row-class-name class-2 class-3"); 596 | assert.equal("Has class", $("#testContainer td").hasClass("row-class-name"), true); 597 | }); 598 | addTest("hasClass", "Does any element have class - none", function(assert) { 599 | $("#testContainer td").addClass("row-class-name class-2 class-3"); 600 | assert.equal("Does not have class", $("#testContainer td").hasClass("not-a-class"), false); 601 | }); 602 | addTest("hasClass", "Do all elements have class", function(assert) { 603 | $("#testContainer td").eq(0).addClass("class-1 class-random"); 604 | $("#testContainer td").eq(1).addClass("class-1 class-abc"); 605 | assert.equal("Has class", $("#testContainer td").hasClass("class-1", true), true); 606 | }); 607 | addTest("hasClass", "Do all elements not have class", function(assert) { 608 | $("#testContainer td").eq(0).addClass("class-1 class-random"); 609 | $("#testContainer td").eq(1).addClass("class-1 class-abc"); 610 | assert.equal("Has class", $("#testContainer td").hasClass("class-abc", true), false); 611 | }); 612 | 613 | 614 | //parent 615 | addTest("parent", "Get all parents", function(assert) { 616 | assert.equal("Parent count correct", $("#testContainer .abc").parent().length, $("#testContainer .abc").length); 617 | }); 618 | addTest("parent", "Get all parents - removing duplicates", function(assert) { 619 | assert.equal("Parent count correct", $("#testContainer div").parent().length, 2); 620 | }); 621 | 622 | 623 | //children 624 | addTest("children", "Get all children", function(assert) { 625 | assert.equal("Child count correct", $("#testContainer tr").children().length, $("#testContainer th, #testContainer td").length); 626 | }); 627 | 628 | 629 | //filter 630 | addTest("filter", "Remove elements that don't match", function(assert) { 631 | assert.equal("Filtered count", $("#testContainer *").filter("div").length, $("#testContainer div").length); 632 | }); 633 | addTest("filter", "No selector", function(assert) { 634 | assert.equal("Filtered count", $("#testContainer *").filter("").length, 0); 635 | }); 636 | 637 | 638 | //first 639 | addTest("first", "Gets first element", function(assert) { 640 | assert.equal("First element", $("#testContainer div").first().get(0), document.querySelector("#testContainer div")); 641 | }); 642 | addTest("first", "Gets null when empty list", function(assert) { 643 | assert.equal("First element null", $("#testContainer .none").first().get(0), null); 644 | }); 645 | 646 | 647 | //last 648 | addTest("last", "Gets last element", function(assert) { 649 | assert.equal("Last element", $("#testContainer tr").last().get(0), document.querySelectorAll("#testContainer tr")[1]); 650 | }); 651 | addTest("last", "Gets null when empty list", function(assert) { 652 | assert.equal("Last element null", $("#testContainer .none").last().get(0), null); 653 | }); 654 | 655 | 656 | //eq 657 | addTest("eq", "Get positive index", function(assert) { 658 | assert.equal("Elements equal", $("#testContainer *").eq(3).get(0), document.querySelectorAll("#testContainer *")[3]); 659 | }); 660 | addTest("eq", "Get negative index", function(assert) { 661 | var elems = document.querySelectorAll("#testContainer *"); 662 | assert.equal("Elements equal", $("#testContainer *").eq(-2).get(0), elems[elems.length - 2]); 663 | }); 664 | addTest("eq", "Get index out of bounds", function(assert) { 665 | assert.equal("Negative index - Element empty", $("#testContainer *").eq(-100).get(0), null); 666 | assert.equal("Positive index - Element null", $("#testContainer *").eq(100).get(0), null); 667 | }); 668 | 669 | 670 | //insertAfter 671 | addTest("insertAfter", "Insert elements", function(assert) { 672 | var sourceElems = $("#testContainer td").insertAfter($("#testContainer th")); 673 | assert.equal("Elements copied", $("#testContainer td").length, 6); 674 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(1).text(), sourceElems.eq(0).text()); 675 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(2).text(), sourceElems.eq(1).text()); 676 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(4).text(), sourceElems.eq(0).text()); 677 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(5).text(), sourceElems.eq(1).text()); 678 | }); 679 | 680 | 681 | //insertBefore 682 | addTest("insertBefore", "Insert elements", function(assert) { 683 | var sourceElems = $("#testContainer td").insertBefore($("#testContainer th")); 684 | assert.equal("Elements copied", $("#testContainer td").length, 6); 685 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(0).text(), sourceElems.eq(0).text()); 686 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(1).text(), sourceElems.eq(1).text()); 687 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(3).text(), sourceElems.eq(0).text()); 688 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(4).text(), sourceElems.eq(1).text()); 689 | }); 690 | 691 | 692 | //appendChild 693 | addTest("appendChild", "Insert elements", function(assert) { 694 | var sourceElems = $("#testContainer td"); 695 | $("#testContainer tr").appendChild(sourceElems); 696 | assert.equal("Elements copied", $("#testContainer td").length, 6); 697 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(2).text(), sourceElems.eq(0).text()); 698 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(3).text(), sourceElems.eq(1).text()); 699 | assert.equal("Elements in correct order", $("#testContainer tr").last().children().eq(2).text(), sourceElems.eq(0).text()); 700 | assert.equal("Elements in correct order", $("#testContainer tr").last().children().eq(3).text(), sourceElems.eq(1).text()); 701 | }); 702 | 703 | 704 | //prependChild 705 | addTest("prependChild", "Insert elements", function(assert) { 706 | var sourceElems = $("#testContainer td"); 707 | $("#testContainer tr").prependChild(sourceElems); 708 | assert.equal("Elements copied", $("#testContainer td").length, 6); 709 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(0).text(), sourceElems.eq(0).text()); 710 | assert.equal("Elements in correct order", $("#testContainer tr").first().children().eq(1).text(), sourceElems.eq(1).text()); 711 | assert.equal("Elements in correct order", $("#testContainer tr").last().children().eq(0).text(), sourceElems.eq(0).text()); 712 | assert.equal("Elements in correct order", $("#testContainer tr").last().children().eq(1).text(), sourceElems.eq(1).text()); 713 | }); 714 | 715 | 716 | //remove 717 | addTest("remove", "Remove elements", function(assert) { 718 | $("#testContainer tr").remove(); 719 | assert.equal("Elements removed", $("#testContainer tr").length, 0); 720 | }); 721 | addTest("remove", "Remove hierarchy of elements", function(assert) { 722 | $("#testContainer div").remove(); 723 | assert.equal("Elements removed", $("#testContainer div").length, 0); 724 | }); 725 | addTest("remove", "Remove event listeners", function(assert) { 726 | var elems = $("#testContainer span"); 727 | elems.on("click", function() { 728 | assert.equal("This handler shouldn't execute", true, false); 729 | }); 730 | elems.remove(); 731 | elems.trigger("click"); 732 | assert.equal("Event removed", typeof elems.get(0)._LDOMEvents, "undefined"); 733 | }); 734 | 735 | 736 | 737 | // ------------------------------ End Tests ------------------------------ 738 | 739 | for (var i = 0; i < methods.length; i++) { 740 | var method = methods[i]; 741 | $("#function-" + method).find("summary").appendChild( 742 | $("").addClass("test-stats").appendChild( 743 | $("").text("-").attr("id", "function-successCount-" + method) 744 | ).appendChild( 745 | $("").text(" / ") 746 | ).appendChild( 747 | $("").text("-").attr("id", "function-totalCount-" + method) 748 | ) 749 | ); 750 | } 751 | 752 | var testQueue = []; 753 | var totalSuccess = 0; 754 | var totalCount = 0; 755 | var methodSuccesses = {}; 756 | var methodAssertions = {}; 757 | for (var i = 0; i < methods.length; i++) { 758 | var method = methods[i]; 759 | methodSuccesses[method] = 0; 760 | methodAssertions[method] = 0; 761 | for (var j = 0; j < tests[method].length; j++) { 762 | testQueue.push({ 763 | method: method, 764 | testData: tests[method][j] 765 | }); 766 | } 767 | } 768 | runTests(); 769 | function runTests() { 770 | if (testQueue.length === 0) { 771 | if (totalCount === totalSuccess) { 772 | $("#aggregate-totalCount").parent().addClass("test-success"); 773 | } else { 774 | $("#aggregate-totalCount").parent().addClass("test-fail"); 775 | } 776 | return; 777 | } 778 | var test = testQueue.shift(); 779 | var method = test.method; 780 | test = test.testData; 781 | test.run(function(testCaseName) { 782 | methodAssertions[method]++; 783 | methodSuccesses[method]++; 784 | totalSuccess++; 785 | totalCount++; 786 | $("#function-totalCount-" + method).text(methodAssertions[method]); 787 | $("#function-successCount-" + method).text(methodSuccesses[method]); 788 | if (methodSuccesses[method] === methodAssertions[method]) { 789 | $("#function-successCount-" + method).parent().addClass("test-success"); 790 | } 791 | $("#function-" + method).appendChild($("
").text(test.testName + testCaseName).addClass("test-success").addClass("test-case")); 792 | 793 | $("#aggregate-totalCount").text(totalCount); 794 | $("#aggregate-successCount").text(totalSuccess); 795 | }, function(e) { 796 | methodAssertions[method]++; 797 | totalCount++; 798 | console.error(e); 799 | $("#function-" + method).appendChild($("
").text(test.testName + e).removeClass().addClass("test-fail").addClass("test-case")); 800 | $("#function-successCount-" + method).text(methodSuccesses[method]).parent().removeClass().addClass("test-fail"); 801 | $("#function-totalCount-" + method).text(methodAssertions[method]); 802 | $("#function-" + method).attr("open", ""); 803 | 804 | $("#aggregate-totalCount").text(totalCount); 805 | }); 806 | setTimeout(runTests, 5); 807 | } --------------------------------------------------------------------------------