31 | * $('#content').highlight('ipsum', { element: 'em', className: 'important' });
32 | *
33 | * // remove default highlight
34 | * $('#content').unhighlight();
35 | *
36 | * // remove custom highlight
37 | * $('#content').unhighlight({ element: 'em', className: 'important' });
38 | *
39 | *
40 | * Copyright (c) 2009 Bartek Szopka
41 | *
42 | * Licensed under MIT license.
43 | *
44 | */
45 |
46 | jQuery.extend({
47 | highlight: function (node, re, nodeName, className) {
48 | if (node.nodeType === 3) {
49 | var match = node.data.match(re);
50 | if (match) {
51 | var highlight = document.createElement(nodeName || 'span');
52 | highlight.className = className || 'highlight';
53 | var wordNode = node.splitText(match.index);
54 | wordNode.splitText(match[0].length);
55 | var wordClone = wordNode.cloneNode(true);
56 | highlight.appendChild(wordClone);
57 | wordNode.parentNode.replaceChild(highlight, wordNode);
58 | return 1; //skip added node in parent
59 | }
60 | } else if ((node.nodeType === 1 && node.childNodes) && // only element nodes that have children
61 | !/(script|style)/i.test(node.tagName) && // ignore script and style nodes
62 | !(node.tagName === nodeName.toUpperCase() && node.className === className)) { // skip if already highlighted
63 | for (var i = 0; i < node.childNodes.length; i++) {
64 | i += jQuery.highlight(node.childNodes[i], re, nodeName, className);
65 | }
66 | }
67 | return 0;
68 | }
69 | });
70 |
71 | jQuery.fn.unhighlight = function (options) {
72 | var settings = { className: 'highlight', element: 'span' };
73 | jQuery.extend(settings, options);
74 |
75 | return this.find(settings.element + "." + settings.className).each(function () {
76 | var parent = this.parentNode;
77 | parent.replaceChild(this.firstChild, this);
78 | parent.normalize();
79 | }).end();
80 | };
81 |
82 | jQuery.fn.highlight = function (words, options) {
83 | var settings = { className: 'highlight', element: 'span', caseSensitive: false, wordsOnly: false };
84 | jQuery.extend(settings, options);
85 |
86 | if (words.constructor === String) {
87 | words = [words];
88 | }
89 | words = jQuery.grep(words, function(word, i){
90 | return word != '';
91 | });
92 | words = jQuery.map(words, function(word, i) {
93 | return word.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
94 | });
95 | if (words.length == 0) { return this; };
96 |
97 | var flag = settings.caseSensitive ? "" : "i";
98 | var pattern = "(" + words.join("|") + ")";
99 | if (settings.wordsOnly) {
100 | pattern = "\\b" + pattern + "\\b";
101 | }
102 | var re = new RegExp(pattern, flag);
103 |
104 | return this.each(function () {
105 | jQuery.highlight(this, re, settings.element, settings.className);
106 | });
107 | };
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
40 |
41 |
Preview of the document
42 |
43 |
50 |
51 |
54 |
119 |
120 |
135 |
136 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | /* Copyright (c) 2013-2016 The TagSpaces Authors.
2 | * Use of this source code is governed by the MIT license which can be found in the LICENSE.txt file. */
3 |
4 | /* global define MailParser, DOMPurify, Readability */
5 | /* globals marked, MailParser, Mousetrap */
6 |
7 | "use strict";
8 |
9 | var readabilityContent;
10 | var cleanedHTML;
11 | var mhtmlViewer;
12 | var fontSize = 14;
13 |
14 | function setContent(content, filePathURI) {
15 | //console.log("MHTML Content: "+content);
16 | var mhtparser = new mailparser.MailParser();
17 | mhtparser.on("end", function(mail_object) {
18 | //console.log("mail_object:", mail_object);
19 |
20 | var contLocation = /^content-location:(.*$)/im.exec(content);
21 | mail_object.contentLocation = (contLocation && contLocation.length > 0) ? contLocation[1] : "not found";
22 | cleanedHTML = DOMPurify.sanitize(mail_object.html);
23 |
24 | updateHTMLContent($("#mhtmlViewer"), cleanedHTML);
25 |
26 | $("#fileMeta").append("saved on " + mail_object.headers.date);
27 |
28 | // View readability mode
29 |
30 | try {
31 | var documentClone = document.cloneNode(true);
32 | var article = new Readability(document.baseURI, documentClone).parse();
33 | readabilityContent = article.content;
34 | } catch (e) {
35 | console.log("Error handling" + e);
36 | var msg = {
37 | command: "showAlertDialog",
38 | title: 'Readability Mode',
39 | message: 'This content can not be loaded.'
40 | };
41 | window.parent.postMessage(JSON.stringify(msg), "*");
42 | }
43 |
44 |
45 | if(readabilityContent) {
46 | updateHTMLContent($("#mhtmlViewer"), readabilityContent);
47 | }
48 |
49 | mhtmlViewer = document.getElementById("mhtmlViewer");
50 | mhtmlViewer.style.fontSize = fontSize;//"large";
51 | mhtmlViewer.style.fontFamily = "Helvetica, Arial, sans-serif";
52 | mhtmlViewer.style.background = "#ffffff";
53 | mhtmlViewer.style.color = "";
54 |
55 | init(filePathURI, mail_object);
56 | });
57 |
58 | mhtparser.write(content);
59 | mhtparser.end();
60 | }
61 |
62 | function handleLinks($element) {
63 | $element.find("a[href]").each(function() {
64 | var currentSrc = $(this).attr("href");
65 | $(this).off();
66 | $(this).on('click', function(e) {
67 | e.preventDefault();
68 | var msg = {command: "openLinkExternally", link: currentSrc};
69 | window.parent.postMessage(JSON.stringify(msg), "*");
70 | });
71 | });
72 | }
73 |
74 | function updateHTMLContent($targetElement, content) {
75 | $targetElement.html(content);
76 | handleLinks($targetElement);
77 | }
78 |
79 | function init(filePathURI, objectlocation) {
80 | var isCordova;
81 | var isWin;
82 | var isWeb;
83 |
84 | var $htmlContent;
85 |
86 | function getParameterByName(name) {
87 | name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
88 | var regex = new RegExp("[\\?&]" + name + "=([^]*)"),
89 | results = regex.exec(window.location.search);
90 | return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
91 | }
92 |
93 | var locale = getParameterByName("locale");
94 |
95 | var extSettings;
96 | loadExtSettings();
97 |
98 | isCordova = parent.isCordova;
99 | isWin = parent.isWin;
100 | isWeb = parent.isWeb;
101 |
102 | $htmlContent = $("#mhtmlViewer");
103 |
104 | var styles = ['', 'solarized-dark', 'github', 'metro-vibes', 'clearness', 'clearness-dark'];
105 | var currentStyleIndex = 0;
106 | if (extSettings && extSettings.styleIndex) {
107 | currentStyleIndex = extSettings.styleIndex;
108 | }
109 |
110 | var zoomSteps = ['zoomSmallest', 'zoomSmaller', 'zoomSmall', 'zoomDefault', 'zoomLarge', 'zoomLarger', 'zoomLargest'];
111 | var currentZoomState = 3;
112 | if (extSettings && extSettings.zoomState) {
113 | currentZoomState = extSettings.zoomState;
114 | }
115 |
116 | $htmlContent.removeClass();
117 | $htmlContent.addClass('markdown ' + styles[currentStyleIndex] + " " + zoomSteps[currentZoomState]);
118 |
119 | // Menu: hide readability items
120 | $("#toSansSerifFont").show();
121 | $("#toSerifFont").show();
122 | $("#increasingFontSize").show();
123 | $("#decreasingFontSize").show();
124 | $("#readabilityOn").hide();
125 | $("#changeStyleButton").hide();
126 | $("#resetStyleButton").hide();
127 |
128 | //hide zoom operation menu items because they don't influence on the style
129 | $("#zoomInButton").hide();
130 | $("#zoomOutButton").hide();
131 | $("#zoomResetButton").hide();
132 |
133 | $("#zoomInButton").on('click', function() {
134 | //console.log("#zoomInButton click");
135 | currentZoomState++;
136 | if (currentZoomState >= zoomSteps.length) {
137 | currentZoomState = 6;
138 | }
139 | $htmlContent.removeClass();
140 | $htmlContent.addClass('markdown ' + styles[currentStyleIndex] + " " + zoomSteps[currentZoomState]);
141 | saveExtSettings();
142 | });
143 |
144 | $("#zoomOutButton").on('click', function() {
145 | //console.log("#zoomOutButton click");
146 | currentZoomState--;
147 | if (currentZoomState < 0) {
148 | currentZoomState = 0;
149 | }
150 | $htmlContent.removeClass();
151 | $htmlContent.addClass('markdown ' + styles[currentStyleIndex] + " " + zoomSteps[currentZoomState]);
152 | saveExtSettings();
153 | });
154 |
155 | $("#zoomResetButton").on('click', function() {
156 | currentZoomState = 3;
157 | $htmlContent.removeClass();
158 | $htmlContent.addClass('markdown ' + styles[currentStyleIndex] + " " + zoomSteps[currentZoomState]);
159 | saveExtSettings();
160 | });
161 |
162 | $("#openInNewWindowButton").on('click', function() {
163 | window.parent.open(filePathURI, '_blank'); // , 'nodeIntegration=0'
164 | });
165 |
166 | $("#openURLButton").on('click', function() {
167 | var msg = {command: "openLinkExternally", link: objectlocation.contentLocation.trim()};
168 | window.parent.postMessage(JSON.stringify(msg), "*");
169 | });
170 |
171 | $("#toSansSerifFont").on('click', function(e) {
172 | e.stopPropagation();
173 | $htmlContent[0].style.fontFamily = "Helvetica, Arial, sans-serif";
174 | });
175 |
176 | $("#toSerifFont").on('click', function(e) {
177 | e.stopPropagation();
178 | $htmlContent[0].style.fontFamily = "Georgia, Times New Roman, serif";
179 | });
180 |
181 | $("#increasingFontSize").on('click', function(e) {
182 | e.stopPropagation();
183 | increaseFont();
184 | });
185 |
186 | $("#decreasingFontSize").on('click', function(e) {
187 | e.stopPropagation();
188 | decreaseFont();
189 | });
190 |
191 | $("#whiteBackgroundColor").on('click', function(e) {
192 | e.stopPropagation();
193 | $htmlContent[0].style.background = "#ffffff";
194 | $htmlContent[0].style.color = "";
195 | });
196 |
197 | $("#blackBackgroundColor").on('click', function(e) {
198 | e.stopPropagation();
199 | $htmlContent[0].style.background = "#282a36";
200 | $htmlContent[0].style.color = "#ffffff";
201 | });
202 |
203 | $("#sepiaBackgroundColor").on('click', function(e) {
204 | e.stopPropagation();
205 | $htmlContent[0].style.color = "#5b4636";
206 | $htmlContent[0].style.background = "#f4ecd8";
207 | });
208 |
209 | $("#changeStyleButton").on('click', function() {
210 | currentStyleIndex = currentStyleIndex + 1;
211 | if (currentStyleIndex >= styles.length) {
212 | currentStyleIndex = 0;
213 | }
214 | $htmlContent.removeClass();
215 | $htmlContent.addClass('markdown ' + styles[currentStyleIndex] + " " + zoomSteps[currentZoomState]);
216 | saveExtSettings();
217 | });
218 |
219 | $("#resetStyleButton").on('click', function() {
220 | currentStyleIndex = 0;
221 | //currentZoomState = 5;
222 | $htmlContent.removeClass();
223 | $htmlContent.addClass('markdown ' + styles[currentStyleIndex] + " " + zoomSteps[currentZoomState]);
224 | saveExtSettings();
225 | });
226 |
227 | $("#readabilityOn").on('click', function() {
228 | if(readabilityContent) {
229 | updateHTMLContent($("#mhtmlViewer"), readabilityContent);
230 | }
231 | //if ($("#mhtmlViewer").data('clicked', true)) {
232 | $("#toSerifFont").show();
233 | $("#toSansSerifFont").show();
234 | $("#increasingFontSize").show();
235 | $("#decreasingFontSize").show();
236 | $("#readabilityOff").show();
237 | $("#whiteBackgroundColor").show();
238 | $("#blackBackgroundColor").show();
239 | $("#sepiaBackgroundColor").show();
240 | $("#themeStyle").show();
241 | $("#readabilityFont").show();
242 | $("#readabilityFontSize").show();
243 | $("#readabilityOn").hide();
244 | $("#changeStyleButton").hide();
245 | $("#resetStyleButton").hide();
246 | //}
247 | });
248 |
249 | $("#readabilityOff").on('click', function() {
250 | updateHTMLContent($("#mhtmlViewer"), cleanedHTML);
251 | mhtmlViewer.style.fontSize = '';
252 | mhtmlViewer.style.fontFamily = "";
253 | mhtmlViewer.style.color = "";
254 | mhtmlViewer.style.background = "";
255 | $("#readabilityOff").hide();
256 | $("#toSerifFont").hide();
257 | $("#toSansSerifFont").hide();
258 | $("#increasingFontSize").hide();
259 | $("#decreasingFontSize").hide();
260 | $("#whiteBackgroundColor").hide();
261 | $("#blackBackgroundColor").hide();
262 | $("#sepiaBackgroundColor").hide();
263 | $("#themeStyle").hide();
264 | $("#readabilityFont").hide();
265 | $("#readabilityFontSize").hide();
266 | $("#readabilityOn").show();
267 | $("#changeStyleButton").show();
268 | $("#resetStyleButton").show();
269 | });
270 |
271 | function increaseFont() {
272 | var style = window.getComputedStyle($htmlContent[0], null).getPropertyValue('font-size');
273 | var fontSize = parseFloat(style);
274 | $htmlContent[0].style.fontSize = (fontSize + 1) + 'px';
275 | }
276 |
277 | function decreaseFont() {
278 | var style = window.getComputedStyle($htmlContent[0], null).getPropertyValue('font-size');
279 | var fontSize = parseFloat(style);
280 | $htmlContent[0].style.fontSize = (fontSize - 1) + 'px';
281 | }
282 |
283 | Mousetrap.bind(['command++', 'ctrl++'], function(e) {
284 | increaseFont();
285 | return false;
286 | });
287 |
288 | Mousetrap.bind(['command+-', 'ctrl+-'], function(e) {
289 | decreaseFont();
290 | return false;
291 | });
292 |
293 | // Init internationalization
294 | $.i18n.init({
295 | ns: {namespaces: ['ns.viewerMHTML']},
296 | debug: true,
297 | lng: locale,
298 | fallbackLng: 'en_US'
299 | }, function() {
300 | $('[data-i18n]').i18n();
301 | });
302 |
303 | function saveExtSettings() {
304 | var settings = {
305 | "styleIndex": currentStyleIndex,
306 | "zoomState": currentZoomState
307 | };
308 | localStorage.setItem('viewerMHTMLSettings', JSON.stringify(settings));
309 | }
310 |
311 | function loadExtSettings() {
312 | extSettings = JSON.parse(localStorage.getItem("viewerMHTMLSettings"));
313 | }
314 | }
--------------------------------------------------------------------------------