1. var saveToFile = function (fileContent, fileName) {
  2. var uc = Components.classes['@mozilla.org/intl/scriptableunicodeconverter'].createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
  3. uc.charset = 'utf-8';
  4. fileContent = uc.ConvertFromUnicode(fileContent);
  5. var nsIFilePicker = Components.interfaces.nsIFilePicker;
  6. var fp = Components.classes['@mozilla.org/filepicker;1'].createInstance(nsIFilePicker);
  7. fp.init(window, '', fp.modeSave);
  8. fp.defaultString = fileName;
  9. fp.appendFilters(fp.filterHTML);
  10. fp.appendFilters(fp.filterAll);
  11. fp.open(function (rv) {
  12. if (rv == nsIFilePicker.returnOK || rv == nsIFilePicker.returnReplace) {
  13. var stream = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream);
  14. stream.init(fp.file, 0x02|0x20|0x08, 0666, 0);
  15. stream.write(fileContent, fileContent.length);
  16. stream.close();
  17. }
  18. });
  19. };
  20. var resolveURL = function (url, base) {
  21. try {
  22. var ioService = Components.classes['@mozilla.org/network/io-service;1'].getService(Components.interfaces.nsIIOService);
  23. var baseURI = ioService.newURI(base, null, null);
  24. var absURI = ioService.newURI(url, null, baseURI);
  25. return absURI.spec;
  26. } catch (e) {}
  27. };
  28. var getSelWin = function (w) {
  29. if (w.getSelection().toString()) return w;
  30. for (var i = 0, f, r; f = w.frames[i]; i++) {
  31. try {
  32. if (r = getSelWin(f)) return r;
  33. } catch(e) {}
  34. }
  35. };
  36. var encodeImg = function (src, obj) {
  37. var canvas, img, ret = src;
  38. if (/^https?:\/\//.test(src)) {
  39. canvas = doc.createElement('canvas');
  40. if (!obj || obj.nodeName.toLowerCase() != 'img') {
  41. img = doc.createElement('img');
  42. img.src = src;
  43. } else {
  44. img = obj;
  45. };
  46. if (img.complete) try{
  47. canvas.width = img.width;
  48. canvas.height = img.height;
  49. canvas.getContext('2d').drawImage(img, 0, 0);
  50. ret = canvas.toDataURL((/\.jpe?g/i.test(src) ? 'image/jpeg' : 'image/png'));
  51. } catch (e) {};
  52. if (img != obj) img.src = 'about:blank';
  53. };
  54. return ret;
  55. };
  56. var toSrc = function (obj) {
  57. var strToSrc = function (str) {
  58. var chr, ret = '', i = 0, meta = {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '\x22' : '\\\x22', '\\': '\\\\'};
  59. while (chr = str.charAt(i++)) {
  60. ret += meta[chr] || chr;
  61. };
  62. return '\x22' + ret + '\x22';
  63. },
  64. arrToSrc = function (arr) {
  65. var ret = [];
  66. for (var i = 0; i < arr.length; i++) {
  67. ret[i] = toSrc(arr[i]) || 'null';
  68. };
  69. return '[' + ret.join(',') + ']';
  70. },
  71. objToSrc = function (obj) {
  72. var val, ret = [];
  73. for (var prop in obj) {
  74. if (obj.hasOwnProperty(prop) && (val = toSrc(obj[prop]))) ret.push(strToSrc(prop) + ': ' + val);
  75. };
  76. return '{' + ret.join(',') + '}';
  77. };
  78. switch (Object.prototype.toString.call(obj).slice(8, -1)) {
  79. case 'Array': return arrToSrc(obj);
  80. case 'Boolean':
  81. case 'Function':
  82. case 'RegExp': return obj.toString();
  83. case 'Date': return 'new Date(' + obj.getTime() + ')';
  84. case 'Math': return 'Math';
  85. case 'Number': return isFinite(obj) ? String(obj) : 'null';
  86. case 'Object': return objToSrc(obj);
  87. case 'String': return strToSrc(obj);
  88. default: return obj ? (obj.nodeType == 1 && obj.id ? 'document.getElementById(' + strToSrc(obj.id) + ')' : '{}') : 'null';
  89. }
  90. };
  91. var mainWin = document.commandDispatcher.focusedWindow.top == content ? document.commandDispatcher.focusedWindow : content
  92. var selWin = getSelWin(mainWin), win = selWin || mainWin, doc = win.document, loc = win.location;
  93. var ele, pEle, clone, reUrl = /(url\(\x22)(.+?)(\x22\))/g;
  94. if (selWin) {
  95. var rng = win.getSelection().getRangeAt(0);
  96. pEle = rng.commonAncestorContainer;
  97. ele = rng.cloneContents();
  98. } else {
  99. pEle = doc.documentElement;
  100. ele = (doc.body || doc.getElementsByTagName('body')[0]).cloneNode(true);
  101. };
  102. while (pEle) {
  103. if (pEle.nodeType == 1) {
  104. clone = pEle.cloneNode(false);
  105. clone.appendChild(ele);
  106. ele = clone;
  107. };
  108. pEle = pEle.parentNode
  109. };
  110. var sel = doc.createElement('div');
  111. sel.appendChild(ele);
  112. for (var el, all = sel.getElementsByTagName('*'), i = all.length; i--;) {
  113. el = all[i];
  114. if (el.style && el.style.backgroundImage) el.style.backgroundImage = el.style.backgroundImage.replace(reUrl, function (a, prev, url, next) {
  115. if (!/^[a-z]+:/.test(url)) url = resolveURL(url, loc.href);
  116. return prev + encodeImg(url) + next;
  117. });
  118. switch (el.nodeName.toLowerCase()) {
  119. case 'link':
  120. case 'style':
  121. case 'script': el.parentNode.removeChild(el); break;
  122. case 'a':
  123. case 'area': if (el.hasAttribute('href') && el.getAttribute('href').charAt(0) != '#') el.href = el.href; break;
  124. case 'img':
  125. case 'input': if (el.hasAttribute('src')) el.src = encodeImg(el.src, el); break;
  126. case 'audio':
  127. case 'video':
  128. case 'embed':
  129. case 'frame':
  130. case 'iframe': if (el.hasAttribute('src')) el.src = el.src; break;
  131. case 'object': if (el.hasAttribute('data')) el.data = el.data; break;
  132. case 'form': if (el.hasAttribute('action')) el.action = el.action; break;
  133. }
  134. };
  135. var head = ele.insertBefore(doc.createElement('head'), ele.firstChild);
  136. var meta = doc.createElement('meta');
  137. meta.httpEquiv = 'content-type';
  138. meta.content = 'text/html; charset=utf-8';
  139. head.appendChild(meta);
  140. var title = doc.getElementsByTagName('title')[0];
  141. if (title) head.appendChild(title.cloneNode(true));
  142. head.copyScript = function (unsafeWin) {
  143. if ('$' in unsafeWin) return;
  144. var f = doc.createElement('iframe');
  145. f.src = 'about:blank';
  146. f.setAttribute('style', 'position:fixed;left:0;top:0;visibility:hidden;width:0;height:0;');
  147. doc.documentElement.appendChild(f);
  148. var str, script = doc.createElement('script');
  149. script.type = 'text/javascript';
  150. for (var name in unsafeWin) {
  151. if (name in f.contentWindow || !/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(name)) continue;
  152. try {
  153. str = toSrc(unsafeWin[name]);
  154. if (!/\{\s*\[native code\]\s*\}/.test(str)) {
  155. script.appendChild(doc.createTextNode('var ' + name + ' = ' + str.replace(/<\/(script>)/ig, '<\\/$1') + ';\n'));
  156. }
  157. } catch (e) {};
  158. };
  159. f.parentNode.removeChild(f);
  160. if (script.childNodes.length) this.nextSibling.appendChild(script);
  161. };
  162. head.copyScript(win.wrappedJSObject || win);
  163. head.copyStyle = function (s) {
  164. if (!s) return;
  165. var style = doc.createElement('style');
  166. style.type = 'text/css';
  167. if (s.media && s.media.mediaText) style.media = s.media.mediaText;
  168. try {
  169. for (var i = 0, rule; rule = s.cssRules[i]; i++) {
  170. if (rule.type != 3) {
  171. if((!rule.selectorText || rule.selectorText.indexOf(':') != -1) || (!sel.querySelector || sel.querySelector(rule.selectorText))) {
  172. var css = !rule.cssText ? '' : rule.cssText.replace(reUrl, function (a, prev, url, next) {
  173. if (!/^[a-z]+:/.test(url)) url = resolveURL(url, s.href || loc.href);
  174. if(rule.type == 1 && rule.style && rule.style.backgroundImage) url = encodeImg(url);
  175. return prev + url + next;
  176. });
  177. style.appendChild(doc.createTextNode(css + '\n'));
  178. }
  179. } else {
  180. this.copyStyle(rule.styleSheet);
  181. }
  182. }
  183. } catch(e) {
  184. if (s.ownerNode) style = s.ownerNode.cloneNode(false);
  185. };
  186. this.appendChild(style);
  187. };
  188. var sheets = doc.styleSheets;
  189. for (var j = 0; j < sheets.length; j++) head.copyStyle(sheets[j]);
  190. head.appendChild(doc.createTextNode('\n'));
  191. var doctype = '', dt = doc.doctype;
  192. if (dt && dt.name) {
  193. doctype += '<!DOCTYPE ' + dt.name;
  194. if (dt.publicId) doctype += ' PUBLIC \x22' + dt.publicId + '\x22';
  195. if (dt.systemId) doctype += ' \x22' + dt.systemId + '\x22';
  196. doctype += '>\n';
  197. };
  198. var fileName = selWin ? win.getSelection().toString() : (title && title.text ? title.text : loc.pathname.split('/').pop());
  199. fileName = fileName.replace(/[:\\\/<>?*|"]+/g, '_').replace(/\s+/g, ' ').slice(0, 100).replace(/^\s+|\s+$/g, '');
  200. fileName += (function () {
  201. var d = new Date(), z = function(n){return '_' + (n < 10 ? '0' : '') + n};
  202. return z(d.getHours()) + z(d.getMinutes()) + z(d.getSeconds());
  203. })();
  204. if(!/\.html?$/.test(fileName))fileName += '.html';
  205. saveToFile(doctype + sel.innerHTML + '\n<!-- This document saved from ' + (loc.protocol != 'data:' ? loc.href : 'data:uri') + ' -->', fileName);