_base.js 13 KB

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