1 (function (GCN) { 2 3 'use strict'; 4 5 /** 6 * Only grows, never shrinks. 7 * @private 8 * @type {number} 9 */ 10 var uniqueIdCounter = 0; 11 12 /** 13 * Generates a unique id with an optional prefix. 14 * 15 * The returned value is only unique among other returned values, 16 * not globally. 17 * 18 * @public 19 * @param {string} 20 * Optional prefix for the id to be generated. 21 * @return {string} 22 * Never the same string more than once. 23 */ 24 function uniqueId(prefix) { 25 return (prefix || '') + (++uniqueIdCounter); 26 } 27 28 function escapePropertyName(name) { 29 return name.replace(/\./g, '\\.'); 30 } 31 32 /** 33 * A regular expression to identify executable script tags. 34 * 35 * @type {RegExp} 36 */ 37 var rgxpScriptType = /\/(java|ecma)script/i; 38 39 /** 40 * A regular expression to find script tags. 41 * 42 * @type {RegExp} 43 */ 44 var rgxpScriptTag = new RegExp('<script(\\s[^>]*?)?>', 'ig'); 45 46 /** 47 * A regular expression to find script types. 48 * 49 * @type {RegExp} 50 */ 51 var rgxpType = new RegExp( 52 ' type\\s*=\\s*' 53 + '[\\"\\\']' 54 + '([^\\"\\\']*[^\\s][^\\"\\\']*)' 55 + '[\\"\\\']', 56 'i' 57 ); 58 59 var rand = Math.random().toString().replace('.', ''); 60 61 /** 62 * Places sentinal strings in front of every executable script tag. 63 * 64 * @param {string} html HTML markup 65 * @return {string} HTML string with marked <script> tags. 66 */ 67 function markScriptTagLocations(html) { 68 var i = 0; 69 return html.replace(rgxpScriptTag, function (str, substr, offset) { 70 var type = substr && substr.match(rgxpType); 71 if (!type || rgxpScriptType.test(type)) { 72 return rand + (i++) + str; 73 } 74 return str; 75 }); 76 } 77 78 /** 79 * Replaces the type attribute of <script> tags with a value that will 80 * protect them from being specially handled by jQuery. 81 * 82 * @param {string} html Markup 83 * @return {string} Markup with <script> tags protected from jQuery. 84 */ 85 function protectScriptTags(html, $scripts) { 86 var i; 87 var $script; 88 var type; 89 for (i = 0; i < $scripts.length; i++) { 90 $script = $scripts.eq(i).clone(); 91 type = $script.attr('type'); 92 if (!type || rgxpScriptType.test(type)) { 93 $script.attr('type', rand).attr('data-origtype', type); 94 html = html.replace(rand + i, $script[0].outerHTML); 95 } 96 } 97 return html; 98 } 99 100 /** 101 * Restores the type attribute for <script> tags that have been processed 102 * via protectScriptTags(). 103 * 104 * @param {jQuery.<HTMLElement>} $element Root HTMLElement in which to 105 * restore <script> tags. 106 */ 107 function restoreScriptTagTypes($element) { 108 var $scripts = $element.find('script[type="' + rand + '"]'); 109 var $script; 110 var orig; 111 var i; 112 for (i = 0; i < $scripts.length; i++) { 113 $script = $scripts.eq(i); 114 $script.removeClass(rand + i); 115 orig = $script.attr('data-origtype'); 116 if (typeof orig === 'string') { 117 $script.attr('type', orig).removeAttr('data-origtype'); 118 } else { 119 $script.removeAttr('type'); 120 } 121 } 122 } 123 124 /** 125 * Joins the innerHTML of multiple elements. 126 * 127 * @param {jQuery.<HTMLElement>} $elements 128 */ 129 function joinContents($elements) { 130 var contents = ''; 131 $elements.each(function () { 132 contents += jQuery(this).html(); 133 }); 134 return contents; 135 } 136 137 /** 138 * Inserts the inner HTML of the given HTML markup while preserving 139 * <script> tags, and still allowing jQuery to execute them. 140 * 141 * @param {jQuery.<HTMLElement>} $element 142 * @param {string} html 143 */ 144 function insertInnerHTMLWithScriptTags($element, html) { 145 if (!rgxpScriptTag.test(html)) { 146 $element.html(jQuery(html).contents()); 147 return; 148 } 149 var marked = markScriptTagLocations(html); 150 var $scripts = jQuery(html).filter('script'); 151 var $html = jQuery(marked); 152 var contents = joinContents($html.filter(':not(script)')); 153 contents = protectScriptTags(contents, $scripts); 154 $element.html(contents).append($scripts); 155 restoreScriptTagTypes($element); 156 } 157 158 GCN.uniqueId = uniqueId; 159 GCN.escapePropertyName = escapePropertyName; 160 GCN.insertInnerHTMLWithScriptTags = insertInnerHTMLWithScriptTags; 161 162 }(GCN)); 163