_base.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. /*
  2. Copyright (c) 2004-2012, The Dojo Foundation All Rights Reserved.
  3. Available via Academic Free License >= 2.1 OR the modified BSD license.
  4. see: http://dojotoolkit.org/license for details
  5. */
  6. if(!dojo._hasResource["dojox.html._base"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
  7. dojo._hasResource["dojox.html._base"] = true;
  8. /*
  9. Status: dont know where this will all live exactly
  10. Need to pull in the implementation of the various helper methods
  11. Some can be static method, others maybe methods of the ContentSetter (?)
  12. Gut the ContentPane, replace its _setContent with our own call to dojox.html.set()
  13. */
  14. dojo.provide("dojox.html._base");
  15. dojo.require("dojo.html");
  16. (function() {
  17. if(dojo.isIE){
  18. var alphaImageLoader = /(AlphaImageLoader\([^)]*?src=(['"]))(?![a-z]+:|\/)([^\r\n;}]+?)(\2[^)]*\)\s*[;}]?)/g;
  19. }
  20. // css at-rules must be set before any css declarations according to CSS spec
  21. // match:
  22. // @import 'http://dojotoolkit.org/dojo.css';
  23. // @import 'you/never/thought/' print;
  24. // @import url("it/would/work") tv, screen;
  25. // @import url(/did/you/now.css);
  26. // but not:
  27. // @namespace dojo "http://dojotoolkit.org/dojo.css"; /* namespace URL should always be a absolute URI */
  28. // @charset 'utf-8';
  29. // @media print{ #menuRoot {display:none;} }
  30. // we adjust all paths that dont start on '/' or contains ':'
  31. //(?![a-z]+:|\/)
  32. var cssPaths = /(?:(?:@import\s*(['"])(?![a-z]+:|\/)([^\r\n;{]+?)\1)|url\(\s*(['"]?)(?![a-z]+:|\/)([^\r\n;]+?)\3\s*\))([a-z, \s]*[;}]?)/g;
  33. var adjustCssPaths = dojox.html._adjustCssPaths = function(cssUrl, cssText){
  34. // summary:
  35. // adjusts relative paths in cssText to be relative to cssUrl
  36. // a path is considered relative if it doesn't start with '/' and not contains ':'
  37. // description:
  38. // Say we fetch a HTML page from level1/page.html
  39. // It has some inline CSS:
  40. // @import "css/page.css" tv, screen;
  41. // ...
  42. // background-image: url(images/aplhaimage.png);
  43. //
  44. // as we fetched this HTML and therefore this CSS
  45. // from level1/page.html, these paths needs to be adjusted to:
  46. // @import 'level1/css/page.css' tv, screen;
  47. // ...
  48. // background-image: url(level1/images/alphaimage.png);
  49. //
  50. // In IE it will also adjust relative paths in AlphaImageLoader()
  51. // filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='images/alphaimage.png');
  52. // will be adjusted to:
  53. // filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='level1/images/alphaimage.png');
  54. //
  55. // Please note that any relative paths in AlphaImageLoader in external css files wont work, as
  56. // the paths in AlphaImageLoader is MUST be declared relative to the HTML page,
  57. // not relative to the CSS file that declares it
  58. if(!cssText || !cssUrl){ return; }
  59. // support the ImageAlphaFilter if it exists, most people use it in IE 6 for transparent PNGs
  60. // We are NOT going to kill it in IE 7 just because the PNGs work there. Somebody might have
  61. // other uses for it.
  62. // If user want to disable css filter in IE6 he/she should
  63. // unset filter in a declaration that just IE 6 doesn't understands
  64. // like * > .myselector { filter:none; }
  65. if(alphaImageLoader){
  66. cssText = cssText.replace(alphaImageLoader, function(ignore, pre, delim, url, post){
  67. return pre + (new dojo._Url(cssUrl, './'+url).toString()) + post;
  68. });
  69. }
  70. return cssText.replace(cssPaths, function(ignore, delimStr, strUrl, delimUrl, urlUrl, media){
  71. if(strUrl){
  72. return '@import "' + (new dojo._Url(cssUrl, './'+strUrl).toString()) + '"' + media;
  73. }else{
  74. return 'url(' + (new dojo._Url(cssUrl, './'+urlUrl).toString()) + ')' + media;
  75. }
  76. });
  77. };
  78. // attributepaths one tag can have multiple paths, example:
  79. // <input src="..." style="url(..)"/> or <a style="url(..)" href="..">
  80. // <img style='filter:progid...AlphaImageLoader(src="noticeTheSrcHereRunsThroughHtmlSrc")' src="img">
  81. var htmlAttrPaths = /(<[a-z][a-z0-9]*\s[^>]*)(?:(href|src)=(['"]?)([^>]*?)\3|style=(['"]?)([^>]*?)\5)([^>]*>)/gi;
  82. var adjustHtmlPaths = dojox.html._adjustHtmlPaths = function(htmlUrl, cont){
  83. var url = htmlUrl || "./";
  84. return cont.replace(htmlAttrPaths,
  85. function(tag, start, name, delim, relUrl, delim2, cssText, end){
  86. return start + (name ?
  87. (name + '=' + delim + (new dojo._Url(url, relUrl).toString()) + delim)
  88. : ('style=' + delim2 + adjustCssPaths(url, cssText) + delim2)
  89. ) + end;
  90. }
  91. );
  92. };
  93. var snarfStyles = dojox.html._snarfStyles = function (/*String*/cssUrl, /*String*/cont, /*Array*/styles){
  94. /**************** cut out all <style> and <link rel="stylesheet" href=".."> **************/
  95. // also return any attributes from this tag (might be a media attribute)
  96. // if cssUrl is set it will adjust paths accordingly
  97. styles.attributes = [];
  98. return cont.replace(/(?:<style([^>]*)>([\s\S]*?)<\/style>|<link\s+(?=[^>]*rel=['"]?stylesheet)([^>]*?href=(['"])([^>]*?)\4[^>\/]*)\/?>)/gi,
  99. function(ignore, styleAttr, cssText, linkAttr, delim, href){
  100. // trim attribute
  101. var i, attr = (styleAttr||linkAttr||"").replace(/^\s*([\s\S]*?)\s*$/i, "$1");
  102. if(cssText){
  103. i = styles.push(cssUrl ? adjustCssPaths(cssUrl, cssText) : cssText);
  104. }else{
  105. i = styles.push('@import "' + href + '";');
  106. attr = attr.replace(/\s*(?:rel|href)=(['"])?[^\s]*\1\s*/gi, ""); // remove rel=... and href=...
  107. }
  108. if(attr){
  109. attr = attr.split(/\s+/);// split on both "\n", "\t", " " etc
  110. var atObj = {}, tmp;
  111. for(var j = 0, e = attr.length; j < e; j++){
  112. tmp = attr[j].split('='); // split name='value'
  113. atObj[tmp[0]] = tmp[1].replace(/^\s*['"]?([\s\S]*?)['"]?\s*$/, "$1"); // trim and remove ''
  114. }
  115. styles.attributes[i - 1] = atObj;
  116. }
  117. return "";
  118. }
  119. );
  120. };
  121. var snarfScripts = dojox.html._snarfScripts = function(cont, byRef){
  122. // summary
  123. // strips out script tags from cont
  124. // invoke with
  125. // byRef = {errBack:function(){/*add your download error code here*/, downloadRemote: true(default false)}}
  126. // byRef will have {code: 'jscode'} when this scope leaves
  127. byRef.code = "";
  128. //Update script tags nested in comments so that the script tag collector doesn't pick
  129. //them up.
  130. cont = cont.replace(/<[!][-][-](.|\s)*?[-][-]>/g,
  131. function(comment){
  132. return comment.replace(/<(\/?)script\b/ig,"&lt;$1Script");
  133. }
  134. );
  135. function download(src){
  136. if(byRef.downloadRemote){
  137. // console.debug('downloading',src);
  138. //Fix up src, in case there were entity character encodings in it.
  139. //Probably only need to worry about a subset.
  140. src = src.replace(/&([a-z0-9#]+);/g, function(m, name) {
  141. switch(name) {
  142. case "amp" : return "&";
  143. case "gt" : return ">";
  144. case "lt" : return "<";
  145. default:
  146. return name.charAt(0)=="#" ? String.fromCharCode(name.substring(1)) : "&"+name+";";
  147. }
  148. });
  149. dojo.xhrGet({
  150. url: src,
  151. sync: true,
  152. load: function(code){
  153. byRef.code += code+";";
  154. },
  155. error: byRef.errBack
  156. });
  157. }
  158. }
  159. // match <script>, <script type="text/..., but not <script type="dojo(/method)...
  160. return cont.replace(/<script\s*(?![^>]*type=['"]?(?:dojo\/|text\/html\b))(?:[^>]*?(?:src=(['"]?)([^>]*?)\1[^>]*)?)*>([\s\S]*?)<\/script>/gi,
  161. function(ignore, delim, src, code){
  162. if(src){
  163. download(src);
  164. }else{
  165. byRef.code += code;
  166. }
  167. return "";
  168. }
  169. );
  170. };
  171. var evalInGlobal = dojox.html.evalInGlobal = function(code, appendNode){
  172. // we do our own eval here as dojo.eval doesn't eval in global crossbrowser
  173. // This work X browser but but it relies on a DOM
  174. // plus it doesn't return anything, thats unrelevant here but not for dojo core
  175. appendNode = appendNode || dojo.doc.body;
  176. var n = appendNode.ownerDocument.createElement('script');
  177. n.type = "text/javascript";
  178. appendNode.appendChild(n);
  179. n.text = code; // DOM 1 says this should work
  180. };
  181. dojo.declare("dojox.html._ContentSetter", [dojo.html._ContentSetter], {
  182. // adjustPaths: Boolean
  183. // Adjust relative paths in html string content to point to this page
  184. // Only useful if you grab content from a another folder than the current one
  185. adjustPaths: false,
  186. referencePath: ".",
  187. renderStyles: false,
  188. executeScripts: false,
  189. scriptHasHooks: false,
  190. scriptHookReplacement: null,
  191. _renderStyles: function(styles){
  192. // insert css from content into document head
  193. this._styleNodes = [];
  194. var st, att, cssText, doc = this.node.ownerDocument;
  195. var head = doc.getElementsByTagName('head')[0];
  196. for(var i = 0, e = styles.length; i < e; i++){
  197. cssText = styles[i]; att = styles.attributes[i];
  198. st = doc.createElement('style');
  199. st.setAttribute("type", "text/css"); // this is required in CSS spec!
  200. for(var x in att){
  201. st.setAttribute(x, att[x]);
  202. }
  203. this._styleNodes.push(st);
  204. head.appendChild(st); // must insert into DOM before setting cssText
  205. if(st.styleSheet){ // IE
  206. st.styleSheet.cssText = cssText;
  207. }else{ // w3c
  208. st.appendChild(doc.createTextNode(cssText));
  209. }
  210. }
  211. },
  212. empty: function() {
  213. this.inherited("empty", arguments);
  214. // empty out the styles array from any previous use
  215. this._styles = [];
  216. },
  217. onBegin: function() {
  218. // summary
  219. // Called after instantiation, but before set();
  220. // It allows modification of any of the object properties - including the node and content
  221. // provided - before the set operation actually takes place
  222. // This implementation extends that of dojo.html._ContentSetter
  223. // to add handling for adjustPaths, renderStyles on the html string content before it is set
  224. this.inherited("onBegin", arguments);
  225. var cont = this.content,
  226. node = this.node;
  227. var styles = this._styles;// init vars
  228. if(dojo.isString(cont)){
  229. if(this.adjustPaths && this.referencePath){
  230. cont = adjustHtmlPaths(this.referencePath, cont);
  231. }
  232. if(this.renderStyles || this.cleanContent){
  233. cont = snarfStyles(this.referencePath, cont, styles);
  234. }
  235. // because of a bug in IE, script tags that is first in html hierarchy doesnt make it into the DOM
  236. // when content is innerHTML'ed, so we can't use dojo.query to retrieve scripts from DOM
  237. if(this.executeScripts){
  238. var _t = this;
  239. var byRef = {
  240. downloadRemote: true,
  241. errBack:function(e){
  242. _t._onError.call(_t, 'Exec', 'Error downloading remote script in "'+_t.id+'"', e);
  243. }
  244. };
  245. cont = snarfScripts(cont, byRef);
  246. this._code = byRef.code;
  247. }
  248. }
  249. this.content = cont;
  250. },
  251. onEnd: function() {
  252. // summary
  253. // Called after set(), when the new content has been pushed into the node
  254. // It provides an opportunity for post-processing before handing back the node to the caller
  255. // This implementation extends that of dojo.html._ContentSetter
  256. var code = this._code,
  257. styles = this._styles;
  258. // clear old stylenodes from the DOM
  259. // these were added by the last set call
  260. // (in other words, if you dont keep and reuse the ContentSetter for a particular node
  261. // .. you'll have no practical way to do this)
  262. if(this._styleNodes && this._styleNodes.length){
  263. while(this._styleNodes.length){
  264. dojo.destroy(this._styleNodes.pop());
  265. }
  266. }
  267. // render new style nodes
  268. if(this.renderStyles && styles && styles.length){
  269. this._renderStyles(styles);
  270. }
  271. if(this.executeScripts && code){
  272. if(this.cleanContent){
  273. // clean JS from html comments and other crap that browser
  274. // parser takes care of in a normal page load
  275. code = code.replace(/(<!--|(?:\/\/)?-->|<!\[CDATA\[|\]\]>)/g, '');
  276. }
  277. if(this.scriptHasHooks){
  278. // replace _container_ with this.scriptHookReplace()
  279. // the scriptHookReplacement can be a string
  280. // or a function, which when invoked returns the string you want to substitute in
  281. code = code.replace(/_container_(?!\s*=[^=])/g, this.scriptHookReplacement);
  282. }
  283. try{
  284. evalInGlobal(code, this.node);
  285. }catch(e){
  286. this._onError('Exec', 'Error eval script in '+this.id+', '+e.message, e);
  287. }
  288. }
  289. this.inherited("onEnd", arguments);
  290. },
  291. tearDown: function() {
  292. this.inherited(arguments);
  293. delete this._styles;
  294. // only tear down -or another set() - will explicitly throw away the
  295. // references to the style nodes we added
  296. if(this._styleNodes && this._styleNodes.length){
  297. while(this._styleNodes.length){
  298. dojo.destroy(this._styleNodes.pop());
  299. }
  300. }
  301. delete this._styleNodes;
  302. // reset the defaults from the prototype
  303. dojo.mixin(this, dojo.getObject(this.declaredClass).prototype);
  304. }
  305. });
  306. dojox.html.set = function(/* DomNode */ node, /* String|DomNode|NodeList */ cont, /* Object? */ params){
  307. // TODO: add all the other options
  308. // summary:
  309. // inserts (replaces) the given content into the given node
  310. // node:
  311. // the parent element that will receive the content
  312. // cont:
  313. // the content to be set on the parent element.
  314. // This can be an html string, a node reference or a NodeList, dojo.NodeList, Array or other enumerable list of nodes
  315. // params:
  316. // Optional flags/properties to configure the content-setting. See dojo.html._ContentSetter
  317. // example:
  318. // A safe string/node/nodelist content replacement/injection with hooks for extension
  319. // Example Usage:
  320. // dojo.html.set(node, "some string");
  321. // dojo.html.set(node, contentNode, {options});
  322. // dojo.html.set(node, myNode.childNodes, {options});
  323. if(!params){
  324. // simple and fast
  325. return dojo.html._setNodeContent(node, cont, true);
  326. }else{
  327. // more options but slower
  328. var op = new dojox.html._ContentSetter(dojo.mixin(
  329. params,
  330. { content: cont, node: node }
  331. ));
  332. return op.set();
  333. }
  334. };
  335. })();
  336. }