1 /** 2 * ecma5schims.js - Shim for ECMA5 compatibility 3 * (http://en.wikipedia.org/wiki/Shim_%28computing%29) 4 * 5 * A shim library that implements common functions that are missing on some 6 * environments in order to complete ECMA5 compatibility across all major 7 * browsers. 8 * 9 * TODO: This code needs to be refactored so as to conform to Aloha coding 10 * standards. It is also severly lacking in documentation. Please take 11 * note of: https://github.com/alohaeditor/Aloha-Editor/wiki/Commit-Checklist . 12 */ 13 14 define([], function(){ 15 'use strict'; 16 17 var shims = { 18 // Function bind 19 bind: function(owner){ 20 var obj = this.obj || this; 21 var native_method = Function.prototype.bind; 22 var args= Array.prototype.slice.call(arguments, 1); 23 24 if(native_method){ 25 return native_method.apply(obj, arguments); 26 } 27 else{ 28 return function() { 29 return obj.apply(owner, arguments.length===0? args : args.concat(Array.prototype.slice.call(arguments))); 30 } 31 } 32 }, 33 34 // String trim 35 trim: function(){ 36 var obj = this.obj || this; 37 var native_method = String.prototype.trim; 38 39 if(native_method){ 40 return native_method.call(obj); 41 } 42 else { 43 return obj.replace(/^\s+/, '').replace(/\s+$/, ''); 44 } 45 }, 46 47 // Array methods 48 indexOf: function(find, i /*opt*/){ 49 var obj = this.obj || this; 50 var native_method = Array.prototype.indexOf; 51 52 if(native_method){ 53 return native_method.call(obj, find, i); 54 } 55 else { 56 if (i===undefined) i= 0; 57 if (i<0) i+= obj.length; 58 if (i<0) i= 0; 59 for (var n = obj.length; i<n; i++) 60 if (i in obj && obj[i]===find) 61 return i; 62 return -1; 63 } 64 }, 65 66 forEach: function(action, that /*opt*/){ 67 var obj = this.obj || this; 68 var native_method = Array.prototype.forEach; 69 70 if(native_method){ 71 return native_method.call(obj, action, that); 72 } 73 else { 74 for (var i= 0, n = obj.length; i<n; i++) 75 if (i in obj) 76 action.call(that, obj[i], i, obj); 77 } 78 }, 79 80 map: function(mapper, that /*opt*/, chain /*opt */){ 81 var obj = this.obj || this; 82 var native_method = Array.prototype.map; 83 var returnWrapper = (typeof arguments[arguments.length - 1] == "boolean") ? Array.prototype.pop.call(arguments) : false; 84 var result = []; 85 86 if(native_method){ 87 result = native_method.call(obj, mapper, that); 88 } 89 else { 90 var other= new Array(obj.length); 91 for (var i= 0, n= obj.length; i<n; i++) 92 if (i in obj) 93 other[i]= mapper.call(that, obj[i], i, obj); 94 result = other; 95 } 96 97 return returnWrapper ? $_(result) : result; 98 }, 99 100 filter: function(filterFunc, that /*opt*/, chain /*opt */){ 101 var obj = this.obj || this; 102 var native_method = Array.prototype.filter; 103 var returnWrapper = (typeof arguments[arguments.length - 1] == "boolean") ? Array.prototype.pop.call(arguments) : false; 104 var result = []; 105 106 if(native_method){ 107 result = native_method.call(obj, filterFunc, that); 108 } 109 else { 110 var other= [], v; 111 for (var i=0, n= obj.length; i<n; i++) 112 if (i in obj && filterFunc.call(that, v= obj[i], i, obj)) 113 other.push(v); 114 result = other; 115 } 116 117 return returnWrapper ? $_(result) : result; 118 }, 119 120 every: function(tester, that /*opt*/) { 121 var obj = this.obj || this; 122 var native_method = Array.prototype.every; 123 124 if(native_method){ 125 return native_method.call(obj, tester, that); 126 } 127 else { 128 for (var i= 0, n= obj.length; i<n; i++) 129 if (i in obj && !tester.call(that, obj[i], i, obj)) 130 return false; 131 return true; 132 } 133 }, 134 135 some: function(tester, that /*opt*/){ 136 var obj = this.obj || this; 137 var native_method = Array.prototype.some; 138 139 if(native_method){ 140 return native_method.call(obj, tester, that); 141 } 142 else { 143 for (var i= 0, n= obj.length; i<n; i++) 144 if (i in obj && tester.call(that, obj[i], i, obj)) 145 return true; 146 return false; 147 } 148 }, 149 150 // Since IE7 doesn't support 'hasAttribute' method on nodes 151 // TODO: raise an exception if the object is not an node 152 hasAttribute: function(attr){ 153 var obj = this.obj || this; 154 var native_method = obj.hasAttribute; 155 156 if(native_method){ 157 return obj.hasAttribute(attr); 158 } 159 else { 160 return !!obj.getAttribute(attr); 161 } 162 } 163 164 }; 165 166 var $_ = function(obj) { 167 var wrapper = function() {}; 168 wrapper.prototype = shims; 169 170 var wrapper_instance = new wrapper(); 171 wrapper_instance.obj = obj; 172 return wrapper_instance; 173 }; 174 175 for (var shim in shims) { 176 $_[shim] = shims[shim]; 177 } 178 179 180 // Node constants 181 // http://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-1841493061 182 if(typeof Node != 'undefined'){ 183 $_.Node = Node; 184 } 185 else { 186 $_.Node = { 187 'ELEMENT_NODE' : 1, 188 'ATTRIBUTE_NODE': 2, 189 'TEXT_NODE': 3, 190 'CDATA_SECTION_NODE': 4, 191 'ENTITY_REFERENCE_NODE': 5, 192 'ENTITY_NODE': 6, 193 'PROCESSING_INSTRUCTION_NODE': 7, 194 'COMMENT_NODE': 8, 195 'DOCUMENT_NODE': 9, 196 'DOCUMENT_TYPE_NODE': 10, 197 'DOCUMENT_FRAGMENT_NODE': 11, 198 'NOTATION_NODE': 12, 199 //The two nodes are disconnected. Order between disconnected nodes is always implementation-specific. 200 'DOCUMENT_POSITION_DISCONNECTED': 0x01, 201 //The second node precedes the reference node. 202 'DOCUMENT_POSITION_PRECEDING': 0x02, 203 //The node follows the reference node. 204 'DOCUMENT_POSITION_FOLLOWING': 0x04, 205 //The node contains the reference node. A node which contains is always preceding, too. 206 'DOCUMENT_POSITION_CONTAINS': 0x08, 207 //The node is contained by the reference node. A node which is contained is always following, too. 208 'DOCUMENT_POSITION_CONTAINED_BY': 0x10, 209 //The determination of preceding versus following is implementation-specific. 210 'DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC': 0x20 211 } 212 }; 213 214 // http://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-compareDocumentPosition 215 // FIXME: Check if the DOMNode prototype can be set. 216 $_.compareDocumentPosition = function(node1, node2) { 217 218 if ('compareDocumentPosition' in document.documentElement ) { 219 return node1.compareDocumentPosition(node2); 220 } 221 222 if (!("contains" in document.documentElement)) { 223 throw 'neither compareDocumentPosition nor contains is supported by this browser.'; 224 } 225 226 if (node1 == node2) return 0; 227 228 //if they don't have the same parent, there's a disconnect 229 if (getRootParent(node1) != getRootParent(node2)) return 1; 230 231 //use this if both nodes have a sourceIndex (text nodes don't) 232 if ("sourceIndex" in node1 && "sourceIndex" in node2) { 233 return comparePosition(node1, node2); 234 } 235 236 //document will definitely contain the other node 237 if (node1 == document) return 20; 238 else if (node2 == document) return 10; 239 240 //get sourceIndexes to use for both nodes 241 var useNode1 = getUseNode(node1), useNode2 = getUseNode(node2); 242 243 //call this function again to get the result 244 var result = comparePosition(useNode1, useNode2); 245 246 //clean up if needed 247 if (node1 != useNode1) useNode1.parentNode.removeChild(useNode1); 248 if (node2 != useNode2) useNode2.parentNode.removeChild(useNode2); 249 return result; 250 }; 251 252 //node.ownerDocument gives the document object, which isn't the right info for a disconnect 253 function getRootParent( node ) { 254 var parent = null; 255 256 if ( node ) { 257 do { parent = node; } 258 while ( node = node.parentNode ); 259 } 260 261 return parent; 262 } 263 264 //Compare Position - MIT Licensed, John Resig; http://ejohn.org/blog/comparing-document-position/ 265 //Already checked for equality and disconnect 266 function comparePosition(node1, node2) { 267 return (node1.contains(node2) && 16) + 268 (node2.contains(node1) && 8) + 269 (node1.sourceIndex >= 0 && node2.sourceIndex >= 0 ? 270 (node1.sourceIndex < node2.sourceIndex && 4) + 271 (node1.sourceIndex > node2.sourceIndex && 2) : 272 1); 273 } 274 275 //get a node with a sourceIndex to use 276 function getUseNode(node) { 277 //if the node already has a sourceIndex, use that node 278 if ("sourceIndex" in node) return node; 279 //otherwise, insert a comment (which has a sourceIndex but minimal DOM impact) before the node and use that 280 return node.parentNode.insertBefore(document.createComment(""), node); 281 } 282 283 $_.getComputedStyle = function(node, style){ 284 if('getComputedStyle' in window) { 285 return window.getComputedStyle(node, style); 286 287 } 288 else { 289 if( node.currentStyle ) { 290 return node.currentStyle; 291 } 292 return null; 293 } 294 }; 295 296 return $_; 297 }); 298