├── plugin.json ├── license.txt ├── af.range.css ├── README.md ├── index.html ├── af.range.js └── appframework.js /plugin.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ranoge", 3 | "title": "Range", 4 | "version": "2.1", 5 | "author": "Intel" 6 | "license": "MIT License", 7 | "description": "A range polyfill for App Framework apps.", 8 | "categories": [ 9 | 10 | ], 11 | "homepage": "https://github.com/01org/af-range", 12 | "githubLink": "https://github.com/01org/af-range" 13 | } -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | MIT X11 License 2 | Copyright (C) <2011> by 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in 12 | all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | THE SOFTWARE. -------------------------------------------------------------------------------- /af.range.css: -------------------------------------------------------------------------------- 1 | .slider { 2 | position: relative; 3 | width: 100%; 4 | height: 50px; 5 | } 6 | 7 | .range,.rangefill { 8 | position: absolute; 9 | top: 10px; 10 | left: 0; 11 | right: 0; 12 | height: 3px; 13 | background-color: #ccc; 14 | border-radius: 10px; 15 | z-index: -1; 16 | border-radius:5px; 17 | border:1px solid #666; 18 | } 19 | 20 | .rangefill{ 21 | width:0px; 22 | background-color:#5393C5; 23 | } 24 | 25 | .rangeBubble{ 26 | position:absolute;top:-25px; 27 | width:20px; 28 | height:20px; 29 | border-radius:15px; 30 | text-align:center; 31 | margin-auto; 32 | border:1px solid #5393C5; 33 | background:#fff; 34 | font-size:.5em; 35 | line-height:20px; 36 | } 37 | 38 | .pointer { 39 | position: absolute; 40 | margin: 0; 41 | padding: 0; 42 | height: 20px; 43 | width: 20px; 44 | background-color: black; 45 | border-radius: 10px; 46 | 47 | /* Webkit (Safari/Chrome 10) */ 48 | background-image: -webkit-gradient(linear, left top, right bottom, color-stop(0, #FDFDFD), color-stop(1, #BEBEBE)); 49 | 50 | /* Webkit (Chrome 11+) */ 51 | background-image: -webkit-linear-gradient(top left, #FDFDFD 0%, #BEBEBE 100%); 52 | 53 | /* W3C Markup, IE10 Release Preview */ 54 | background-image: linear-gradient(to bottom right, #FDFDFD 0%, #BEBEBE 100%); 55 | border: 1px solid #A0A0A0; 56 | cursor: pointer; 57 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # range slider plugin for App Framework apps 2 | 3 | #copyright Intel 4 | 5 | This plugin is similar to the HTML5 range input, but works great on mobile devices 6 | 7 | # Using the range slider plugin 8 | 9 | 1) Call $(container).range({opts}) 10 | 11 | 12 | 13 | ```html 14 |
15 | ``` 16 | 17 | ```js 18 | $("#slider2").range({value:15}); 19 | ``` 20 | 21 | There are different configuration options you can pass in. 22 | 23 | ```js 24 | min:1, //Minimum value for the range slider 25 | max:100, //Maximum value for the range slider 26 | value:0, //initial starting value 27 | rangeClass:"range", //CSS class for range div 28 | pointerClass:"pointer", //CSS class for the bubble/circle on the range 29 | sliderClass:"slider",//CSS class for the slider in general 30 | rangeFillClass:"rangefill", //CSS class for the range as it gets filled 31 | bubbleClass:"rangeBubble", //CSS class for the bubble that appears on top with the value in it 32 | stepFunc:function(){},//Function that gets executed every step when the range gets moved 33 | ``` 34 | 35 | 36 | # Bugs 37 | 38 | Please use github to report any bugs found. Please provide the following 39 | 40 | 1. Any error messages from the console 41 | 42 | 2. Line numbers of offending code 43 | 44 | 3. Test cases 45 | 46 | 4. Description of the Error 47 | 48 | 5. Expected result 49 | 50 | 6. Browser/Device you are testing on 51 | 52 | 53 | # License 54 | 55 | Licensed under the terms of the MIT License, see the included license.txt file. -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mobile Range Slider 6 | 7 | 8 | 9 | 17 | 18 | 19 |
20 |
21 |
22 |
23 |
24 |
25 | 26 | 27 | 28 |
29 | 30 |
31 | 32 | 33 | 34 | 35 | 36 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /af.range.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @author Ian Maffett, Konstantin Likhter 3 | * @copyright Intel 2013 4 | * @desc - Range slider for jqMobi apps 5 | */ 6 | 7 | 8 | /** 9 | * 10 | af.range.js replicates the HTML5 range input for mobile apps. 11 | This is based on https://github.com/ubilabs/mobile-range-slider 12 | but adapted for App Framework 13 | ``` 14 | $("#slider1").range({min:1,max:20,val:10,stepFunc(val){}}); 15 | $("#slider2").interval({min:1, max: 20, value: { 16 | left: 5, 17 | right: 15 18 | }, stepFunc(val) {} }); 19 | ``` 20 | *@param {Object} [options] 21 | *@title $().range([options]); 22 | *@title $().interval([options]); 23 | */ 24 | (function($) { 25 | 26 | var rangeCache = {}; 27 | window.rangeCache = rangeCache; 28 | /** 29 | * This creates a range object or retrieves it from the cache 30 | */ 31 | $.fn.range = function(opts) { 32 | if (this.length === 0) return; 33 | if (opts === undefined) 34 | return rangeCache[this[0].rangeID]; 35 | for (var i = 0; i < this.length; i++) { 36 | //Assign a jqid for the cache in case they don't have an id on the elements 37 | if (!this[i].rangeID) 38 | this[i].rangeID = $.uuid(); 39 | rangeCache[this[i].rangeID] = new range(this[i], opts); 40 | } 41 | }; 42 | 43 | var range = function(elem, opts) { 44 | var that = this; 45 | this.elem = elem; 46 | $(elem).bind('destroy', function() { 47 | var id = that.elem.rangeID; 48 | if (rangeCache[id]) delete rangeCache[id]; 49 | }); 50 | 51 | for (var j in opts) { 52 | this[j] = opts[j]; 53 | } 54 | 55 | if (!this.pointer) { 56 | this.pointer = $("
").get(); //round pointer we drag 57 | this.elem.appendChild(this.pointer); 58 | } 59 | if (!this.range) { 60 | this.range = $("
").get(); //range that we drag on 61 | this.elem.appendChild(this.range); 62 | } 63 | if (!this.rangeFill) { 64 | this.rangeFill = $("
").get(); //range fill to the left 65 | this.elem.appendChild(this.rangeFill); 66 | } 67 | if (!this.bubble) { 68 | this.bubble = $("
").get(); //bubble above showing the value 69 | this.elem.appendChild(this.bubble); 70 | } 71 | 72 | this.pointer.style.webkitTransitionDuration = "0ms"; 73 | if (this.elem.style.position === "static") 74 | this.elem.style.position = "relative"; 75 | 76 | 77 | if (opts['value']) 78 | this.val(opts['value']); 79 | 80 | this.elem.addEventListener("touchstart", this); 81 | //this.pointer.addEventListener("touchstart", this); 82 | 83 | this.elem._afRange = true; 84 | 85 | return this; 86 | }; 87 | 88 | range.prototype = { 89 | min: 1, 90 | max: 100, 91 | value: 0, 92 | rangeClass: "range", 93 | pointerClass: "pointer", 94 | sliderClass: "slider", 95 | rangeFillClass: "rangefill", 96 | bubbleClass: "rangeBubble", 97 | stepFunc: function() {}, 98 | handleEvent: function(evt) { 99 | if (evt.target != this.pointer && this.pointer != this.elem._afLastDragged) { 100 | return; 101 | } 102 | if (evt.target == this.pointer) { 103 | this.elem._afLastDragged = this.pointer; 104 | } 105 | switch (evt.type) { 106 | case "touchstart": 107 | this.onTouchStart(evt); 108 | break; 109 | case "touchmove": 110 | this.onTouchMove(evt); 111 | break; 112 | case "touchend": 113 | this.onTouchEnd(evt); 114 | break; 115 | } 116 | }, 117 | onTouchStart: function(event) { 118 | var that = this; 119 | this.elem.addEventListener("touchmove", this); 120 | this.elem.addEventListener("touchend", this); 121 | 122 | }, 123 | onTouchMove: function(event) { 124 | event.preventDefault(); 125 | 126 | var position = event.touches[0].pageX, 127 | element, 128 | pointerW = this.pointer.offsetWidth, 129 | rangeW = this.range.offsetWidth, 130 | width = rangeW - pointerW, 131 | range = this.max - this.min, 132 | value; 133 | 134 | position -= $(this.elem).offset().left; 135 | 136 | position += pointerW / 2; 137 | position = Math.min(position, rangeW); 138 | position = Math.max(position - pointerW, 0); 139 | $(this.bubble).cssTranslate(position + "px,0"); 140 | $(this.pointer).cssTranslate(position + "px,0"); 141 | 142 | // update 143 | value = this.min + Math.round(position * range / width); 144 | this.val(value, position); 145 | }, 146 | onTouchEnd: function(event) { 147 | 148 | this.elem.removeEventListener("touchmove", this, true); 149 | this.elem.removeEventListener("touchend", this, true); 150 | }, 151 | val: function(val, position) { 152 | if (val === undefined) 153 | return this.value; 154 | 155 | val = Math.min(val, this.max); 156 | val = Math.max(val, this.min); 157 | if (position === undefined) { 158 | var position = this.getPosition(val); 159 | $(this.bubble).cssTranslate(position + "px,0"); 160 | $(this.pointer).cssTranslate(position + "px,0"); 161 | 162 | 163 | } 164 | this.rangeFill.style.width = position + "px"; 165 | 166 | this.bubble.innerHTML = val; 167 | this.value = val; 168 | this.stepFunc(val); 169 | }, 170 | 171 | getPosition: function(val) { 172 | var 173 | value = val || this.value, 174 | pointerW = this.pointer.offsetWidth, 175 | rangeW = this.range.offsetWidth, 176 | range = this.max - this.min, 177 | width = rangeW - pointerW, 178 | position = Math.round((value - this.min) * width / range, 10); 179 | 180 | return position; 181 | } 182 | }; 183 | 184 | /** @constructor */ 185 | var intervalSelector = function() { 186 | intervalSelector.super.apply(this, arguments); 187 | return this; 188 | } 189 | 190 | // extend intervalSelector from range 191 | !function() { 192 | var f = function() {}; 193 | f.prototype = range.prototype; 194 | var F = new f(); 195 | intervalSelector.prototype = F; 196 | intervalSelector.prototype.constructor = intervalSelector; 197 | intervalSelector.super = range; 198 | }(); 199 | 200 | intervalSelector.prototype.val = function(val, position, dontFire) { 201 | if (val === undefined) 202 | return this.value; 203 | 204 | val = Math.min(val, this.max); 205 | val = Math.max(val, this.min); 206 | if (position === undefined) { 207 | var position = this.getPosition(val); 208 | 209 | $(this.bubble).cssTranslate(position + "px,0"); 210 | $(this.pointer).cssTranslate(position + "px,0"); 211 | 212 | } 213 | //this.rangeFill.style.width = position + "px"; 214 | 215 | 216 | this.bubble.innerHTML = val; 217 | this.value = val; 218 | 219 | //this.stepFunc(val); 220 | if (!dontFire) { 221 | this.stepFunc(position, val); 222 | } 223 | }; 224 | 225 | /** @constructor */ 226 | var interval = function(el, opts) { 227 | var that = this; 228 | 229 | this.stepFunc = opts.stepFunc || function() { }; 230 | delete opts.stepFunc; 231 | 232 | // configs for first and second intervalSelectors 233 | var cfg = {}, firstCfg = {}, secondCfg = {}; 234 | $.extend(cfg, this.defaultRangeConfig, opts); 235 | $.extend(firstCfg, cfg, { 236 | value: opts.value.left, 237 | stepFunc: this.rangeStepFunc.bind(this) 238 | }); 239 | $.extend(secondCfg, cfg, { 240 | value: opts.value.right, 241 | stepFunc: this.rangeStepFunc.bind(this) 242 | }); 243 | 244 | // create first range 245 | this.first = new intervalSelector(el, firstCfg); 246 | // using the same range and rangeFill objects 247 | $.extend(secondCfg, { 248 | range: this.first.range, 249 | rangeFill: this.first.rangeFill 250 | }); 251 | // create second range 252 | this.second = new intervalSelector(el, secondCfg); 253 | 254 | // set rangeFill position 255 | var firstPos = this.first.getPosition(), 256 | secondPos = this.second.getPosition(); 257 | 258 | this.first.rangeFill.style.left = firstPos + 'px'; 259 | this.first.rangeFill.style.width = (secondPos - firstPos) + 'px'; 260 | }; 261 | 262 | interval.prototype = { 263 | 264 | defaultRangeConfig: { 265 | min: 1, 266 | max: 100, 267 | value: 0, 268 | rangeClass: "range", 269 | pointerClass: "pointer", 270 | sliderClass: "slider", 271 | rangeFillClass: "rangefill", 272 | bubbleClass: "rangeBubble" 273 | }, 274 | 275 | stepFunc: function() { }, 276 | 277 | rangeStepFunc: function(pos, val) { 278 | if (!this.first || !this.second) { 279 | return; 280 | } 281 | 282 | var firstPos = this.first.getPosition(), 283 | secondPos = this.second.getPosition(), 284 | isFirstLeft = firstPos < secondPos, 285 | leftPos = isFirstLeft ? firstPos : secondPos, 286 | rightPos = isFirstLeft ? secondPos : firstPos; 287 | 288 | this.first.rangeFill.style.left = leftPos + 'px'; 289 | this.first.rangeFill.style.width = (rightPos - leftPos) + 'px'; 290 | 291 | this.stepFunc(this.val()); 292 | }, 293 | 294 | val: function(val) { 295 | if (val === undefined) { 296 | return { 297 | left: Math.min(this.first.val(), this.second.val()), 298 | right: Math.max(this.first.val(), this.second.val()) 299 | }; 300 | } else { 301 | if (typeof val.left !== 'number' || typeof val.right !== 'number') { 302 | throw new TypeError('interval.set incorret value passed'); 303 | } 304 | // first one should not fire the stepFunc event 305 | this.first.val(val.left, undefined, true); 306 | this.second.val(val.right); 307 | } 308 | } 309 | }; 310 | 311 | $.fn.interval = function(opts) { 312 | if (this.length === 0) return; 313 | if (opts === undefined) 314 | return rangeCache[this[0].rangeID]; 315 | for (var i = 0; i < this.length; i++) { 316 | //Assign a jqid for the cache in case they don't have an id on the elements 317 | if (!this[i].rangeID) 318 | this[i].rangeID = $.uuid(); 319 | 320 | rangeCache[this[i].rangeID] = new interval(this[i], opts); 321 | } 322 | return this; 323 | }; 324 | 325 | })(jq); 326 | -------------------------------------------------------------------------------- /appframework.js: -------------------------------------------------------------------------------- 1 | /** 2 | * App Framwork query selector class for HTML5 mobile apps on a WebkitBrowser. 3 | * Since most mobile devices (Android, iOS, webOS) use a WebKit browser, you only need to target one browser. 4 | * We are able to increase the speed greatly by removing support for legacy desktop browsers and taking advantage of browser features, like native JSON parsing and querySelectorAll 5 | 6 | 7 | * MIT License 8 | * @author AppMobi 9 | * @copyright Intel 10 | * @api private 11 | */ 12 | if (!window.af || typeof(af) !== "function") { 13 | /** 14 | * This is our master af object that everything is built upon. 15 | * $ is a pointer to this object 16 | * @title appframework 17 | * @api private 18 | */ 19 | var af = (function(window) { 20 | "use strict"; 21 | var nundefined, document = window.document, 22 | emptyArray = [], 23 | slice = emptyArray.slice, 24 | classCache = {}, 25 | eventHandlers = [], 26 | _eventID = 1, 27 | jsonPHandlers = [], 28 | _jsonPID = 1, 29 | fragementRE = /^\s*<(\w+)[^>]*>/, 30 | classSelectorRE = /^\.([\w-]+)$/, 31 | tagSelectorRE = /^[\w-]+$/, 32 | _attrCache = {}, 33 | _propCache = {}, 34 | cssNumber = { 35 | "columncount": true, 36 | "fontweight": true, 37 | "lineheight": true, 38 | "column-count": true, 39 | "font-weight": true, 40 | "line-height": true, 41 | "opacity": true, 42 | "orphans": true, 43 | "widows": true, 44 | "zIndex": true, 45 | "z-index": true, 46 | "zoom": true 47 | }, 48 | isWin8 = (typeof(MSApp) === "object"); 49 | 50 | /** 51 | * internal function used for $().css - checks to see if it's a number and the css property 52 | * needs "px" added to it 53 | * @api private 54 | */ 55 | 56 | function _addPx(prop, val) { 57 | return (typeof(val) === "number") && !cssNumber[prop.toLowerCase()] ? val + "px" : val; 58 | } 59 | 60 | /** 61 | * internal function to use domfragments for insertion 62 | * 63 | * @api private 64 | */ 65 | 66 | function _insertFragments(afm, container, insert) { 67 | var frag = document.createDocumentFragment(); 68 | if (insert) { 69 | for (var j = afm.length - 1; j >= 0; j--) { 70 | frag.insertBefore(afm[j], frag.firstChild); 71 | } 72 | container.insertBefore(frag, container.firstChild); 73 | 74 | } else { 75 | 76 | for (var k = 0; k < afm.length; k++) 77 | frag.appendChild(afm[k]); 78 | container.appendChild(frag); 79 | } 80 | frag = null; 81 | } 82 | 83 | 84 | 85 | /** 86 | * Internal function to test if a class name fits in a regular expression 87 | * @param {String} name to search against 88 | * @return {Boolean} 89 | * @api private 90 | */ 91 | 92 | function classRE(name) { 93 | return name in classCache ? classCache[name] : (classCache[name] = new RegExp('(^|\\s)' + name + '(\\s|$)')); 94 | } 95 | 96 | /** 97 | * Internal function that returns a array of unique elements 98 | * @param {Array} array to compare against 99 | * @return {Array} array of unique elements 100 | * @api private 101 | */ 102 | 103 | function unique(arr) { 104 | for (var i = 0; i < arr.length; i++) { 105 | if (arr.indexOf(arr[i]) != i) { 106 | arr.splice(i, 1); 107 | i--; 108 | } 109 | } 110 | return arr; 111 | } 112 | 113 | /** 114 | * Given a set of nodes, it returns them as an array. Used to find 115 | * siblings of an element 116 | * @param {Nodelist} Node list to search 117 | * @param {Object} [element] to find siblings off of 118 | * @return {Array} array of sibblings 119 | * @api private 120 | */ 121 | 122 | function siblings(nodes, element) { 123 | var elems = []; 124 | if (nodes == nundefined) 125 | return elems; 126 | 127 | for (; nodes; nodes = nodes.nextSibling) { 128 | if (nodes.nodeType == 1 && nodes !== element) { 129 | elems.push(nodes); 130 | } 131 | } 132 | return elems; 133 | } 134 | 135 | /** 136 | * This is the internal appframework object that gets extended and added on to it 137 | * This is also the start of our query selector engine 138 | * @param {String|Element|Object|Array} selector 139 | * @param {String|Element|Object} [context] 140 | */ 141 | var $afm = function(toSelect, what) { 142 | this.length = 0; 143 | if (!toSelect) { 144 | return this; 145 | } else if (toSelect instanceof $afm && what == nundefined) { 146 | return toSelect; 147 | } else if (af.isFunction(toSelect)) { 148 | return af(document).ready(toSelect); 149 | } else if (af.isArray(toSelect) && toSelect.length != nundefined) { //Passing in an array or object 150 | for (var i = 0; i < toSelect.length; i++) 151 | this[this.length++] = toSelect[i]; 152 | return this; 153 | } else if (af.isObject(toSelect) && af.isObject(what)) { //var tmp=$("span"); $("p").find(tmp); 154 | if (toSelect.length == nundefined) { 155 | if (toSelect.parentNode == what) 156 | this[this.length++] = toSelect; 157 | } else { 158 | for (var j = 0; j < toSelect.length; j++) 159 | if (toSelect[j].parentNode == what) 160 | this[this.length++] = toSelect[j]; 161 | } 162 | return this; 163 | } else if (af.isObject(toSelect) && what == nundefined) { //Single object 164 | this[this.length++] = toSelect; 165 | return this; 166 | } else if (what !== nundefined) { 167 | if (what instanceof $afm) { 168 | return what.find(toSelect); 169 | } 170 | 171 | } else { 172 | what = document; 173 | } 174 | 175 | return this.selector(toSelect, what); 176 | 177 | }; 178 | 179 | /** 180 | * This calls the $afm function 181 | * @param {String|Element|Object|Array} selector 182 | * @param {String|Element|Object} [context] 183 | */ 184 | var $ = function(selector, what) { 185 | return new $afm(selector, what); 186 | }; 187 | 188 | /** 189 | * this is the engine for "all" and is only exposed internally 190 | * @api private 191 | */ 192 | 193 | function _selectorAll(selector, what) { 194 | try { 195 | return what.querySelectorAll(selector); 196 | /*classSelectorRE.test(selector) ? what.getElementsByClassName(RegExp.$1) : 197 | tagSelectorRE.test(selector) ? what.getElementsByTagName(selector) : 198 | what.querySelectorAll(selector);*/ 199 | } catch (e) { 200 | return []; 201 | } 202 | } 203 | /** 204 | * this is the query selector engine for elements 205 | * @param {String} selector 206 | * @param {String|Element|Object} [context] 207 | * @api private 208 | */ 209 | 210 | function _selector(selector, what) { 211 | 212 | 213 | selector = selector.trim(); 214 | 215 | if (selector[0] === "#" && selector.indexOf(".") == -1 && selector.indexOf(" ") === -1 && selector.indexOf(">") === -1) { 216 | if (what == document) 217 | _shimNodes(what.getElementById(selector.replace("#", "")), this); 218 | else 219 | _shimNodes(_selectorAll(selector, what), this); 220 | } else if ((selector[0] === "<" && selector[selector.length - 1] === ">") || (selector.indexOf("<") !== -1 && selector.indexOf(">") !== -1)) //html 221 | 222 | { 223 | var tmp = document.createElement("div"); 224 | if (isWin8) { 225 | MSApp.execUnsafeLocalFunction(function() { 226 | tmp.innerHTML = selector.trim(); 227 | }); 228 | } else 229 | tmp.innerHTML = selector.trim(); 230 | _shimNodes(tmp.childNodes, this); 231 | } else { 232 | _shimNodes((_selectorAll(selector, what)), this); 233 | } 234 | return this; 235 | } 236 | 237 | function _shimNodes(nodes, obj) { 238 | if (!nodes) 239 | return; 240 | if (nodes.nodeType) { 241 | obj[obj.length++] = nodes; 242 | return; 243 | } 244 | for (var i = 0, iz = nodes.length; i < iz; i++) 245 | obj[obj.length++] = nodes[i]; 246 | } 247 | /** 248 | * Checks to see if the parameter is a $afm object 249 | ``` 250 | var foo=$('#header'); 251 | $.is$(foo); 252 | ``` 253 | 254 | * @param {Object} element 255 | * @return {Boolean} 256 | * @title $.is$(param) 257 | */ 258 | $.is$ = function(obj) { 259 | return obj instanceof $afm; 260 | }; 261 | /** 262 | * Map takes in elements and executes a callback function on each and returns a collection 263 | ``` 264 | $.map([1,2],function(ind){return ind+1}); 265 | ``` 266 | 267 | * @param {Array|Object} elements 268 | * @param {Function} callback 269 | * @return {Object} appframework object with elements in it 270 | * @title $.map(elements,callback) 271 | */ 272 | $.map = function(elements, callback) { 273 | var value, values = [], 274 | i, key; 275 | if ($.isArray(elements)) 276 | for (i = 0; i < elements.length; i++) { 277 | value = callback(elements[i], i); 278 | if (value !== nundefined) 279 | values.push(value); 280 | } else if ($.isObject(elements)) 281 | for (key in elements) { 282 | if (!elements.hasOwnProperty(key)) 283 | continue; 284 | value = callback(elements[key], key); 285 | if (value !== nundefined) 286 | values.push(value); 287 | } 288 | return af([values]); 289 | }; 290 | 291 | /** 292 | * Iterates through elements and executes a callback. Returns if false 293 | ``` 294 | $.each([1,2],function(ind){console.log(ind);}); 295 | ``` 296 | 297 | * @param {Array|Object} elements 298 | * @param {Function} callback 299 | * @return {Array} elements 300 | * @title $.each(elements,callback) 301 | */ 302 | $.each = function(elements, callback) { 303 | var i, key; 304 | if ($.isArray(elements)) 305 | for (i = 0; i < elements.length; i++) { 306 | if (callback(i, elements[i]) === false) 307 | return elements; 308 | } else if ($.isObject(elements)) 309 | for (key in elements) { 310 | if (!elements.hasOwnProperty(key)) 311 | continue; 312 | if (callback(key, elements[key]) === false) 313 | return elements; 314 | } 315 | return elements; 316 | }; 317 | 318 | /** 319 | * Extends an object with additional arguments 320 | ``` 321 | $.extend({foo:'bar'}); 322 | $.extend(element,{foo:'bar'}); 323 | ``` 324 | 325 | * @param {Object} [target] element 326 | * @param any number of additional arguments 327 | * @return {Object} [target] 328 | * @title $.extend(target,{params}) 329 | */ 330 | $.extend = function(target) { 331 | if (target == nundefined) 332 | target = this; 333 | if (arguments.length === 1) { 334 | for (var key in target) 335 | this[key] = target[key]; 336 | return this; 337 | } else { 338 | slice.call(arguments, 1).forEach(function(source) { 339 | for (var key in source) 340 | target[key] = source[key]; 341 | }); 342 | } 343 | return target; 344 | }; 345 | 346 | /** 347 | * Checks to see if the parameter is an array 348 | ``` 349 | var arr=[]; 350 | $.isArray(arr); 351 | ``` 352 | 353 | * @param {Object} element 354 | * @return {Boolean} 355 | * @example $.isArray([1]); 356 | * @title $.isArray(param) 357 | */ 358 | $.isArray = function(obj) { 359 | return obj instanceof Array && obj.push != nundefined; //ios 3.1.3 doesn't have Array.isArray 360 | }; 361 | 362 | /** 363 | * Checks to see if the parameter is a function 364 | ``` 365 | var func=function(){}; 366 | $.isFunction(func); 367 | ``` 368 | 369 | * @param {Object} element 370 | * @return {Boolean} 371 | * @title $.isFunction(param) 372 | */ 373 | $.isFunction = function(obj) { 374 | return typeof obj === "function" && !(obj instanceof RegExp); 375 | }; 376 | /** 377 | * Checks to see if the parameter is a object 378 | ``` 379 | var foo={bar:'bar'}; 380 | $.isObject(foo); 381 | ``` 382 | 383 | * @param {Object} element 384 | * @return {Boolean} 385 | * @title $.isObject(param) 386 | */ 387 | $.isObject = function(obj) { 388 | return typeof obj === "object"; 389 | }; 390 | 391 | /** 392 | * Prototype for afm object. Also extens $.fn 393 | */ 394 | $.fn = $afm.prototype = { 395 | constructor: $afm, 396 | forEach: emptyArray.forEach, 397 | reduce: emptyArray.reduce, 398 | push: emptyArray.push, 399 | indexOf: emptyArray.indexOf, 400 | concat: emptyArray.concat, 401 | selector: _selector, 402 | oldElement: undefined, 403 | slice: emptyArray.slice, 404 | length: 0, 405 | /** 406 | * This is a utility function for .end() 407 | * @param {Object} params 408 | * @return {Object} an appframework with params.oldElement set to this 409 | * @api private 410 | */ 411 | setupOld: function(params) { 412 | if (params == nundefined) 413 | return $(); 414 | params.oldElement = this; 415 | return params; 416 | 417 | }, 418 | /** 419 | * This is a wrapper to $.map on the selected elements 420 | ``` 421 | $().map(function(){this.value+=ind}); 422 | ``` 423 | 424 | * @param {Function} callback 425 | * @return {Object} an appframework object 426 | * @title $().map(function) 427 | */ 428 | map: function(fn) { 429 | var value, values = [], 430 | i; 431 | for (i = 0; i < this.length; i++) { 432 | value = fn(i, this[i]); 433 | if (value !== nundefined) 434 | values.push(value); 435 | } 436 | return $([values]); 437 | }, 438 | /** 439 | * Iterates through all elements and applys a callback function 440 | ``` 441 | $().each(function(){console.log(this.value)}); 442 | ``` 443 | 444 | * @param {Function} callback 445 | * @return {Object} an appframework object 446 | * @title $().each(function) 447 | */ 448 | each: function(callback) { 449 | this.forEach(function(el, idx) { 450 | callback.call(el, idx, el); 451 | }); 452 | return this; 453 | }, 454 | /** 455 | * This is executed when DOMContentLoaded happens, or after if you've registered for it. 456 | ``` 457 | $(document).ready(function(){console.log('I'm ready');}); 458 | ``` 459 | 460 | * @param {Function} callback 461 | * @return {Object} an appframework object 462 | * @title $().ready(function) 463 | */ 464 | 465 | ready: function(callback) { 466 | if (document.readyState === "complete" || document.readyState === "loaded" || (!$.os.ie && document.readyState === "interactive")) //IE10 fires interactive too early 467 | callback(); 468 | else 469 | document.addEventListener("DOMContentLoaded", callback, false); 470 | return this; 471 | }, 472 | /** 473 | * Searches through the collection and reduces them to elements that match the selector 474 | ``` 475 | $("#foo").find('.bar'); 476 | $("#foo").find($('.bar')); 477 | $("#foo").find($('.bar').get()); 478 | ``` 479 | 480 | * @param {String|Object|Array} selector 481 | * @return {Object} an appframework object filtered 482 | * @title $().find(selector) 483 | 484 | */ 485 | find: function(sel) { 486 | if (this.length === 0) 487 | return this; 488 | var elems = []; 489 | var tmpElems; 490 | for (var i = 0; i < this.length; i++) { 491 | tmpElems = ($(sel, this[i])); 492 | 493 | for (var j = 0; j < tmpElems.length; j++) { 494 | elems.push(tmpElems[j]); 495 | } 496 | } 497 | return $(unique(elems)); 498 | }, 499 | /** 500 | * Gets or sets the innerHTML for the collection. 501 | * If used as a get, the first elements innerHTML is returned 502 | ``` 503 | $("#foo").html(); //gets the first elements html 504 | $("#foo").html('new html');//sets the html 505 | $("#foo").html('new html',false); //Do not do memory management cleanup 506 | ``` 507 | 508 | * @param {String} html to set 509 | * @param {Bool} [cleanup] - set to false for performance tests and if you do not want to execute memory management cleanup 510 | * @return {Object} an appframework object 511 | * @title $().html([html]) 512 | */ 513 | html: function(html, cleanup) { 514 | if (this.length === 0) 515 | return this; 516 | if (html === nundefined) 517 | return this[0].innerHTML; 518 | 519 | for (var i = 0; i < this.length; i++) { 520 | if (cleanup !== false) 521 | $.cleanUpContent(this[i], false, true); 522 | if (isWin8) { 523 | MSApp.execUnsafeLocalFunction(function() { 524 | this[i].innerHTML = html; 525 | }); 526 | } else 527 | this[i].innerHTML = html; 528 | } 529 | return this; 530 | }, 531 | 532 | 533 | /** 534 | * Gets or sets the innerText for the collection. 535 | * If used as a get, the first elements innerText is returned 536 | ``` 537 | $("#foo").text(); //gets the first elements text; 538 | $("#foo").text('new text'); //sets the text 539 | ``` 540 | 541 | * @param {String} text to set 542 | * @return {Object} an appframework object 543 | * @title $().text([text]) 544 | */ 545 | text: function(text) { 546 | if (this.length === 0) 547 | return this; 548 | if (text === nundefined) 549 | return this[0].textContent; 550 | for (var i = 0; i < this.length; i++) { 551 | this[i].textContent = text; 552 | } 553 | return this; 554 | }, 555 | /** 556 | * Gets or sets a css property for the collection 557 | * If used as a get, the first elements css property is returned 558 | * This will add px to properties that need it. 559 | ``` 560 | $().css("background"); // Gets the first elements background 561 | $().css("background","red") //Sets the elements background to red 562 | ``` 563 | 564 | * @param {String} attribute to get 565 | * @param {String} value to set as 566 | * @return {Object} an appframework object 567 | * @title $().css(attribute,[value]) 568 | */ 569 | css: function(attribute, value, obj) { 570 | var toAct = obj != nundefined ? obj : this[0]; 571 | if (this.length === 0) 572 | return this; 573 | if (value == nundefined && typeof(attribute) === "string") { 574 | var styles = window.getComputedStyle(toAct); 575 | return toAct.style[attribute] ? toAct.style[attribute] : window.getComputedStyle(toAct)[attribute]; 576 | } 577 | for (var i = 0; i < this.length; i++) { 578 | if ($.isObject(attribute)) { 579 | for (var j in attribute) { 580 | this[i].style[j] = _addPx(j, attribute[j]); 581 | } 582 | } else { 583 | this[i].style[attribute] = _addPx(attribute, value); 584 | } 585 | } 586 | return this; 587 | }, 588 | /** 589 | * Gets or sets css vendor specific css properties 590 | * If used as a get, the first elements css property is returned 591 | ``` 592 | $().css("background"); // Gets the first elements background 593 | $().css("background","red") //Sets the elements background to red 594 | ``` 595 | 596 | * @param {String} attribute to get 597 | * @param {String} value to set as 598 | * @return {Object} an appframework object 599 | * @title $().css(attribute,[value]) 600 | */ 601 | vendorCss: function(attribute, value, obj) { 602 | return this.css($.feat.cssPrefix + attribute, value, obj); 603 | }, 604 | /** 605 | * Performs a css vendor specific transform:translate operation on the collection. 606 | * 607 | ``` 608 | $("#main").cssTranslate('200px,0,0'); 609 | ``` 610 | * @param {String} Transform values 611 | * @return {Object} an appframework object 612 | * @title $().vendorCss(value) 613 | */ 614 | cssTranslate: function(val) { 615 | return this.vendorCss("Transform", "translate" + $.feat.cssTransformStart + val + $.feat.cssTransformEnd); 616 | }, 617 | /** 618 | * Gets the computed style of CSS values 619 | * 620 | ``` 621 | $("#main").computedStyle('display'); 622 | ``` 623 | * @param {String} css property 624 | * @return {Int|String|Float|} css vlaue 625 | * @title $().computedStyle() 626 | */ 627 | computedStyle: function(val) { 628 | if (this.length === 0 || val == nundefined) return; 629 | return window.getComputedStyle(this[0], '')[val]; 630 | }, 631 | /** 632 | * Sets the innerHTML of all elements to an empty string 633 | ``` 634 | $().empty(); 635 | ``` 636 | 637 | * @return {Object} an appframework object 638 | * @title $().empty() 639 | */ 640 | empty: function() { 641 | for (var i = 0; i < this.length; i++) { 642 | $.cleanUpContent(this[i], false, true); 643 | this[i].textContent = ''; 644 | } 645 | return this; 646 | }, 647 | /** 648 | * Sets the elements display property to "none". 649 | * This will also store the old property into an attribute for hide 650 | ``` 651 | $().hide(); 652 | ``` 653 | 654 | * @return {Object} an appframework object 655 | * @title $().hide() 656 | */ 657 | hide: function() { 658 | if (this.length === 0) 659 | return this; 660 | for (var i = 0; i < this.length; i++) { 661 | if (this.css("display", null, this[i]) != "none") { 662 | this[i].setAttribute("afmOldStyle", this.css("display", null, this[i])); 663 | this[i].style.display = "none"; 664 | } 665 | } 666 | return this; 667 | }, 668 | /** 669 | * Shows all the elements by setting the css display property 670 | * We look to see if we were retaining an old style (like table-cell) and restore that, otherwise we set it to block 671 | ``` 672 | $().show(); 673 | ``` 674 | 675 | * @return {Object} an appframework object 676 | * @title $().show() 677 | */ 678 | show: function() { 679 | if (this.length === 0) 680 | return this; 681 | for (var i = 0; i < this.length; i++) { 682 | if (this.css("display", null, this[i]) == "none") { 683 | this[i].style.display = this[i].getAttribute("afmOldStyle") ? this[i].getAttribute("afmOldStyle") : 'block'; 684 | this[i].removeAttribute("afmOldStyle"); 685 | } 686 | } 687 | return this; 688 | }, 689 | /** 690 | * Toggle the visibility of a div 691 | ``` 692 | $().toggle(); 693 | $().toggle(true); //force showing 694 | ``` 695 | 696 | * @param {Boolean} [show] -force the hiding or showing of the element 697 | * @return {Object} an appframework object 698 | * @title $().toggle([show]) 699 | */ 700 | toggle: function(show) { 701 | var show2 = show === true ? true : false; 702 | for (var i = 0; i < this.length; i++) { 703 | if (window.getComputedStyle(this[i])['display'] !== "none" || (show != nundefined && show2 === false)) { 704 | this[i].setAttribute("afmOldStyle", this[i].style.display); 705 | this[i].style.display = "none"; 706 | } else { 707 | this[i].style.display = this[i].getAttribute("afmOldStyle") != nundefined ? this[i].getAttribute("afmOldStyle") : 'block'; 708 | this[i].removeAttribute("afmOldStyle"); 709 | } 710 | } 711 | return this; 712 | }, 713 | /** 714 | * Gets or sets an elements value 715 | * If used as a getter, we return the first elements value. If nothing is in the collection, we return undefined 716 | ``` 717 | $().value; //Gets the first elements value; 718 | $().value="bar"; //Sets all elements value to bar 719 | ``` 720 | 721 | * @param {String} [value] to set 722 | * @return {String|Object} A string as a getter, appframework object as a setter 723 | * @title $().val([value]) 724 | */ 725 | val: function(value) { 726 | if (this.length === 0) 727 | return (value === nundefined) ? undefined : this; 728 | if (value == nundefined) 729 | return this[0].value; 730 | for (var i = 0; i < this.length; i++) { 731 | this[i].value = value; 732 | } 733 | return this; 734 | }, 735 | /** 736 | * Gets or sets an attribute on an element 737 | * If used as a getter, we return the first elements value. If nothing is in the collection, we return undefined 738 | ``` 739 | $().attr("foo"); //Gets the first elements 'foo' attribute 740 | $().attr("foo","bar");//Sets the elements 'foo' attribute to 'bar' 741 | $().attr("foo",{bar:'bar'}) //Adds the object to an internal cache 742 | ``` 743 | 744 | * @param {String|Object} attribute to act upon. If it's an object (hashmap), it will set the attributes based off the kvp. 745 | * @param {String|Array|Object|function} [value] to set 746 | * @return {String|Object|Array|Function} If used as a getter, return the attribute value. If a setter, return an appframework object 747 | * @title $().attr(attribute,[value]) 748 | */ 749 | attr: function(attr, value) { 750 | if (this.length === 0) 751 | return (value === nundefined) ? undefined : this; 752 | if (value === nundefined && !$.isObject(attr)) { 753 | var val = (this[0].afmCacheId && _attrCache[this[0].afmCacheId][attr]) ? (this[0].afmCacheId && _attrCache[this[0].afmCacheId][attr]) : this[0].getAttribute(attr); 754 | return val; 755 | } 756 | for (var i = 0; i < this.length; i++) { 757 | if ($.isObject(attr)) { 758 | for (var key in attr) { 759 | $(this[i]).attr(key, attr[key]); 760 | } 761 | } else if ($.isArray(value) || $.isObject(value) || $.isFunction(value)) { 762 | 763 | if (!this[i].afmCacheId) 764 | this[i].afmCacheId = $.uuid(); 765 | 766 | if (!_attrCache[this[i].afmCacheId]) 767 | _attrCache[this[i].afmCacheId] = {}; 768 | _attrCache[this[i].afmCacheId][attr] = value; 769 | } else if (value === null && value != nundefined) { 770 | this[i].removeAttribute(attr); 771 | if (this[i].afmCacheId && _attrCache[this[i].afmCacheId][attr]) 772 | delete _attrCache[this[i].afmCacheId][attr]; 773 | } else { 774 | this[i].setAttribute(attr, value); 775 | } 776 | } 777 | return this; 778 | }, 779 | /** 780 | * Removes an attribute on the elements 781 | ``` 782 | $().removeAttr("foo"); 783 | ``` 784 | 785 | * @param {String} attributes that can be space delimited 786 | * @return {Object} appframework object 787 | * @title $().removeAttr(attribute) 788 | */ 789 | removeAttr: function(attr) { 790 | var that = this; 791 | for (var i = 0; i < this.length; i++) { 792 | attr.split(/\s+/g).forEach(function(param) { 793 | that[i].removeAttribute(param); 794 | if (that[i].afmCacheId && _attrCache[that[i].afmCacheId][attr]) 795 | delete _attrCache[that[i].afmCacheId][attr]; 796 | }); 797 | } 798 | return this; 799 | }, 800 | 801 | /** 802 | * Gets or sets a property on an element 803 | * If used as a getter, we return the first elements value. If nothing is in the collection, we return undefined 804 | ``` 805 | $().prop("foo"); //Gets the first elements 'foo' property 806 | $().prop("foo","bar");//Sets the elements 'foo' property to 'bar' 807 | $().prop("foo",{bar:'bar'}) //Adds the object to an internal cache 808 | ``` 809 | 810 | * @param {String|Object} property to act upon. If it's an object (hashmap), it will set the attributes based off the kvp. 811 | * @param {String|Array|Object|function} [value] to set 812 | * @return {String|Object|Array|Function} If used as a getter, return the property value. If a setter, return an appframework object 813 | * @title $().prop(property,[value]) 814 | */ 815 | prop: function(prop, value) { 816 | if (this.length === 0) 817 | return (value === nundefined) ? undefined : this; 818 | if (value === nundefined && !$.isObject(prop)) { 819 | var res; 820 | var val = (this[0].afmCacheId && _propCache[this[0].afmCacheId][prop]) ? (this[0].afmCacheId && _propCache[this[0].afmCacheId][prop]) : !(res = this[0][prop]) && prop in this[0] ? this[0][prop] : res; 821 | return val; 822 | } 823 | for (var i = 0; i < this.length; i++) { 824 | if ($.isObject(prop)) { 825 | for (var key in prop) { 826 | $(this[i]).prop(key, prop[key]); 827 | } 828 | } else if ($.isArray(value) || $.isObject(value) || $.isFunction(value)) { 829 | 830 | if (!this[i].afmCacheId) 831 | this[i].afmCacheId = $.uuid(); 832 | 833 | if (!_propCache[this[i].afmCacheId]) 834 | _propCache[this[i].afmCacheId] = {}; 835 | _propCache[this[i].afmCacheId][prop] = value; 836 | } else if (value === null && value !== undefined) { 837 | $(this[i]).removeProp(prop); 838 | } else { 839 | this[i][prop] = value; 840 | } 841 | } 842 | return this; 843 | }, 844 | /** 845 | * Removes a property on the elements 846 | ``` 847 | $().removeProp("foo"); 848 | ``` 849 | 850 | * @param {String} properties that can be space delimited 851 | * @return {Object} appframework object 852 | * @title $().removeProp(attribute) 853 | */ 854 | removeProp: function(prop) { 855 | var that = this; 856 | for (var i = 0; i < this.length; i++) { 857 | prop.split(/\s+/g).forEach(function(param) { 858 | if (that[i][param]) 859 | that[i][param] = undefined; 860 | if (that[i].afmCacheId && _propCache[that[i].afmCacheId][prop]) { 861 | delete _propCache[that[i].afmCacheId][prop]; 862 | } 863 | }); 864 | } 865 | return this; 866 | }, 867 | 868 | /** 869 | * Removes elements based off a selector 870 | ``` 871 | $().remove(); //Remove all 872 | $().remove(".foo");//Remove off a string selector 873 | var element=$("#foo").get(); 874 | $().remove(element); //Remove by an element 875 | $().remove($(".foo")); //Remove by a collection 876 | 877 | ``` 878 | 879 | * @param {String|Object|Array} selector to filter against 880 | * @return {Object} appframework object 881 | * @title $().remove(selector) 882 | */ 883 | remove: function(selector) { 884 | var elems = $(this).filter(selector); 885 | if (elems == nundefined) 886 | return this; 887 | for (var i = 0; i < elems.length; i++) { 888 | $.cleanUpContent(elems[i], true, true); 889 | elems[i].parentNode.removeChild(elems[i]); 890 | } 891 | return this; 892 | }, 893 | /** 894 | * Adds a css class to elements. 895 | ``` 896 | $().addClass("selected"); 897 | ``` 898 | 899 | * @param {String} classes that are space delimited 900 | * @return {Object} appframework object 901 | * @title $().addClass(name) 902 | */ 903 | addClass: function(name) { 904 | if (name == nundefined) return this; 905 | for (var i = 0; i < this.length; i++) { 906 | var cls = this[i].className; 907 | var classList = []; 908 | var that = this; 909 | name.split(/\s+/g).forEach(function(cname) { 910 | if (!that.hasClass(cname, that[i])) 911 | classList.push(cname); 912 | }); 913 | 914 | this[i].className += (cls ? " " : "") + classList.join(" "); 915 | this[i].className = this[i].className.trim(); 916 | } 917 | return this; 918 | }, 919 | /** 920 | * Removes a css class from elements. 921 | ``` 922 | $().removeClass("foo"); //single class 923 | $().removeClass("foo selected");//remove multiple classess 924 | ``` 925 | 926 | * @param {String} classes that are space delimited 927 | * @return {Object} appframework object 928 | * @title $().removeClass(name) 929 | */ 930 | removeClass: function(name) { 931 | if (name == nundefined) return this; 932 | for (var i = 0; i < this.length; i++) { 933 | if (name == nundefined) { 934 | this[i].className = ''; 935 | return this; 936 | } 937 | var classList = this[i].className; 938 | name.split(/\s+/g).forEach(function(cname) { 939 | classList = classList.replace(classRE(cname), " "); 940 | }); 941 | if (classList.length > 0) 942 | this[i].className = classList.trim(); 943 | else 944 | this[i].className = ""; 945 | } 946 | return this; 947 | }, 948 | /** 949 | * Replaces a css class on elements. 950 | ``` 951 | $().replaceClass("on", "off"); 952 | ``` 953 | 954 | * @param {String} classes that are space delimited 955 | * @param {String} classes that are space delimited 956 | * @return {Object} appframework object 957 | * @title $().replaceClass(old, new) 958 | */ 959 | replaceClass: function(name, newName) { 960 | if (name == nundefined || newName == nundefined) return this; 961 | for (var i = 0; i < this.length; i++) { 962 | if (name == nundefined) { 963 | this[i].className = newName; 964 | continue; 965 | } 966 | var classList = this[i].className; 967 | name.split(/\s+/g).concat(newName.split(/\s+/g)).forEach(function(cname) { 968 | classList = classList.replace(classRE(cname), " "); 969 | }); 970 | classList = classList.trim(); 971 | if (classList.length > 0) { 972 | this[i].className = (classList + " " + newName).trim(); 973 | } else 974 | this[i].className = newName; 975 | } 976 | return this; 977 | }, 978 | /** 979 | * Checks to see if an element has a class. 980 | ``` 981 | $().hasClass('foo'); 982 | $().hasClass('foo',element); 983 | ``` 984 | 985 | * @param {String} class name to check against 986 | * @param {Object} [element] to check against 987 | * @return {Boolean} 988 | * @title $().hasClass(name,[element]) 989 | */ 990 | hasClass: function(name, element) { 991 | if (this.length === 0) 992 | return false; 993 | if (!element) 994 | element = this[0]; 995 | return classRE(name).test(element.className); 996 | }, 997 | /** 998 | * Appends to the elements 999 | * We boil everything down to an appframework object and then loop through that. 1000 | * If it's HTML, we create a dom element so we do not break event bindings. 1001 | * if it's a script tag, we evaluate it. 1002 | ``` 1003 | $().append("
"); //Creates the object from the string and appends it 1004 | $().append($("#foo")); //Append an object; 1005 | ``` 1006 | 1007 | * @param {String|Object} Element/string to add 1008 | * @param {Boolean} [insert] insert or append 1009 | * @return {Object} appframework object 1010 | * @title $().append(element,[insert]) 1011 | */ 1012 | append: function(element, insert) { 1013 | if (element && element.length != nundefined && element.length === 0) 1014 | return this; 1015 | if ($.isArray(element) || $.isObject(element)) 1016 | element = $(element); 1017 | var i; 1018 | 1019 | 1020 | for (i = 0; i < this.length; i++) { 1021 | if (element.length && typeof element != "string") { 1022 | element = $(element); 1023 | _insertFragments(element, this[i], insert); 1024 | } else { 1025 | var obj = fragementRE.test(element) ? $(element) : undefined; 1026 | if (obj == nundefined || obj.length === 0) { 1027 | obj = document.createTextNode(element); 1028 | } 1029 | if (obj.nodeName != nundefined && obj.nodeName.toLowerCase() == "script" && (!obj.type || obj.type.toLowerCase() === 'text/javascript')) { 1030 | window['eval'](obj.innerHTML); 1031 | } else if (obj instanceof $afm) { 1032 | _insertFragments(obj, this[i], insert); 1033 | } else { 1034 | insert != nundefined ? this[i].insertBefore(obj, this[i].firstChild) : this[i].appendChild(obj); 1035 | } 1036 | } 1037 | } 1038 | return this; 1039 | }, 1040 | /** 1041 | * Appends the current collection to the selector 1042 | ``` 1043 | $().appendTo("#foo"); //Append an object; 1044 | ``` 1045 | 1046 | * @param {String|Object} Selector to append to 1047 | * @param {Boolean} [insert] insert or append 1048 | * @title $().appendTo(element,[insert]) 1049 | */ 1050 | appendTo: function(selector, insert) { 1051 | var tmp = $(selector); 1052 | tmp.append(this); 1053 | return this; 1054 | }, 1055 | /** 1056 | * Prepends the current collection to the selector 1057 | ``` 1058 | $().prependTo("#foo"); //Prepend an object; 1059 | ``` 1060 | 1061 | * @param {String|Object} Selector to prepent to 1062 | * @title $().prependTo(element) 1063 | */ 1064 | prependTo: function(selector) { 1065 | var tmp = $(selector); 1066 | tmp.append(this, true); 1067 | return this; 1068 | }, 1069 | /** 1070 | * Prepends to the elements 1071 | * This simply calls append and sets insert to true 1072 | ``` 1073 | $().prepend("
");//Creates the object from the string and appends it 1074 | $().prepend($("#foo")); //Prepends an object 1075 | ``` 1076 | 1077 | * @param {String|Object} Element/string to add 1078 | * @return {Object} appframework object 1079 | * @title $().prepend(element) 1080 | */ 1081 | prepend: function(element) { 1082 | return this.append(element, 1); 1083 | }, 1084 | /** 1085 | * Inserts collection before the target (adjacent) 1086 | ``` 1087 | $().insertBefore(af("#target")); 1088 | ``` 1089 | 1090 | * @param {String|Object} Target 1091 | * @title $().insertBefore(target); 1092 | */ 1093 | insertBefore: function(target, after) { 1094 | if (this.length === 0) 1095 | return this; 1096 | target = $(target).get(0); 1097 | if (!target) 1098 | return this; 1099 | for (var i = 0; i < this.length; i++) { 1100 | after ? target.parentNode.insertBefore(this[i], target.nextSibling) : target.parentNode.insertBefore(this[i], target); 1101 | } 1102 | return this; 1103 | }, 1104 | /** 1105 | * Inserts collection after the target (adjacent) 1106 | ``` 1107 | $().insertAfter(af("#target")); 1108 | ``` 1109 | * @param {String|Object} target 1110 | * @title $().insertAfter(target); 1111 | */ 1112 | insertAfter: function(target) { 1113 | this.insertBefore(target, true); 1114 | }, 1115 | /** 1116 | * Returns the raw DOM element. 1117 | ``` 1118 | $().get(); //returns the first element 1119 | $().get(2);// returns the third element 1120 | ``` 1121 | 1122 | * @param {Int} [index] 1123 | * @return {Object} raw DOM element 1124 | * @title $().get([index]) 1125 | */ 1126 | get: function(index) { 1127 | index = index == nundefined ? 0 : index; 1128 | if (index < 0) 1129 | index += this.length; 1130 | return (this[index]) ? this[index] : undefined; 1131 | }, 1132 | /** 1133 | * Returns the offset of the element, including traversing up the tree 1134 | ``` 1135 | $().offset(); 1136 | ``` 1137 | 1138 | * @return {Object} with left, top, width and height properties 1139 | * @title $().offset() 1140 | */ 1141 | offset: function() { 1142 | var obj; 1143 | if (this.length === 0) 1144 | return this; 1145 | if (this[0] == window) 1146 | return { 1147 | left: 0, 1148 | top: 0, 1149 | right: 0, 1150 | bottom: 0, 1151 | width: window.innerWidth, 1152 | height: window.innerHeight 1153 | }; 1154 | else 1155 | obj = this[0].getBoundingClientRect(); 1156 | return { 1157 | left: obj.left + window.pageXOffset, 1158 | top: obj.top + window.pageYOffset, 1159 | right: obj.right + window.pageXOffset, 1160 | bottom: obj.bottom + window.pageYOffset, 1161 | width: obj.right - obj.left, 1162 | height: obj.bottom - obj.top 1163 | }; 1164 | }, 1165 | /** 1166 | * returns the height of the element, including padding on IE 1167 | ``` 1168 | $().height(); 1169 | ``` 1170 | * @return {string} height 1171 | * @title $().height() 1172 | */ 1173 | height: function(val) { 1174 | if (this.length === 0) 1175 | return this; 1176 | if (val != nundefined) 1177 | return this.css("height", val); 1178 | if (this[0] == this[0].window) 1179 | return window.innerHeight; 1180 | if (this[0].nodeType == this[0].DOCUMENT_NODE) 1181 | return this[0].documentElement.offsetheight; 1182 | else { 1183 | var tmpVal = this.css("height").replace("px", ""); 1184 | if (tmpVal) 1185 | return tmpVal; 1186 | else 1187 | return this.offset().height; 1188 | } 1189 | }, 1190 | /** 1191 | * returns the width of the element, including padding on IE 1192 | ``` 1193 | $().width(); 1194 | ``` 1195 | * @return {string} width 1196 | * @title $().width() 1197 | */ 1198 | width: function(val) { 1199 | if (this.length === 0) 1200 | return this; 1201 | if (val != nundefined) 1202 | return this.css("width", val); 1203 | if (this[0] == this[0].window) 1204 | return window.innerWidth; 1205 | if (this[0].nodeType == this[0].DOCUMENT_NODE) 1206 | return this[0].documentElement.offsetwidth; 1207 | else { 1208 | var tmpVal = this.css("width").replace("px", ""); 1209 | if (tmpVal) 1210 | return tmpVal; 1211 | else 1212 | return this.offset().width; 1213 | } 1214 | }, 1215 | /** 1216 | * Returns the parent nodes of the elements based off the selector 1217 | ``` 1218 | $("#foo").parent('.bar'); 1219 | $("#foo").parent($('.bar')); 1220 | $("#foo").parent($('.bar').get()); 1221 | ``` 1222 | 1223 | * @param {String|Array|Object} [selector] 1224 | * @return {Object} appframework object with unique parents 1225 | * @title $().parent(selector) 1226 | */ 1227 | parent: function(selector, recursive) { 1228 | if (this.length === 0) 1229 | return this; 1230 | var elems = []; 1231 | for (var i = 0; i < this.length; i++) { 1232 | var tmp = this[i]; 1233 | while (tmp.parentNode && tmp.parentNode != document) { 1234 | elems.push(tmp.parentNode); 1235 | if (tmp.parentNode) 1236 | tmp = tmp.parentNode; 1237 | if (!recursive) 1238 | break; 1239 | } 1240 | } 1241 | return this.setupOld($(unique(elems)).filter(selector)); 1242 | }, 1243 | /** 1244 | * Returns the parents of the elements based off the selector (traversing up until html document) 1245 | ``` 1246 | $("#foo").parents('.bar'); 1247 | $("#foo").parents($('.bar')); 1248 | $("#foo").parents($('.bar').get()); 1249 | ``` 1250 | 1251 | * @param {String|Array|Object} [selector] 1252 | * @return {Object} appframework object with unique parents 1253 | * @title $().parents(selector) 1254 | */ 1255 | parents: function(selector) { 1256 | return this.parent(selector, true); 1257 | }, 1258 | /** 1259 | * Returns the child nodes of the elements based off the selector 1260 | ``` 1261 | $("#foo").children('.bar'); //Selector 1262 | $("#foo").children($('.bar')); //Objects 1263 | $("#foo").children($('.bar').get()); //Single element 1264 | ``` 1265 | 1266 | * @param {String|Array|Object} [selector] 1267 | * @return {Object} appframework object with unique children 1268 | * @title $().children(selector) 1269 | */ 1270 | children: function(selector) { 1271 | 1272 | if (this.length === 0) 1273 | return this; 1274 | var elems = []; 1275 | for (var i = 0; i < this.length; i++) { 1276 | elems = elems.concat(siblings(this[i].firstChild)); 1277 | } 1278 | return this.setupOld($((elems)).filter(selector)); 1279 | 1280 | }, 1281 | /** 1282 | * Returns the siblings of the element based off the selector 1283 | ``` 1284 | $("#foo").siblings('.bar'); //Selector 1285 | $("#foo").siblings($('.bar')); //Objects 1286 | $("#foo").siblings($('.bar').get()); //Single element 1287 | ``` 1288 | 1289 | * @param {String|Array|Object} [selector] 1290 | * @return {Object} appframework object with unique siblings 1291 | * @title $().siblings(selector) 1292 | */ 1293 | siblings: function(selector) { 1294 | if (this.length === 0) 1295 | return this; 1296 | var elems = []; 1297 | for (var i = 0; i < this.length; i++) { 1298 | if (this[i].parentNode) 1299 | elems = elems.concat(siblings(this[i].parentNode.firstChild, this[i])); 1300 | } 1301 | return this.setupOld($(elems).filter(selector)); 1302 | }, 1303 | /** 1304 | * Returns the closest element based off the selector and optional context 1305 | ``` 1306 | $("#foo").closest('.bar'); //Selector 1307 | $("#foo").closest($('.bar')); //Objects 1308 | $("#foo").closest($('.bar').get()); //Single element 1309 | ``` 1310 | 1311 | * @param {String|Array|Object} selector 1312 | * @param {Object} [context] 1313 | * @return {Object} Returns an appframework object with the closest element based off the selector 1314 | * @title $().closest(selector,[context]); 1315 | */ 1316 | closest: function(selector, context) { 1317 | if (this.length === 0) 1318 | return this; 1319 | var elems = [], 1320 | cur = this[0]; 1321 | 1322 | var start = $(selector, context); 1323 | if (start.length === 0) 1324 | return $(); 1325 | while (cur && start.indexOf(cur) == -1) { 1326 | cur = cur !== context && cur !== document && cur.parentNode; 1327 | } 1328 | return $(cur); 1329 | 1330 | }, 1331 | /** 1332 | * Filters elements based off the selector 1333 | ``` 1334 | $("#foo").filter('.bar'); //Selector 1335 | $("#foo").filter($('.bar')); //Objects 1336 | $("#foo").filter($('.bar').get()); //Single element 1337 | ``` 1338 | 1339 | * @param {String|Array|Object} selector 1340 | * @return {Object} Returns an appframework object after the filter was run 1341 | * @title $().filter(selector); 1342 | */ 1343 | filter: function(selector) { 1344 | if (this.length === 0) 1345 | return this; 1346 | 1347 | if (selector == nundefined) 1348 | return this; 1349 | var elems = []; 1350 | for (var i = 0; i < this.length; i++) { 1351 | var val = this[i]; 1352 | if (val.parentNode && $(selector, val.parentNode).indexOf(val) >= 0) 1353 | elems.push(val); 1354 | } 1355 | return this.setupOld($(unique(elems))); 1356 | }, 1357 | /** 1358 | * Basically the reverse of filter. Return all elements that do NOT match the selector 1359 | ``` 1360 | $("#foo").not('.bar'); //Selector 1361 | $("#foo").not($('.bar')); //Objects 1362 | $("#foo").not($('.bar').get()); //Single element 1363 | ``` 1364 | 1365 | * @param {String|Array|Object} selector 1366 | * @return {Object} Returns an appframework object after the filter was run 1367 | * @title $().not(selector); 1368 | */ 1369 | not: function(selector) { 1370 | if (this.length === 0) 1371 | return this; 1372 | var elems = []; 1373 | for (var i = 0; i < this.length; i++) { 1374 | var val = this[i]; 1375 | if (val.parentNode && $(selector, val.parentNode).indexOf(val) == -1) 1376 | elems.push(val); 1377 | } 1378 | return this.setupOld($(unique(elems))); 1379 | }, 1380 | /** 1381 | * Gets or set data-* attribute parameters on elements (when a string) 1382 | * When used as a getter, it's only the first element 1383 | ``` 1384 | $().data("foo"); //Gets the data-foo attribute for the first element 1385 | $().data("foo","bar"); //Sets the data-foo attribute for all elements 1386 | $().data("foo",{bar:'bar'});//object as the data 1387 | ``` 1388 | 1389 | * @param {String} key 1390 | * @param {String|Array|Object} value 1391 | * @return {String|Object} returns the value or appframework object 1392 | * @title $().data(key,[value]); 1393 | */ 1394 | data: function(key, value) { 1395 | return this.attr('data-' + key, value); 1396 | }, 1397 | /** 1398 | * Rolls back the appframework elements when filters were applied 1399 | * This can be used after .not(), .filter(), .children(), .parent() 1400 | ``` 1401 | $().filter(".panel").end(); //This will return the collection BEFORE filter is applied 1402 | ``` 1403 | 1404 | * @return {Object} returns the previous appframework object before filter was applied 1405 | * @title $().end(); 1406 | */ 1407 | end: function() { 1408 | return this.oldElement != nundefined ? this.oldElement : $(); 1409 | }, 1410 | /** 1411 | * Clones the nodes in the collection. 1412 | ``` 1413 | $().clone();// Deep clone of all elements 1414 | $().clone(false); //Shallow clone 1415 | ``` 1416 | 1417 | * @param {Boolean} [deep] - do a deep copy or not 1418 | * @return {Object} appframework object of cloned nodes 1419 | * @title $().clone(); 1420 | */ 1421 | clone: function(deep) { 1422 | deep = deep === false ? false : true; 1423 | if (this.length === 0) 1424 | return this; 1425 | var elems = []; 1426 | for (var i = 0; i < this.length; i++) { 1427 | elems.push(this[i].cloneNode(deep)); 1428 | } 1429 | 1430 | return $(elems); 1431 | }, 1432 | /** 1433 | * Returns the number of elements in the collection 1434 | ``` 1435 | $().size(); 1436 | ``` 1437 | 1438 | * @return {Int} 1439 | * @title $().size(); 1440 | */ 1441 | size: function() { 1442 | return this.length; 1443 | }, 1444 | /** 1445 | * Serailizes a form into a query string 1446 | ``` 1447 | $().serialize(); 1448 | ``` 1449 | * @return {String} 1450 | * @title $().serialize() 1451 | */ 1452 | serialize: function() { 1453 | if (this.length === 0) 1454 | return ""; 1455 | var params = []; 1456 | for (var i = 0; i < this.length; i++) { 1457 | this.slice.call(this[i].elements).forEach(function(elem) { 1458 | var type = elem.getAttribute("type"); 1459 | if (elem.nodeName.toLowerCase() != "fieldset" && !elem.disabled && type != "submit" && type != "reset" && type != "button" && ((type != "radio" && type != "checkbox") || elem.checked)) { 1460 | 1461 | if (elem.getAttribute("name")) { 1462 | if (elem.type == "select-multiple") { 1463 | for (var j = 0; j < elem.options.length; j++) { 1464 | if (elem.options[j].selected) 1465 | params.push(elem.getAttribute("name") + "=" + encodeURIComponent(elem.options[j].value)); 1466 | } 1467 | } else 1468 | params.push(elem.getAttribute("name") + "=" + encodeURIComponent(elem.value)); 1469 | } 1470 | } 1471 | }); 1472 | } 1473 | return params.join("&"); 1474 | }, 1475 | 1476 | /* added in 1.2 */ 1477 | /** 1478 | * Reduce the set of elements based off index 1479 | ``` 1480 | $().eq(index) 1481 | ``` 1482 | * @param {Int} index - Index to filter by. If negative, it will go back from the end 1483 | * @return {Object} appframework object 1484 | * @title $().eq(index) 1485 | */ 1486 | eq: function(ind) { 1487 | return $(this.get(ind)); 1488 | }, 1489 | /** 1490 | * Returns the index of the selected element in the collection 1491 | ``` 1492 | $().index(elem) 1493 | ``` 1494 | * @param {String|Object} element to look for. Can be a selector or object 1495 | * @return integer - index of selected element 1496 | * @title $().index(elem) 1497 | */ 1498 | index: function(elem) { 1499 | return elem ? this.indexOf($(elem)[0]) : this.parent().children().indexOf(this[0]); 1500 | }, 1501 | /** 1502 | * Returns boolean if the object is a type of the selector 1503 | ``` 1504 | $().is(selector) 1505 | ``` 1506 | * param {String|Object} selector to act upon 1507 | * @return boolean 1508 | * @title $().is(selector) 1509 | */ 1510 | is: function(selector) { 1511 | return !!selector && this.filter(selector).length > 0; 1512 | } 1513 | 1514 | }; 1515 | 1516 | 1517 | /* AJAX functions */ 1518 | 1519 | function empty() {} 1520 | $.ajaxSettings = { 1521 | type: 'GET', 1522 | beforeSend: empty, 1523 | success: empty, 1524 | error: empty, 1525 | complete: empty, 1526 | context: undefined, 1527 | timeout: 0, 1528 | crossDomain: null 1529 | }; 1530 | /** 1531 | * Execute a jsonP call, allowing cross domain scripting 1532 | * options.url - URL to call 1533 | * options.success - Success function to call 1534 | * options.error - Error function to call 1535 | ``` 1536 | $.jsonP({url:'mysite.php?callback=?&foo=bar',success:function(){},error:function(){}}); 1537 | ``` 1538 | 1539 | * @param {Object} options 1540 | * @title $.jsonP(options) 1541 | */ 1542 | $.jsonP = function(options) { 1543 | if (isWin8) { 1544 | options.type = "get"; 1545 | options.dataType = null; 1546 | return $.get(options); 1547 | } 1548 | var callbackName = 'jsonp_callback' + (++_jsonPID); 1549 | var abortTimeout = "", 1550 | context; 1551 | var script = document.createElement("script"); 1552 | var abort = function() { 1553 | $(script).remove(); 1554 | if (window[callbackName]) 1555 | window[callbackName] = empty; 1556 | }; 1557 | window[callbackName] = function(data) { 1558 | clearTimeout(abortTimeout); 1559 | $(script).remove(); 1560 | delete window[callbackName]; 1561 | options.success.call(context, data); 1562 | }; 1563 | script.src = options.url.replace(/=\?/, '=' + callbackName); 1564 | if (options.error) { 1565 | script.onerror = function() { 1566 | clearTimeout(abortTimeout); 1567 | options.error.call(context, "", 'error'); 1568 | }; 1569 | } 1570 | $('head').append(script); 1571 | if (options.timeout > 0) 1572 | abortTimeout = setTimeout(function() { 1573 | options.error.call(context, "", 'timeout'); 1574 | }, options.timeout); 1575 | return {}; 1576 | }; 1577 | 1578 | /** 1579 | * Execute an Ajax call with the given options 1580 | * options.type - Type of request 1581 | * options.beforeSend - function to execute before sending the request 1582 | * options.success - success callback 1583 | * options.error - error callback 1584 | * options.complete - complete callback - callled with a success or error 1585 | * options.timeout - timeout to wait for the request 1586 | * options.url - URL to make request against 1587 | * options.contentType - HTTP Request Content Type 1588 | * options.headers - Object of headers to set 1589 | * options.dataType - Data type of request 1590 | * options.data - data to pass into request. $.param is called on objects 1591 | ``` 1592 | var opts={ 1593 | type:"GET", 1594 | success:function(data){}, 1595 | url:"mypage.php", 1596 | data:{bar:'bar'}, 1597 | } 1598 | $.ajax(opts); 1599 | ``` 1600 | 1601 | * @param {Object} options 1602 | * @title $.ajax(options) 1603 | */ 1604 | $.ajax = function(opts) { 1605 | var xhr; 1606 | try { 1607 | 1608 | var settings = opts || {}; 1609 | for (var key in $.ajaxSettings) { 1610 | if (typeof(settings[key]) == 'undefined') 1611 | settings[key] = $.ajaxSettings[key]; 1612 | } 1613 | 1614 | if (!settings.url) 1615 | settings.url = window.location; 1616 | if (!settings.contentType) 1617 | settings.contentType = "application/x-www-form-urlencoded"; 1618 | if (!settings.headers) 1619 | settings.headers = {}; 1620 | 1621 | if (!('async' in settings) || settings.async !== false) 1622 | settings.async = true; 1623 | 1624 | if (!settings.dataType) 1625 | settings.dataType = "text/html"; 1626 | else { 1627 | switch (settings.dataType) { 1628 | case "script": 1629 | settings.dataType = 'text/javascript, application/javascript'; 1630 | break; 1631 | case "json": 1632 | settings.dataType = 'application/json'; 1633 | break; 1634 | case "xml": 1635 | settings.dataType = 'application/xml, text/xml'; 1636 | break; 1637 | case "html": 1638 | settings.dataType = 'text/html'; 1639 | break; 1640 | case "text": 1641 | settings.dataType = 'text/plain'; 1642 | break; 1643 | default: 1644 | settings.dataType = "text/html"; 1645 | break; 1646 | case "jsonp": 1647 | return $.jsonP(opts); 1648 | } 1649 | } 1650 | if ($.isObject(settings.data)) 1651 | settings.data = $.param(settings.data); 1652 | if (settings.type.toLowerCase() === "get" && settings.data) { 1653 | if (settings.url.indexOf("?") === -1) 1654 | settings.url += "?" + settings.data; 1655 | else 1656 | settings.url += "&" + settings.data; 1657 | } 1658 | 1659 | if (/=\?/.test(settings.url)) { 1660 | return $.jsonP(settings); 1661 | } 1662 | if (settings.crossDomain === null) settings.crossDomain = /^([\w-]+:)?\/\/([^\/]+)/.test(settings.url) && 1663 | RegExp.$2 != window.location.host; 1664 | 1665 | if (!settings.crossDomain) 1666 | settings.headers = $.extend({ 1667 | 'X-Requested-With': 'XMLHttpRequest' 1668 | }, settings.headers); 1669 | var abortTimeout; 1670 | var context = settings.context; 1671 | var protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol; 1672 | 1673 | //ok, we are really using xhr 1674 | xhr = new window.XMLHttpRequest(); 1675 | 1676 | 1677 | xhr.onreadystatechange = function() { 1678 | var mime = settings.dataType; 1679 | if (xhr.readyState === 4) { 1680 | clearTimeout(abortTimeout); 1681 | var result, error = false; 1682 | if ((xhr.status >= 200 && xhr.status < 300) || xhr.status === 0 && protocol == 'file:') { 1683 | if (mime === 'application/json' && !(/^\s*$/.test(xhr.responseText))) { 1684 | try { 1685 | result = JSON.parse(xhr.responseText); 1686 | } catch (e) { 1687 | error = e; 1688 | } 1689 | } else if (mime === 'application/xml, text/xml') { 1690 | result = xhr.responseXML; 1691 | } else if (mime == "text/html") { 1692 | result = xhr.responseText; 1693 | $.parseJS(result); 1694 | } else 1695 | result = xhr.responseText; 1696 | //If we're looking at a local file, we assume that no response sent back means there was an error 1697 | if (xhr.status === 0 && result.length === 0) 1698 | error = true; 1699 | if (error) 1700 | settings.error.call(context, xhr, 'parsererror', error); 1701 | else { 1702 | settings.success.call(context, result, 'success', xhr); 1703 | } 1704 | } else { 1705 | error = true; 1706 | settings.error.call(context, xhr, 'error'); 1707 | } 1708 | settings.complete.call(context, xhr, error ? 'error' : 'success'); 1709 | } 1710 | }; 1711 | xhr.open(settings.type, settings.url, settings.async); 1712 | if (settings.withCredentials) xhr.withCredentials = true; 1713 | 1714 | if (settings.contentType) 1715 | settings.headers['Content-Type'] = settings.contentType; 1716 | for (var name in settings.headers) 1717 | xhr.setRequestHeader(name, settings.headers[name]); 1718 | if (settings.beforeSend.call(context, xhr, settings) === false) { 1719 | xhr.abort(); 1720 | return false; 1721 | } 1722 | 1723 | if (settings.timeout > 0) 1724 | abortTimeout = setTimeout(function() { 1725 | xhr.onreadystatechange = empty; 1726 | xhr.abort(); 1727 | settings.error.call(context, xhr, 'timeout'); 1728 | }, settings.timeout); 1729 | xhr.send(settings.data); 1730 | } catch (e) { 1731 | // General errors (e.g. access denied) should also be sent to the error callback 1732 | console.log(e); 1733 | settings.error.call(context, xhr, 'error', e); 1734 | } 1735 | return xhr; 1736 | }; 1737 | 1738 | 1739 | /** 1740 | * Shorthand call to an Ajax GET request 1741 | ``` 1742 | $.get("mypage.php?foo=bar",function(data){}); 1743 | ``` 1744 | 1745 | * @param {String} url to hit 1746 | * @param {Function} success 1747 | * @title $.get(url,success) 1748 | */ 1749 | $.get = function(url, success) { 1750 | return this.ajax({ 1751 | url: url, 1752 | success: success 1753 | }); 1754 | }; 1755 | /** 1756 | * Shorthand call to an Ajax POST request 1757 | ``` 1758 | $.post("mypage.php",{bar:'bar'},function(data){}); 1759 | ``` 1760 | 1761 | * @param {String} url to hit 1762 | * @param {Object} [data] to pass in 1763 | * @param {Function} success 1764 | * @param {String} [dataType] 1765 | * @title $.post(url,[data],success,[dataType]) 1766 | */ 1767 | $.post = function(url, data, success, dataType) { 1768 | if (typeof(data) === "function") { 1769 | success = data; 1770 | data = {}; 1771 | } 1772 | if (dataType === nundefined) 1773 | dataType = "html"; 1774 | return this.ajax({ 1775 | url: url, 1776 | type: "POST", 1777 | data: data, 1778 | dataType: dataType, 1779 | success: success 1780 | }); 1781 | }; 1782 | /** 1783 | * Shorthand call to an Ajax request that expects a JSON response 1784 | ``` 1785 | $.getJSON("mypage.php",{bar:'bar'},function(data){}); 1786 | ``` 1787 | 1788 | * @param {String} url to hit 1789 | * @param {Object} [data] 1790 | * @param {Function} [success] 1791 | * @title $.getJSON(url,data,success) 1792 | */ 1793 | $.getJSON = function(url, data, success) { 1794 | if (typeof(data) === "function") { 1795 | success = data; 1796 | data = {}; 1797 | } 1798 | return this.ajax({ 1799 | url: url, 1800 | data: data, 1801 | success: success, 1802 | dataType: "json" 1803 | }); 1804 | }; 1805 | 1806 | /** 1807 | * Converts an object into a key/value par with an optional prefix. Used for converting objects to a query string 1808 | ``` 1809 | var obj={ 1810 | foo:'foo', 1811 | bar:'bar' 1812 | } 1813 | var kvp=$.param(obj,'data'); 1814 | ``` 1815 | 1816 | * @param {Object} object 1817 | * @param {String} [prefix] 1818 | * @return {String} Key/value pair representation 1819 | * @title $.param(object,[prefix]; 1820 | */ 1821 | $.param = function(obj, prefix) { 1822 | var str = []; 1823 | if (obj instanceof $afm) { 1824 | obj.each(function() { 1825 | var k = prefix ? prefix + "[" + this.id + "]" : this.id, 1826 | v = this.value; 1827 | str.push((k) + "=" + encodeURIComponent(v)); 1828 | }); 1829 | } else { 1830 | for (var p in obj) { 1831 | var k = prefix ? prefix + "[" + p + "]" : p, 1832 | v = obj[p]; 1833 | str.push($.isObject(v) ? $.param(v, k) : (k) + "=" + encodeURIComponent(v)); 1834 | } 1835 | } 1836 | return str.join("&"); 1837 | }; 1838 | /** 1839 | * Used for backwards compatibility. Uses native JSON.parse function 1840 | ``` 1841 | var obj=$.parseJSON("{\"bar\":\"bar\"}"); 1842 | ``` 1843 | 1844 | * @params {String} string 1845 | * @return {Object} 1846 | * @title $.parseJSON(string) 1847 | */ 1848 | $.parseJSON = function(string) { 1849 | return JSON.parse(string); 1850 | }; 1851 | /** 1852 | * Helper function to convert XML into the DOM node representation 1853 | ``` 1854 | var xmlDoc=$.parseXML("bar"); 1855 | ``` 1856 | 1857 | * @param {String} string 1858 | * @return {Object} DOM nodes 1859 | * @title $.parseXML(string) 1860 | */ 1861 | $.parseXML = function(string) { 1862 | if (isWin8) { 1863 | MSApp.execUnsafeLocalFunction(function() { 1864 | return (new DOMParser()).parseFromString(string, "text/xml"); 1865 | }); 1866 | } else 1867 | return (new DOMParser()).parseFromString(string, "text/xml"); 1868 | }; 1869 | /** 1870 | * Helper function to parse the user agent. Sets the following 1871 | * .os.webkit 1872 | * .os.android 1873 | * .os.ipad 1874 | * .os.iphone 1875 | * .os.webos 1876 | * .os.touchpad 1877 | * .os.blackberry 1878 | * .os.opera 1879 | * .os.fennec 1880 | * .os.ie 1881 | * .os.ieTouch 1882 | * .os.supportsTouch 1883 | * .os.playbook 1884 | * .feat.nativetouchScroll 1885 | * @api private 1886 | */ 1887 | 1888 | function detectUA($, userAgent) { 1889 | $.os = {}; 1890 | $.os.webkit = userAgent.match(/WebKit\/([\d.]+)/) ? true : false; 1891 | $.os.android = userAgent.match(/(Android)\s+([\d.]+)/) || userAgent.match(/Silk-Accelerated/) ? true : false; 1892 | $.os.androidICS = $.os.android && userAgent.match(/(Android)\s4/) ? true : false; 1893 | $.os.ipad = userAgent.match(/(iPad).*OS\s([\d_]+)/) ? true : false; 1894 | $.os.iphone = !$.os.ipad && userAgent.match(/(iPhone\sOS)\s([\d_]+)/) ? true : false; 1895 | $.os.webos = userAgent.match(/(webOS|hpwOS)[\s\/]([\d.]+)/) ? true : false; 1896 | $.os.touchpad = $.os.webos && userAgent.match(/TouchPad/) ? true : false; 1897 | $.os.ios = $.os.ipad || $.os.iphone; 1898 | $.os.playbook = userAgent.match(/PlayBook/) ? true : false; 1899 | $.os.blackberry = $.os.playbook || userAgent.match(/BlackBerry/) ? true : false; 1900 | $.os.blackberry10 = $.os.blackberry && userAgent.match(/Safari\/536/) ? true : false; 1901 | $.os.chrome = userAgent.match(/Chrome/) ? true : false; 1902 | $.os.opera = userAgent.match(/Opera/) ? true : false; 1903 | $.os.fennec = userAgent.match(/fennec/i) ? true : userAgent.match(/Firefox/) ? true : false; 1904 | $.os.ie = userAgent.match(/MSIE 10.0/i) ? true : false; 1905 | $.os.ieTouch = $.os.ie && userAgent.toLowerCase().match(/touch/i) ? true : false; 1906 | $.os.supportsTouch = ((window.DocumentTouch && document instanceof window.DocumentTouch) || 'ontouchstart' in window); 1907 | //features 1908 | $.feat = {}; 1909 | var head = document.documentElement.getElementsByTagName("head")[0]; 1910 | $.feat.nativeTouchScroll = typeof(head.style["-webkit-overflow-scrolling"]) !== "undefined" && $.os.ios; 1911 | $.feat.cssPrefix = $.os.webkit ? "Webkit" : $.os.fennec ? "Moz" : $.os.ie ? "ms" : $.os.opera ? "O" : ""; 1912 | $.feat.cssTransformStart = !$.os.opera ? "3d(" : "("; 1913 | $.feat.cssTransformEnd = !$.os.opera ? ",0)" : ")"; 1914 | if ($.os.android && !$.os.webkit) 1915 | $.os.android = false; 1916 | } 1917 | 1918 | detectUA($, navigator.userAgent); 1919 | $.__detectUA = detectUA; //needed for unit tests 1920 | 1921 | /** 1922 | * Utility function to create a psuedo GUID 1923 | ``` 1924 | var id= $.uuid(); 1925 | ``` 1926 | * @title $.uuid 1927 | */ 1928 | $.uuid = function() { 1929 | var S4 = function() { 1930 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 1931 | }; 1932 | return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4()); 1933 | }; 1934 | 1935 | /** 1936 | * Gets the css matrix, or creates a fake one 1937 | ``` 1938 | $.getCssMatrix(domElement) 1939 | ``` 1940 | @returns matrix with postion 1941 | */ 1942 | $.getCssMatrix = function(ele) { 1943 | if (typeof(ele) === $afm) ele = ele.get(0); 1944 | if (ele == nundefined) return window.WebKitCSSMatrix || window.MSCSSMatrix || { 1945 | a: 0, 1946 | b: 0, 1947 | c: 0, 1948 | d: 0, 1949 | e: 0, 1950 | f: 0 1951 | }; 1952 | try { 1953 | if (window.WebKitCSSMatrix) 1954 | return new WebKitCSSMatrix(window.getComputedStyle(ele).webkitTransform); 1955 | else if (window.MSCSSMatrix) 1956 | return new MSCSSMatrix(window.getComputedStyle(ele).transform); 1957 | else { 1958 | //fake css matrix 1959 | var mat = window.getComputedStyle(ele)[$.feat.cssPrefix + 'Transform'].replace(/[^0-9\-.,]/g, '').split(','); 1960 | return { 1961 | a: +mat[0], 1962 | b: +mat[1], 1963 | c: +mat[2], 1964 | d: +mat[3], 1965 | e: +mat[4], 1966 | f: +mat[5] 1967 | }; 1968 | } 1969 | } catch (e) { 1970 | return { 1971 | a: 0, 1972 | b: 0, 1973 | c: 0, 1974 | d: 0, 1975 | e: 0, 1976 | f: 0 1977 | }; 1978 | } 1979 | }; 1980 | 1981 | /** 1982 | * $.create - a faster alertnative to $("
this is some text
"); 1983 | ``` 1984 | $.create("div",{id:'main',innerHTML:'this is some text'}); 1985 | $.create("
this is some text
"); 1986 | ``` 1987 | * @param {String} DOM Element type or html 1988 | * @param [{Object}] properties to apply to the element 1989 | * @return {Object} Returns an appframework object 1990 | * @title $.create(type,[params]) 1991 | */ 1992 | $.create = function(type, props) { 1993 | var elem; 1994 | var f = new $afm(); 1995 | if (props || type[0] !== "<") { 1996 | if (props.html) 1997 | props.innerHTML = props.html, delete props.html; 1998 | 1999 | elem = document.createElement(type); 2000 | for (var j in props) { 2001 | elem[j] = props[j]; 2002 | } 2003 | f[f.length++] = elem; 2004 | } else { 2005 | elem = document.createElement("div"); 2006 | if (isWin8) { 2007 | MSApp.execUnsafeLocalFunction(function() { 2008 | elem.innerHTML = selector.trim(); 2009 | }); 2010 | } else 2011 | elem.innerHTML = type; 2012 | _shimNodes(elem.childNodes, f); 2013 | } 2014 | return f; 2015 | }; 2016 | /** 2017 | * $.query - a faster alertnative to $("div"); 2018 | ``` 2019 | $.query(".panel"); 2020 | ``` 2021 | * @param {String} selector 2022 | * @param {Object} [context] 2023 | * @return {Object} Returns an appframework object 2024 | * @title $.query(selector,[context]) 2025 | */ 2026 | $.query = function(sel, what) { 2027 | if (!sel) 2028 | return new $afm(); 2029 | what = what || document; 2030 | var f = new $afm(); 2031 | return f.selector(sel, what); 2032 | }; 2033 | /** 2034 | Zepto.js events 2035 | @api private 2036 | */ 2037 | 2038 | //The following is modified from Zepto.js / events.js 2039 | //We've removed depricated events like .live and allow anonymous functions to be removed 2040 | var handlers = {}, 2041 | _afmid = 1; 2042 | /** 2043 | * Gets or sets the expando property on a javascript element 2044 | * Also increments the internal counter for elements; 2045 | * @param {Object} element 2046 | * @return {Int} afmid 2047 | * @api private 2048 | */ 2049 | 2050 | function afmid(element) { 2051 | return element._afmid || (element._afmid = _afmid++); 2052 | } 2053 | /** 2054 | * Searches through a local array that keeps track of event handlers for proxying. 2055 | * Since we listen for multiple events, we match up the event, function and selector. 2056 | * This is used to find, execute, remove proxied event functions 2057 | * @param {Object} element 2058 | * @param {String} [event] 2059 | * @param {Function} [function] 2060 | * @param {String|Object|Array} [selector] 2061 | * @return {Function|null} handler function or false if not found 2062 | * @api private 2063 | */ 2064 | 2065 | function findHandlers(element, event, fn, selector) { 2066 | event = parse(event); 2067 | if (event.ns) 2068 | var matcher = matcherFor(event.ns); 2069 | return (handlers[afmid(element)] || []).filter(function(handler) { 2070 | return handler && (!event.e || handler.e == event.e) && (!event.ns || matcher.test(handler.ns)) && (!fn || handler.fn == fn || (typeof handler.fn === 'function' && typeof fn === 'function' && "" + handler.fn === "" + fn)) && (!selector || handler.sel == selector); 2071 | }); 2072 | } 2073 | /** 2074 | * Splits an event name by "." to look for namespaces (e.g touch.click) 2075 | * @param {String} event 2076 | * @return {Object} an object with the event name and namespace 2077 | * @api private 2078 | */ 2079 | 2080 | function parse(event) { 2081 | var parts = ('' + event).split('.'); 2082 | return { 2083 | e: parts[0], 2084 | ns: parts.slice(1).sort().join(' ') 2085 | }; 2086 | } 2087 | /** 2088 | * Regular expression checker for event namespace checking 2089 | * @param {String} namespace 2090 | * @return {Regex} regular expression 2091 | * @api private 2092 | */ 2093 | 2094 | function matcherFor(ns) { 2095 | return new RegExp('(?:^| )' + ns.replace(' ', ' .* ?') + '(?: |$)'); 2096 | } 2097 | 2098 | /** 2099 | * Utility function that will loop through events that can be a hash or space delimited and executes the function 2100 | * @param {String|Object} events 2101 | * @param {Function} fn 2102 | * @param {Iterator} [iterator] 2103 | * @api private 2104 | */ 2105 | 2106 | function eachEvent(events, fn, iterator) { 2107 | if ($.isObject(events)) 2108 | $.each(events, iterator); 2109 | else 2110 | events.split(/\s/).forEach(function(type) { 2111 | iterator(type, fn); 2112 | }); 2113 | } 2114 | 2115 | /** 2116 | * Helper function for adding an event and creating the proxy handler function. 2117 | * All event handlers call this to wire event listeners up. We create proxy handlers so they can be removed then. 2118 | * This is needed for delegate/on 2119 | * @param {Object} element 2120 | * @param {String|Object} events 2121 | * @param {Function} function that will be executed when event triggers 2122 | * @param {String|Array|Object} [selector] 2123 | * @param {Function} [getDelegate] 2124 | * @api private 2125 | */ 2126 | 2127 | function add(element, events, fn, selector, getDelegate) { 2128 | 2129 | var id = afmid(element), 2130 | set = (handlers[id] || (handlers[id] = [])); 2131 | eachEvent(events, fn, function(event, fn) { 2132 | var delegate = getDelegate && getDelegate(fn, event), 2133 | callback = delegate || fn; 2134 | var proxyfn = function(event) { 2135 | var result = callback.apply(element, [event].concat(event.data)); 2136 | if (result === false) 2137 | event.preventDefault(); 2138 | return result; 2139 | }; 2140 | var handler = $.extend(parse(event), { 2141 | fn: fn, 2142 | proxy: proxyfn, 2143 | sel: selector, 2144 | del: delegate, 2145 | i: set.length 2146 | }); 2147 | set.push(handler); 2148 | element.addEventListener(handler.e, proxyfn, false); 2149 | }); 2150 | //element=null; 2151 | } 2152 | 2153 | /** 2154 | * Helper function to remove event listeners. We look through each event and then the proxy handler array to see if it exists 2155 | * If found, we remove the listener and the entry from the proxy array. If no function is specified, we remove all listeners that match 2156 | * @param {Object} element 2157 | * @param {String|Object} events 2158 | * @param {Function} [fn] 2159 | * @param {String|Array|Object} [selector] 2160 | * @api private 2161 | */ 2162 | 2163 | function remove(element, events, fn, selector) { 2164 | 2165 | var id = afmid(element); 2166 | eachEvent(events || '', fn, function(event, fn) { 2167 | findHandlers(element, event, fn, selector).forEach(function(handler) { 2168 | delete handlers[id][handler.i]; 2169 | element.removeEventListener(handler.e, handler.proxy, false); 2170 | }); 2171 | }); 2172 | } 2173 | 2174 | $.event = { 2175 | add: add, 2176 | remove: remove 2177 | }; 2178 | 2179 | /** 2180 | * Binds an event to each element in the collection and executes the callback 2181 | ``` 2182 | $().bind('click',function(){console.log('I clicked '+this.id);}); 2183 | ``` 2184 | 2185 | * @param {String|Object} event 2186 | * @param {Function} callback 2187 | * @return {Object} appframework object 2188 | * @title $().bind(event,callback) 2189 | */ 2190 | $.fn.bind = function(event, callback) { 2191 | for (var i = 0; i < this.length; i++) { 2192 | add(this[i], event, callback); 2193 | } 2194 | return this; 2195 | }; 2196 | /** 2197 | * Unbinds an event to each element in the collection. If a callback is passed in, we remove just that one, otherwise we remove all callbacks for those events 2198 | ``` 2199 | $().unbind('click'); //Unbinds all click events 2200 | $().unbind('click',myFunc); //Unbinds myFunc 2201 | ``` 2202 | 2203 | * @param {String|Object} event 2204 | * @param {Function} [callback] 2205 | * @return {Object} appframework object 2206 | * @title $().unbind(event,[callback]); 2207 | */ 2208 | $.fn.unbind = function(event, callback) { 2209 | for (var i = 0; i < this.length; i++) { 2210 | remove(this[i], event, callback); 2211 | } 2212 | return this; 2213 | }; 2214 | 2215 | /** 2216 | * Binds an event to each element in the collection that will only execute once. When it executes, we remove the event listener then right away so it no longer happens 2217 | ``` 2218 | $().one('click',function(){console.log('I was clicked once');}); 2219 | ``` 2220 | 2221 | * @param {String|Object} event 2222 | * @param {Function} [callback] 2223 | * @return appframework object 2224 | * @title $().one(event,callback); 2225 | */ 2226 | $.fn.one = function(event, callback) { 2227 | return this.each(function(i, element) { 2228 | add(this, event, callback, null, function(fn, type) { 2229 | return function() { 2230 | var result = fn.apply(element, arguments); 2231 | remove(element, type, fn); 2232 | return result; 2233 | }; 2234 | }); 2235 | }); 2236 | }; 2237 | 2238 | /** 2239 | * internal variables 2240 | * @api private 2241 | */ 2242 | 2243 | var returnTrue = function() { 2244 | return true; 2245 | }; 2246 | var returnFalse = function() { 2247 | return false; 2248 | }; 2249 | var eventMethods = { 2250 | preventDefault: 'isDefaultPrevented', 2251 | stopImmediatePropagation: 'isImmediatePropagationStopped', 2252 | stopPropagation: 'isPropagationStopped' 2253 | }; 2254 | /** 2255 | * Creates a proxy function for event handlers. 2256 | * As "some" browsers dont support event.stopPropagation this call is bypassed if it cant be found on the event object. 2257 | * @param {String} event 2258 | * @return {Function} proxy 2259 | * @api private 2260 | */ 2261 | 2262 | function createProxy(event) { 2263 | var proxy = $.extend({ 2264 | originalEvent: event 2265 | }, event); 2266 | $.each(eventMethods, function(name, predicate) { 2267 | proxy[name] = function() { 2268 | this[predicate] = returnTrue; 2269 | if (name == "stopImmediatePropagation" || name == "stopPropagation") { 2270 | event.cancelBubble = true; 2271 | if (!event[name]) 2272 | return; 2273 | } 2274 | return event[name].apply(event, arguments); 2275 | }; 2276 | proxy[predicate] = returnFalse; 2277 | }); 2278 | return proxy; 2279 | } 2280 | 2281 | /** 2282 | * Delegate an event based off the selector. The event will be registered at the parent level, but executes on the selector. 2283 | ``` 2284 | $("#div").delegate("p",'click',callback); 2285 | ``` 2286 | 2287 | * @param {String|Array|Object} selector 2288 | * @param {String|Object} event 2289 | * @param {Function} callback 2290 | * @return {Object} appframework object 2291 | * @title $().delegate(selector,event,callback) 2292 | */ 2293 | $.fn.delegate = function(selector, event, callback) { 2294 | for (var i = 0; i < this.length; i++) { 2295 | var element = this[i]; 2296 | add(element, event, callback, selector, function(fn) { 2297 | return function(e) { 2298 | var evt, match = $(e.target).closest(selector, element).get(0); 2299 | if (match) { 2300 | evt = $.extend(createProxy(e), { 2301 | currentTarget: match, 2302 | liveFired: element 2303 | }); 2304 | return fn.apply(match, [evt].concat([].slice.call(arguments, 1))); 2305 | } 2306 | }; 2307 | }); 2308 | } 2309 | return this; 2310 | }; 2311 | 2312 | /** 2313 | * Unbinds events that were registered through delegate. It acts upon the selector and event. If a callback is specified, it will remove that one, otherwise it removes all of them. 2314 | ``` 2315 | $("#div").undelegate("p",'click',callback);//Undelegates callback for the click event 2316 | $("#div").undelegate("p",'click');//Undelegates all click events 2317 | ``` 2318 | 2319 | * @param {String|Array|Object} selector 2320 | * @param {String|Object} event 2321 | * @param {Function} callback 2322 | * @return {Object} appframework object 2323 | * @title $().undelegate(selector,event,[callback]); 2324 | */ 2325 | $.fn.undelegate = function(selector, event, callback) { 2326 | for (var i = 0; i < this.length; i++) { 2327 | remove(this[i], event, callback, selector); 2328 | } 2329 | return this; 2330 | }; 2331 | 2332 | /** 2333 | * Similar to delegate, but the function parameter order is easier to understand. 2334 | * If selector is undefined or a function, we just call .bind, otherwise we use .delegate 2335 | ``` 2336 | $("#div").on("click","p",callback); 2337 | ``` 2338 | 2339 | * @param {String|Array|Object} selector 2340 | * @param {String|Object} event 2341 | * @param {Function} callback 2342 | * @return {Object} appframework object 2343 | * @title $().on(event,selector,callback); 2344 | */ 2345 | $.fn.on = function(event, selector, callback) { 2346 | return selector === nundefined || $.isFunction(selector) ? this.bind(event, selector) : this.delegate(selector, event, callback); 2347 | }; 2348 | /** 2349 | * Removes event listeners for .on() 2350 | * If selector is undefined or a function, we call unbind, otherwise it's undelegate 2351 | ``` 2352 | $().off("click","p",callback); //Remove callback function for click events 2353 | $().off("click","p") //Remove all click events 2354 | ``` 2355 | 2356 | * @param {String|Object} event 2357 | * @param {String|Array|Object} selector 2358 | * @param {Sunction} callback 2359 | * @return {Object} appframework object 2360 | * @title $().off(event,selector,[callback]) 2361 | */ 2362 | $.fn.off = function(event, selector, callback) { 2363 | return selector === nundefined || $.isFunction(selector) ? this.unbind(event, selector) : this.undelegate(selector, event, callback); 2364 | }; 2365 | 2366 | /** 2367 | This triggers an event to be dispatched. Usefull for emulating events, etc. 2368 | ``` 2369 | $().trigger("click",{foo:'bar'});//Trigger the click event and pass in data 2370 | ``` 2371 | 2372 | * @param {String|Object} event 2373 | * @param {Object} [data] 2374 | * @return {Object} appframework object 2375 | * @title $().trigger(event,data); 2376 | */ 2377 | $.fn.trigger = function(event, data, props) { 2378 | if (typeof event == 'string') 2379 | event = $.Event(event, props); 2380 | event.data = data; 2381 | for (var i = 0; i < this.length; i++) { 2382 | this[i].dispatchEvent(event); 2383 | } 2384 | return this; 2385 | }; 2386 | 2387 | /** 2388 | * Creates a custom event to be used internally. 2389 | * @param {String} type 2390 | * @param {Object} [properties] 2391 | * @return {event} a custom event that can then be dispatched 2392 | * @title $.Event(type,props); 2393 | */ 2394 | 2395 | $.Event = function(type, props) { 2396 | var event = document.createEvent('Events'), 2397 | bubbles = true; 2398 | if (props) 2399 | for (var name in props) 2400 | (name == 'bubbles') ? (bubbles = !! props[name]) : (event[name] = props[name]); 2401 | event.initEvent(type, bubbles, true, null, null, null, null, null, null, null, null, null, null, null, null); 2402 | return event; 2403 | }; 2404 | 2405 | /* The following are for events on objects */ 2406 | /** 2407 | * Bind an event to an object instead of a DOM Node 2408 | ``` 2409 | $.bind(this,'event',function(){}); 2410 | ``` 2411 | * @param {Object} object 2412 | * @param {String} event name 2413 | * @param {Function} function to execute 2414 | * @title $.bind(object,event,function); 2415 | */ 2416 | $.bind = function(obj, ev, f) { 2417 | if (!obj) return; 2418 | if (!obj.__events) obj.__events = {}; 2419 | if (!$.isArray(ev)) ev = [ev]; 2420 | for (var i = 0; i < ev.length; i++) { 2421 | if (!obj.__events[ev[i]]) obj.__events[ev[i]] = []; 2422 | obj.__events[ev[i]].push(f); 2423 | } 2424 | }; 2425 | 2426 | /** 2427 | * Trigger an event to an object instead of a DOM Node 2428 | ``` 2429 | $.trigger(this,'event',arguments); 2430 | ``` 2431 | * @param {Object} object 2432 | * @param {String} event name 2433 | * @param {Array} arguments 2434 | * @title $.trigger(object,event,argments); 2435 | */ 2436 | $.trigger = function(obj, ev, args) { 2437 | if (!obj) return; 2438 | var ret = true; 2439 | if (!obj.__events) return ret; 2440 | if (!$.isArray(ev)) ev = [ev]; 2441 | if (!$.isArray(args)) args = []; 2442 | for (var i = 0; i < ev.length; i++) { 2443 | if (obj.__events[ev[i]]) { 2444 | var evts = obj.__events[ev[i]]; 2445 | for (var j = 0; j < evts.length; j++) 2446 | if ($.isFunction(evts[j]) && evts[j].apply(obj, args) === false) 2447 | ret = false; 2448 | } 2449 | } 2450 | return ret; 2451 | }; 2452 | /** 2453 | * Unbind an event to an object instead of a DOM Node 2454 | ``` 2455 | $.unbind(this,'event',function(){}); 2456 | ``` 2457 | * @param {Object} object 2458 | * @param {String} event name 2459 | * @param {Function} function to execute 2460 | * @title $.unbind(object,event,function); 2461 | */ 2462 | $.unbind = function(obj, ev, f) { 2463 | if (!obj.__events) return; 2464 | if (!$.isArray(ev)) ev = [ev]; 2465 | for (var i = 0; i < ev.length; i++) { 2466 | if (obj.__events[ev[i]]) { 2467 | var evts = obj.__events[ev[i]]; 2468 | for (var j = 0; j < evts.length; j++) { 2469 | if (f == nundefined) 2470 | delete evts[j]; 2471 | if (evts[j] == f) { 2472 | evts.splice(j, 1); 2473 | break; 2474 | } 2475 | } 2476 | } 2477 | } 2478 | }; 2479 | 2480 | 2481 | /** 2482 | * Creates a proxy function so you can change the 'this' context in the function 2483 | * Update: now also allows multiple argument call or for you to pass your own arguments 2484 | ``` 2485 | var newObj={foo:bar} 2486 | $("#main").bind("click",$.proxy(function(evt){console.log(this)},newObj); 2487 | 2488 | or 2489 | 2490 | ( $.proxy(function(foo, bar){console.log(this+foo+bar)}, newObj) )('foo', 'bar'); 2491 | 2492 | or 2493 | 2494 | ( $.proxy(function(foo, bar){console.log(this+foo+bar)}, newObj, ['foo', 'bar']) )(); 2495 | ``` 2496 | * @param {Function} Callback 2497 | * @param {Object} Context 2498 | * @title $.proxy(callback,context); 2499 | */ 2500 | $.proxy = function(f, c, args) { 2501 | return function() { 2502 | if (args) return f.apply(c, args); //use provided arguments 2503 | return f.apply(c, arguments); //use scope function call arguments 2504 | }; 2505 | }; 2506 | 2507 | 2508 | /** 2509 | * Removes listeners on a div and its children recursively 2510 | ``` 2511 | cleanUpNode(node,kill) 2512 | ``` 2513 | * @param {HTMLDivElement} the element to clean up recursively 2514 | * @api private 2515 | */ 2516 | 2517 | function cleanUpNode(node, kill) { 2518 | //kill it before it lays eggs! 2519 | if (kill && node.dispatchEvent) { 2520 | 2521 | var e = $.Event('destroy', { 2522 | bubbles: false 2523 | }); 2524 | node.dispatchEvent(e); 2525 | } 2526 | //cleanup itself 2527 | var id = afmid(node); 2528 | if (id && handlers[id]) { 2529 | for (var key in handlers[id]) 2530 | node.removeEventListener(handlers[id][key].e, handlers[id][key].proxy, false); 2531 | delete handlers[id]; 2532 | } 2533 | } 2534 | 2535 | function cleanUpContent(node, kill) { 2536 | if (!node) return; 2537 | //cleanup children 2538 | var children = node.childNodes; 2539 | if (children && children.length > 0) 2540 | for (var child in children) 2541 | cleanUpContent(children[child], kill); 2542 | 2543 | cleanUpNode(node, kill); 2544 | } 2545 | var cleanUpAsap = function(els, kill) { 2546 | for (var i = 0; i < els.length; i++) { 2547 | cleanUpContent(els[i], kill); 2548 | } 2549 | }; 2550 | 2551 | /** 2552 | * Function to clean up node content to prevent memory leaks 2553 | ``` 2554 | $.cleanUpContent(node,itself,kill) 2555 | ``` 2556 | * @param {HTMLNode} node 2557 | * @param {Bool} kill itself 2558 | * @param {bool} Kill nodes 2559 | * @title $.cleanUpContent(node,itself,kill) 2560 | */ 2561 | $.cleanUpContent = function(node, itself, kill) { 2562 | if (!node) return; 2563 | //cleanup children 2564 | var cn = node.childNodes; 2565 | if (cn && cn.length > 0) { 2566 | //destroy everything in a few ms to avoid memory leaks 2567 | //remove them all and copy objs into new array 2568 | $.asap(cleanUpAsap, {}, [slice.apply(cn, [0]), kill]); 2569 | } 2570 | //cleanUp this node 2571 | if (itself) cleanUpNode(node, kill); 2572 | }; 2573 | 2574 | // Like setTimeout(fn, 0); but much faster 2575 | var timeouts = []; 2576 | var contexts = []; 2577 | var params = []; 2578 | /** 2579 | * This adds a command to execute in the JS stack, but is faster then setTimeout 2580 | ``` 2581 | $.asap(function,context,args) 2582 | ``` 2583 | * @param {Function} function 2584 | * @param {Object} context 2585 | * @param {Array} arguments 2586 | */ 2587 | $.asap = function(fn, context, args) { 2588 | if (!$.isFunction(fn)) throw "$.asap - argument is not a valid function"; 2589 | timeouts.push(fn); 2590 | contexts.push(context ? context : {}); 2591 | params.push(args ? args : []); 2592 | //post a message to ourselves so we know we have to execute a function from the stack 2593 | window.postMessage("afm-asap", "*"); 2594 | }; 2595 | window.addEventListener("message", function(event) { 2596 | if (event.source == window && event.data == "afm-asap") { 2597 | event.stopPropagation(); 2598 | if (timeouts.length > 0) { //just in case... 2599 | (timeouts.shift()).apply(contexts.shift(), params.shift()); 2600 | } 2601 | } 2602 | }, true); 2603 | 2604 | /** 2605 | * this function executes javascript in HTML. 2606 | ``` 2607 | $.parseJS(content) 2608 | ``` 2609 | * @param {String|DOM} content 2610 | * @title $.parseJS(content); 2611 | */ 2612 | var remoteJSPages = {}; 2613 | $.parseJS = function(div) { 2614 | if (!div) 2615 | return; 2616 | if (typeof(div) == "string") { 2617 | var elem = document.createElement("div"); 2618 | if (isWin8) { 2619 | MSApp.execUnsafeLocalFunction(function() { 2620 | elem.innerHTML = div; 2621 | }); 2622 | } else 2623 | elem.innerHTML = div; 2624 | 2625 | div = elem; 2626 | } 2627 | var scripts = div.getElementsByTagName("script"); 2628 | div = null; 2629 | for (var i = 0; i < scripts.length; i++) { 2630 | if (scripts[i].src.length > 0 && !remoteJSPages[scripts[i].src] && !isWin8) { 2631 | var doc = document.createElement("script"); 2632 | doc.type = scripts[i].type; 2633 | doc.src = scripts[i].src; 2634 | document.getElementsByTagName('head')[0].appendChild(doc); 2635 | remoteJSPages[scripts[i].src] = 1; 2636 | doc = null; 2637 | } else { 2638 | window['eval'](scripts[i].innerHTML); 2639 | } 2640 | } 2641 | }; 2642 | 2643 | 2644 | /** 2645 | //custom events since people want to do $().click instead of $().bind("click") 2646 | */ 2647 | 2648 | ["click", "keydown", "keyup", "keypress", "submit", "load", "resize", "change", "select", "error"].forEach(function(event) { 2649 | $.fn[event] = function(cb) { 2650 | return cb ? this.bind(event, cb) : this.trigger(event); 2651 | }; 2652 | }); 2653 | 2654 | 2655 | ['focus', 'blur'].forEach(function(name) { 2656 | $.fn[name] = function(callback) { 2657 | if (this.length === 0) return; 2658 | if (callback) 2659 | this.bind(name, callback); 2660 | else 2661 | for (var i = 0; i < this.length; i++) { 2662 | try { 2663 | this[i][name](); 2664 | } catch (e) {} 2665 | } 2666 | return this; 2667 | }; 2668 | }); 2669 | 2670 | /** 2671 | * End of APIS 2672 | * @api private 2673 | */ 2674 | return $; 2675 | 2676 | })(window); 2677 | window.jq = af; //backwards compat 2678 | '$' in window || (window.$ = af); 2679 | if (typeof define === "function" && define.amd) { 2680 | define("appframework", [], function() { 2681 | return af; 2682 | }); 2683 | } else if (typeof module !== 'undefined' && module.exports) { 2684 | module.exports.af = af; 2685 | module.exports.$ = af; 2686 | } 2687 | //Helper function used in af.mobi.plugins. 2688 | if (!window.numOnly) { 2689 | window.numOnly = function numOnly(val) { 2690 | if (val === undefined || val === '') return 0; 2691 | if (isNaN(parseFloat(val))) { 2692 | if (val.replace) { 2693 | val = val.replace(/[^0-9.-]/g, ""); 2694 | } else return 0; 2695 | } 2696 | return parseFloat(val); 2697 | }; 2698 | } 2699 | } --------------------------------------------------------------------------------