git ssb

3+

dust / capsule



Commit e787274e31b573fea422c58d226a4510109917c8

moved plugin files to own directory

dust committed on 3/30/2016, 6:35:24 AM
Parent: 09f12de5622b73e85b6a4ea7cf82783ac81dd05b

Files changed

capsule.htmldeleted
chromium/capsule.htmladded
chromium/capsule.jsadded
chromium/icon.pngadded
chromium/manifest.jsonadded
chromium/serializeSelectedHTML.jsadded
capsule.jsdeleted
icon.pngdeleted
manifest.jsondeleted
serializeSelectedHTML.jsdeleted
capsule.htmlView
@@ -1,112 +1,0 @@
1-<!doctype html>
2-<html>
3- <head>
4- <title>Capsule</title>
5- <style>
6- body {
7- font-family: "Segoe UI", "Lucida Grande", Tahoma, sans-serif;
8- font-size: 100%;
9- width: 400px;
10- height: 600px;
11- }
12- #status {
13- /* avoid an excessively wide status text */
14- white-space: pre;
15- text-overflow: ellipsis;
16- overflow: hidden;
17- max-width: 400px;
18- }
19- #capsule-menu {
20- z-index: 999;
21- position: fixed;
22- top: 0;
23- right: 0;
24- width: 400px;
25- height: 600px;
26- background: #fff;
27- /* border-width: 2px; */
28- border-color: cornsilk !important;
29- border-radius: 5px;
30- }
31-
32- #capsule-selected-content {
33- position: relative;
34- width: 100%;
35- /* overflow-wrap: break-word; */
36- overflow: scroll;
37- background-color: inherit;
38- max-height: 400px;
39- height: 40%;
40- }
41-
42- #capsule-selected-content {}
43-
44- #capsule-hrule {
45- background-color: #EEE;
46- color: #EEE;
47- height:1px;
48- border:none;
49- }
50-
51- #capsule-title {
52- color: #EEE;
53- text-align: center;
54- background: gray;
55- border-width: 2px;
56- border-color: black;
57- border-radius: 2px;
58- padding: 0.25rem;
59- border-left: 0.25rem;
60- border-right: 0.25rem;
61- }
62-
63- #capsule-comment-field {
64- background-color: steelblue;
65- border-width: thick;
66- border-color: white;
67- border-radius: 2px;
68- min-height: 4em;
69- overflow-y: visible;
70- }
71-
72- #capsule-controls {
73- display: block;
74- text-align: center;
75- padding: 0.5rem;
76- }
77-
78- #capsule-button {
79- background-color: darkslategrey;
80- color: #EEE;
81- border: 0;
82- border-radius: 5px;
83- padding-top: 0.25rem;
84- padding-bottom: 0.25rem;
85- }
86- </style>
87-
88- <!--
89- - JavaScript and HTML must be in separate files: see our Content Security
90- - Policy documentation[1] for details and explanation.
91- -
92- - [1]: https://developer.chrome.com/extensions/contentSecurityPolicy
93- -->
94- <script src="capsule.js"></script>
95- </head>
96- <body>
97- <div id="capsule-menu">
98- <div id="capsule-title">Capsule</div>
99- <div id="capsule-selected-content">
100- no selected HTML could be parsed :(
101- </div>
102- <hr id="capsule-hrule" />
103- <div id="capsule-comment-field">
104- Comments go here
105- </div>
106- <div id="capsule-controls">
107- <button id="capsule-send">Send</button>
108- </div>
109- </div>
110- </body>
111-</html>
112-
chromium/capsule.htmlView
@@ -1,0 +1,112 @@
1+<!doctype html>
2+<html>
3+ <head>
4+ <title>Capsule</title>
5+ <style>
6+ body {
7+ font-family: "Segoe UI", "Lucida Grande", Tahoma, sans-serif;
8+ font-size: 100%;
9+ width: 400px;
10+ height: 600px;
11+ }
12+ #status {
13+ /* avoid an excessively wide status text */
14+ white-space: pre;
15+ text-overflow: ellipsis;
16+ overflow: hidden;
17+ max-width: 400px;
18+ }
19+ #capsule-menu {
20+ z-index: 999;
21+ position: fixed;
22+ top: 0;
23+ right: 0;
24+ width: 400px;
25+ height: 600px;
26+ background: #fff;
27+ /* border-width: 2px; */
28+ border-color: cornsilk !important;
29+ border-radius: 5px;
30+ }
31+
32+ #capsule-selected-content {
33+ position: relative;
34+ width: 100%;
35+ /* overflow-wrap: break-word; */
36+ overflow: scroll;
37+ background-color: inherit;
38+ max-height: 400px;
39+ height: 40%;
40+ }
41+
42+ #capsule-selected-content {}
43+
44+ #capsule-hrule {
45+ background-color: #EEE;
46+ color: #EEE;
47+ height:1px;
48+ border:none;
49+ }
50+
51+ #capsule-title {
52+ color: #EEE;
53+ text-align: center;
54+ background: gray;
55+ border-width: 2px;
56+ border-color: black;
57+ border-radius: 2px;
58+ padding: 0.25rem;
59+ border-left: 0.25rem;
60+ border-right: 0.25rem;
61+ }
62+
63+ #capsule-comment-field {
64+ background-color: steelblue;
65+ border-width: thick;
66+ border-color: white;
67+ border-radius: 2px;
68+ min-height: 4em;
69+ overflow-y: visible;
70+ }
71+
72+ #capsule-controls {
73+ display: block;
74+ text-align: center;
75+ padding: 0.5rem;
76+ }
77+
78+ #capsule-button {
79+ background-color: darkslategrey;
80+ color: #EEE;
81+ border: 0;
82+ border-radius: 5px;
83+ padding-top: 0.25rem;
84+ padding-bottom: 0.25rem;
85+ }
86+ </style>
87+
88+ <!--
89+ - JavaScript and HTML must be in separate files: see our Content Security
90+ - Policy documentation[1] for details and explanation.
91+ -
92+ - [1]: https://developer.chrome.com/extensions/contentSecurityPolicy
93+ -->
94+ <script src="capsule.js"></script>
95+ </head>
96+ <body>
97+ <div id="capsule-menu">
98+ <div id="capsule-title">Capsule</div>
99+ <div id="capsule-selected-content">
100+ no selected HTML could be parsed :(
101+ </div>
102+ <hr id="capsule-hrule" />
103+ <div id="capsule-comment-field">
104+ Comments go here
105+ </div>
106+ <div id="capsule-controls">
107+ <button id="capsule-send">Send</button>
108+ </div>
109+ </div>
110+ </body>
111+</html>
112+
chromium/capsule.jsView
@@ -1,0 +1,56 @@
1+var getSelectedHTML = function() {
2+ chrome.tabs.query({active: true}, function(tabs) {
3+ var tab = tabs[0]
4+
5+ chrome.tabs.executeScript(
6+ {
7+ file: './serializeSelectedHTML.js',
8+ runAt: 'document_end'
9+ },
10+ renderSelectedHTML
11+ )
12+ })
13+}
14+
15+var renderSelectedHTML = function(selection) {
16+ const parsedSelection = JSON.parse(selection)
17+
18+ var modify = []
19+
20+ const selectedBox = document.getElementById('capsule-selected-content')
21+
22+ if (parsedSelection.html) {
23+ selectedBox.innerHTML = parsedSelection.html
24+ }
25+}
26+
27+var sendHTML = function(htmlString) {
28+ chrome.tabs.getSelected(function(selectedTab) {
29+ let serialisedURI = encodeURI('ssb-capsule://transmit?body='
30+ .concat(htmlString)
31+ .concat('&src=').concat(selectedTab.url))
32+
33+ const serialiserTabProps = {
34+ url: serialisedURI,
35+ active: true
36+ }
37+
38+ console.log('launching a capsule...')
39+ chrome.tabs.create(serialiserTabProps, function(URITab) {
40+ // TODO this doesn't close the new tab
41+ chrome.tabs.remove(URITab.id)
42+ })
43+ })
44+}
45+
46+var hookToElements = function() {
47+ const sendButton = document.getElementById('capsule-send')
48+ sendButton.addEventListener('click', function() {
49+ const selectedBox = document.getElementById('capsule-selected-content')
50+
51+ sendHTML(selectedBox.innerHTML)
52+ })
53+}
54+
55+document.addEventListener('DOMContentLoaded', getSelectedHTML)
56+document.addEventListener('DOMContentLoaded', hookToElements)
chromium/icon.png
chromium/icon.png
chromium/manifest.jsonView
@@ -1,0 +1,15 @@
1+{
2+ "manifest_version": 2,
3+
4+ "name": "Capsule (SSB)",
5+ "description": "This extension allows publishing of HTML snippets to the SSB galaxy.",
6+ "version": "1.0",
7+
8+ "browser_action": {
9+ "default_icon": "icon.png",
10+ "default_popup": "capsule.html"
11+ },
12+ "permissions": [
13+ "activeTab"
14+ ]
15+}
chromium/serializeSelectedHTML.jsView
@@ -1,0 +1,288 @@
1+/**
2+ * Snapshooter is responsible for returning HTML and computed CSS of all nodes from selected DOM subtree.
3+ *
4+ * @param HTMLElement root Root node for the subtree that will be processed
5+ * @returns {*} object with HTML as a string and CSS as an array of arrays of css properties
6+ */
7+function Snapshooter(root) {
8+ "use strict";
9+
10+ // list of shorthand properties based on CSSShorthands.in from the Chromium
11+ // code (https://code.google.com/p/chromium/codesearch)
12+ // TODO this list should not be hardcoded here
13+ var shorthandProperties = {
14+ 'animation': 'animation',
15+ 'background': 'background',
16+ 'border': 'border',
17+ 'border-top': 'borderTop',
18+ 'border-right': 'borderRight',
19+ 'border-bottom': 'borderBottom',
20+ 'border-left': 'borderLeft',
21+ 'border-width': 'borderWidth',
22+ 'border-color': 'borderColor',
23+ 'border-style': 'borderStyle',
24+ 'border-radius': 'borderRadius',
25+ 'border-image': 'borderImage',
26+ 'border-spacing': 'borderSpacing',
27+ 'flex': 'flex',
28+ 'flex-flow': 'flexFlow',
29+ 'font': 'font',
30+ 'grid-area': 'gridArea',
31+ 'grid-column': 'gridColumn',
32+ 'grid-row': 'gridRow',
33+ 'list-style': 'listStyle',
34+ 'margin': 'margin',
35+ 'marker': 'marker',
36+ 'outline': 'outline',
37+ 'overflow': 'overflow',
38+ 'padding': 'padding',
39+ 'text-decoration': 'textDecoration',
40+ 'transition': 'transition',
41+ '-webkit-border-after': 'webkitBorderAfter',
42+ '-webkit-border-before': 'webkitBorderBefore',
43+ '-webkit-border-end': 'webkitBorderEnd',
44+ '-webkit-border-start': 'webkitBorderStart',
45+ '-webkit-columns': 'webkitBorderColumns',
46+ '-webkit-column-rule': 'webkitBorderColumnRule',
47+ '-webkit-margin-collapse': 'webkitMarginCollapse',
48+ '-webkit-mask': 'webkitMask',
49+ '-webkit-mask-position': 'webkitMaskPosition',
50+ '-webkit-mask-repeat': 'webkitMaskRepeat',
51+ '-webkit-text-emphasis': 'webkitTextEmphasis',
52+ '-webkit-transition': 'webkitTransition',
53+ '-webkit-transform-origin': 'webkitTransformOrigin'
54+ },
55+ idCounter = 1;
56+
57+ /**
58+ * Changes CSSStyleDeclaration to simple Object removing unwanted properties
59+ * ('1','2','parentRule','cssText' etc.) in the process.
60+ *
61+ * @param CSSStyleDeclaration style
62+ * @returns {}
63+ */
64+ function styleDeclarationToSimpleObject(style) {
65+ var i, l, cssName, camelCaseName,
66+ output = {};
67+
68+ for (i = 0, l = style.length; i < l; i++) {
69+ output[style[i]] = style[style[i]];
70+ }
71+
72+ // Work around http://crbug.com/313670 (the "content" property is not
73+ // present as a computed style indexed property value).
74+ output.content = fixContentProperty(style.content);
75+
76+ // Since shorthand properties are not available in the indexed array, copy
77+ // them from named properties
78+ for (cssName in shorthandProperties) {
79+ if (shorthandProperties.hasOwnProperty(cssName)) {
80+ camelCaseName = shorthandProperties[cssName];
81+ output[cssName] = style[camelCaseName];
82+ }
83+ }
84+
85+ return output;
86+ }
87+
88+ // Partial workaround for http://crbug.com/315028 (single words in the
89+ // "content" property are not wrapped with quotes)
90+ function fixContentProperty(content) {
91+ var values, output, value, i, l;
92+
93+ output = [];
94+
95+ if (content) {
96+ //content property can take multiple values - we need to split them up
97+ //FIXME this won't work for '\''
98+ values = content.match(/(?:[^\s']+|'[^']*')+/g);
99+
100+ for (i = 0, l = values.length; i < l; i++) {
101+ value = values[i];
102+
103+ if (value.match(/^(url\()|(attr\()|normal|none|open-quote|close-quote|no-open-quote|no-close-quote|chapter_counter|'/g)) {
104+ output.push(value);
105+ } else {
106+ output.push("'" + value + "'");
107+ }
108+ }
109+ }
110+
111+ return output.join(' ');
112+ }
113+
114+ function createID(node) {
115+ //":snappysnippet_prefix:" is a prefix placeholder
116+ return ':snappysnippet_prefix:' + node.tagName + '_' + idCounter++;
117+ }
118+
119+ function dumpCSS(node, pseudoElement) {
120+ var styles;
121+
122+ styles = node.ownerDocument.defaultView.getComputedStyle(node, pseudoElement);
123+
124+ if (pseudoElement) {
125+ //if we are dealing with pseudoelement, check if 'content' property isn't empty
126+ //if it is, then we can ignore the whole element
127+ if (!styles.getPropertyValue('content')) {
128+ return null;
129+ }
130+ }
131+
132+ return styleDeclarationToSimpleObject(styles);
133+ }
134+
135+ function cssObjectForElement(element, omitPseudoElements) {
136+ return {
137+ id: createID(element),
138+ tagName: element.tagName,
139+ node: dumpCSS(element, null),
140+ before: omitPseudoElements ? null : dumpCSS(element, ':before'),
141+ after: omitPseudoElements ? null : dumpCSS(element, ':after')
142+ };
143+ }
144+
145+ function ancestorTagHTML(element, closingTag) {
146+ var i, attr, value, idSeen,
147+ result, attributes;
148+
149+ if (closingTag) {
150+ return '</' + element.tagName + '>';
151+ }
152+
153+ result = '<' + element.tagName;
154+ attributes = element.attributes;
155+
156+ for (i = 0; i < attributes.length; ++i) {
157+ attr = attributes[i];
158+
159+ if (attr.name.toLowerCase() === 'id') {
160+ value = createID(element);
161+ idSeen = true;
162+ } else {
163+ value = attr.value;
164+ }
165+
166+ result += ' ' + attributes[i].name + '="' + value + '"';
167+ }
168+
169+ if (!idSeen) {
170+ result += ' id="' + createID(element) + '"';
171+ }
172+
173+ result += '>';
174+
175+ return result;
176+ }
177+
178+ /**
179+ * Replaces all relative URLs (in images, links etc.) with absolute URLs
180+ * @param element
181+ */
182+ function relativeURLsToAbsoluteURLs(element) {
183+ switch (element.nodeName) {
184+ case 'A':
185+ case 'AREA':
186+ case 'LINK':
187+ case 'BASE':
188+ if (element.hasAttribute('href')) {
189+ element.setAttribute('href', element.href);
190+ }
191+ break;
192+ case 'IMG':
193+ case 'IFRAME':
194+ case 'INPUT':
195+ case 'FRAME':
196+ case 'SCRIPT':
197+ if (element.hasAttribute('src')) {
198+ element.setAttribute('src', element.src);
199+ }
200+ break;
201+ case 'FORM':
202+ if (element.hasAttribute('action')) {
203+ element.setAttribute('action', element.action);
204+ }
205+ break;
206+ }
207+ }
208+
209+ function init() {
210+ var css = [],
211+ ancestorCss = [],
212+ descendants,
213+ descendant,
214+ htmlSegments,
215+ leadingAncestorHtml,
216+ trailingAncestorHtml,
217+ reverseAncestors = [],
218+ i, l,
219+ parent,
220+ clone;
221+
222+ descendants = root.getElementsByTagName('*');
223+
224+ parent = root.parentElement;
225+ while (parent && parent !== document.body) {
226+ reverseAncestors.push(parent);
227+ parent = parent.parentElement;
228+ }
229+
230+ // First we go through all nodes and dump all CSS
231+ css.push(cssObjectForElement(root));
232+
233+ for (i = 0, l = descendants.length; i < l; i++) {
234+ css.push(cssObjectForElement(descendants[i]));
235+ }
236+
237+ for (i = reverseAncestors.length - 1; i >= 0; i--) {
238+ ancestorCss.push(cssObjectForElement(reverseAncestors[i], true));
239+ }
240+
241+ // Next we dump all HTML and update IDs
242+ // Since we don't want to touch original DOM and we want to change IDs, we clone the original DOM subtree
243+ clone = root.cloneNode(true);
244+ descendants = clone.getElementsByTagName('*');
245+ idCounter = 1;
246+
247+ clone.setAttribute('id', createID(clone));
248+
249+ for (i = 0, l = descendants.length; i < l; i++) {
250+ descendant = descendants[i];
251+ descendant.setAttribute('id', createID(descendant));
252+ relativeURLsToAbsoluteURLs(descendant);
253+ }
254+
255+ // Build leading and trailing HTML for ancestors
256+ htmlSegments = [];
257+ for (i = reverseAncestors.length - 1; i >= 0; i--) {
258+ htmlSegments.push(ancestorTagHTML(reverseAncestors[i]));
259+ }
260+ leadingAncestorHtml = htmlSegments.join('');
261+
262+ htmlSegments = [];
263+ for (i = 0, l = reverseAncestors.length; i < l; i++) {
264+ htmlSegments.push(ancestorTagHTML(reverseAncestors[i], true));
265+ }
266+ trailingAncestorHtml = htmlSegments.join('');
267+
268+ return JSON.stringify({
269+ html: clone.outerHTML,
270+ leadingAncestorHtml: leadingAncestorHtml,
271+ trailingAncestorHtml: trailingAncestorHtml,
272+ css: css,
273+ ancestorCss: ancestorCss
274+ });
275+ }
276+
277+ return init();
278+}
279+
280+var selectionDiv = document.createElement('div')
281+var selection = window.getSelection().getRangeAt(0)
282+var startNode = selection.startContainer.parentNode.cloneNode(true)
283+var endNode = selection.endContainer.parentNode.cloneNode(true)
284+
285+selectionDiv.appendChild(startNode)
286+selectionDiv.appendChild(endNode)
287+
288+Snapshooter(selectionDiv)
capsule.jsView
@@ -1,57 +1,0 @@
1-var getSelectedHTML = function() {
2- chrome.tabs.query({active: true}, function(tabs) {
3- var tab = tabs[0]
4-
5- chrome.tabs.executeScript(
6- {
7- file: './serializeSelectedHTML.js',
8- runAt: 'document_end'
9- },
10- renderSelectedHTML
11- )
12- })
13-}
14-
15-var renderSelectedHTML = function(selection) {
16- const parsedSelection = JSON.parse(selection)
17-
18- var modify = []
19-
20- const selectedBox = document.getElementById('capsule-selected-content')
21-
22- if (parsedSelection.html) {
23- selectedBox.innerHTML = parsedSelection.html
24- }
25-}
26-
27-var sendHTML = function(htmlString) {
28- chrome.tabs.getSelected(function(selectedTab) {
29- let serialisedURI = 'ssb-capsule://?body='
30- .concat(htmlString)
31- .concat('&src=')
32- .concat(selectedTab.url)
33-
34- const serialiserTabProps = {
35- url: serialisedURI,
36- active: true
37- }
38-
39- console.log('launching a capsule...')
40- chrome.tabs.create(serialiserTabProps, function(URITab) {
41- // TODO this doesn't close the new tab
42- chrome.tabs.remove(URITab.id)
43- })
44- })
45-}
46-
47-var hookToElements = function() {
48- const sendButton = document.getElementById('capsule-send')
49- sendButton.addEventListener('click', function() {
50- const selectedBox = document.getElementById('capsule-selected-content')
51-
52- sendHTML(selectedBox.innerHTML)
53- })
54-}
55-
56-document.addEventListener('DOMContentLoaded', getSelectedHTML)
57-document.addEventListener('DOMContentLoaded', hookToElements)
icon.png
icon.png
manifest.jsonView
@@ -1,15 +1,0 @@
1-{
2- "manifest_version": 2,
3-
4- "name": "Capsule (SSB)",
5- "description": "This extension allows publishing of HTML snippets to the SSB galaxy.",
6- "version": "1.0",
7-
8- "browser_action": {
9- "default_icon": "icon.png",
10- "default_popup": "capsule.html"
11- },
12- "permissions": [
13- "activeTab"
14- ]
15-}
serializeSelectedHTML.jsView
@@ -1,288 +1,0 @@
1-/**
2- * Snapshooter is responsible for returning HTML and computed CSS of all nodes from selected DOM subtree.
3- *
4- * @param HTMLElement root Root node for the subtree that will be processed
5- * @returns {*} object with HTML as a string and CSS as an array of arrays of css properties
6- */
7-function Snapshooter(root) {
8- "use strict";
9-
10- // list of shorthand properties based on CSSShorthands.in from the Chromium
11- // code (https://code.google.com/p/chromium/codesearch)
12- // TODO this list should not be hardcoded here
13- var shorthandProperties = {
14- 'animation': 'animation',
15- 'background': 'background',
16- 'border': 'border',
17- 'border-top': 'borderTop',
18- 'border-right': 'borderRight',
19- 'border-bottom': 'borderBottom',
20- 'border-left': 'borderLeft',
21- 'border-width': 'borderWidth',
22- 'border-color': 'borderColor',
23- 'border-style': 'borderStyle',
24- 'border-radius': 'borderRadius',
25- 'border-image': 'borderImage',
26- 'border-spacing': 'borderSpacing',
27- 'flex': 'flex',
28- 'flex-flow': 'flexFlow',
29- 'font': 'font',
30- 'grid-area': 'gridArea',
31- 'grid-column': 'gridColumn',
32- 'grid-row': 'gridRow',
33- 'list-style': 'listStyle',
34- 'margin': 'margin',
35- 'marker': 'marker',
36- 'outline': 'outline',
37- 'overflow': 'overflow',
38- 'padding': 'padding',
39- 'text-decoration': 'textDecoration',
40- 'transition': 'transition',
41- '-webkit-border-after': 'webkitBorderAfter',
42- '-webkit-border-before': 'webkitBorderBefore',
43- '-webkit-border-end': 'webkitBorderEnd',
44- '-webkit-border-start': 'webkitBorderStart',
45- '-webkit-columns': 'webkitBorderColumns',
46- '-webkit-column-rule': 'webkitBorderColumnRule',
47- '-webkit-margin-collapse': 'webkitMarginCollapse',
48- '-webkit-mask': 'webkitMask',
49- '-webkit-mask-position': 'webkitMaskPosition',
50- '-webkit-mask-repeat': 'webkitMaskRepeat',
51- '-webkit-text-emphasis': 'webkitTextEmphasis',
52- '-webkit-transition': 'webkitTransition',
53- '-webkit-transform-origin': 'webkitTransformOrigin'
54- },
55- idCounter = 1;
56-
57- /**
58- * Changes CSSStyleDeclaration to simple Object removing unwanted properties
59- * ('1','2','parentRule','cssText' etc.) in the process.
60- *
61- * @param CSSStyleDeclaration style
62- * @returns {}
63- */
64- function styleDeclarationToSimpleObject(style) {
65- var i, l, cssName, camelCaseName,
66- output = {};
67-
68- for (i = 0, l = style.length; i < l; i++) {
69- output[style[i]] = style[style[i]];
70- }
71-
72- // Work around http://crbug.com/313670 (the "content" property is not
73- // present as a computed style indexed property value).
74- output.content = fixContentProperty(style.content);
75-
76- // Since shorthand properties are not available in the indexed array, copy
77- // them from named properties
78- for (cssName in shorthandProperties) {
79- if (shorthandProperties.hasOwnProperty(cssName)) {
80- camelCaseName = shorthandProperties[cssName];
81- output[cssName] = style[camelCaseName];
82- }
83- }
84-
85- return output;
86- }
87-
88- // Partial workaround for http://crbug.com/315028 (single words in the
89- // "content" property are not wrapped with quotes)
90- function fixContentProperty(content) {
91- var values, output, value, i, l;
92-
93- output = [];
94-
95- if (content) {
96- //content property can take multiple values - we need to split them up
97- //FIXME this won't work for '\''
98- values = content.match(/(?:[^\s']+|'[^']*')+/g);
99-
100- for (i = 0, l = values.length; i < l; i++) {
101- value = values[i];
102-
103- if (value.match(/^(url\()|(attr\()|normal|none|open-quote|close-quote|no-open-quote|no-close-quote|chapter_counter|'/g)) {
104- output.push(value);
105- } else {
106- output.push("'" + value + "'");
107- }
108- }
109- }
110-
111- return output.join(' ');
112- }
113-
114- function createID(node) {
115- //":snappysnippet_prefix:" is a prefix placeholder
116- return ':snappysnippet_prefix:' + node.tagName + '_' + idCounter++;
117- }
118-
119- function dumpCSS(node, pseudoElement) {
120- var styles;
121-
122- styles = node.ownerDocument.defaultView.getComputedStyle(node, pseudoElement);
123-
124- if (pseudoElement) {
125- //if we are dealing with pseudoelement, check if 'content' property isn't empty
126- //if it is, then we can ignore the whole element
127- if (!styles.getPropertyValue('content')) {
128- return null;
129- }
130- }
131-
132- return styleDeclarationToSimpleObject(styles);
133- }
134-
135- function cssObjectForElement(element, omitPseudoElements) {
136- return {
137- id: createID(element),
138- tagName: element.tagName,
139- node: dumpCSS(element, null),
140- before: omitPseudoElements ? null : dumpCSS(element, ':before'),
141- after: omitPseudoElements ? null : dumpCSS(element, ':after')
142- };
143- }
144-
145- function ancestorTagHTML(element, closingTag) {
146- var i, attr, value, idSeen,
147- result, attributes;
148-
149- if (closingTag) {
150- return '</' + element.tagName + '>';
151- }
152-
153- result = '<' + element.tagName;
154- attributes = element.attributes;
155-
156- for (i = 0; i < attributes.length; ++i) {
157- attr = attributes[i];
158-
159- if (attr.name.toLowerCase() === 'id') {
160- value = createID(element);
161- idSeen = true;
162- } else {
163- value = attr.value;
164- }
165-
166- result += ' ' + attributes[i].name + '="' + value + '"';
167- }
168-
169- if (!idSeen) {
170- result += ' id="' + createID(element) + '"';
171- }
172-
173- result += '>';
174-
175- return result;
176- }
177-
178- /**
179- * Replaces all relative URLs (in images, links etc.) with absolute URLs
180- * @param element
181- */
182- function relativeURLsToAbsoluteURLs(element) {
183- switch (element.nodeName) {
184- case 'A':
185- case 'AREA':
186- case 'LINK':
187- case 'BASE':
188- if (element.hasAttribute('href')) {
189- element.setAttribute('href', element.href);
190- }
191- break;
192- case 'IMG':
193- case 'IFRAME':
194- case 'INPUT':
195- case 'FRAME':
196- case 'SCRIPT':
197- if (element.hasAttribute('src')) {
198- element.setAttribute('src', element.src);
199- }
200- break;
201- case 'FORM':
202- if (element.hasAttribute('action')) {
203- element.setAttribute('action', element.action);
204- }
205- break;
206- }
207- }
208-
209- function init() {
210- var css = [],
211- ancestorCss = [],
212- descendants,
213- descendant,
214- htmlSegments,
215- leadingAncestorHtml,
216- trailingAncestorHtml,
217- reverseAncestors = [],
218- i, l,
219- parent,
220- clone;
221-
222- descendants = root.getElementsByTagName('*');
223-
224- parent = root.parentElement;
225- while (parent && parent !== document.body) {
226- reverseAncestors.push(parent);
227- parent = parent.parentElement;
228- }
229-
230- // First we go through all nodes and dump all CSS
231- css.push(cssObjectForElement(root));
232-
233- for (i = 0, l = descendants.length; i < l; i++) {
234- css.push(cssObjectForElement(descendants[i]));
235- }
236-
237- for (i = reverseAncestors.length - 1; i >= 0; i--) {
238- ancestorCss.push(cssObjectForElement(reverseAncestors[i], true));
239- }
240-
241- // Next we dump all HTML and update IDs
242- // Since we don't want to touch original DOM and we want to change IDs, we clone the original DOM subtree
243- clone = root.cloneNode(true);
244- descendants = clone.getElementsByTagName('*');
245- idCounter = 1;
246-
247- clone.setAttribute('id', createID(clone));
248-
249- for (i = 0, l = descendants.length; i < l; i++) {
250- descendant = descendants[i];
251- descendant.setAttribute('id', createID(descendant));
252- relativeURLsToAbsoluteURLs(descendant);
253- }
254-
255- // Build leading and trailing HTML for ancestors
256- htmlSegments = [];
257- for (i = reverseAncestors.length - 1; i >= 0; i--) {
258- htmlSegments.push(ancestorTagHTML(reverseAncestors[i]));
259- }
260- leadingAncestorHtml = htmlSegments.join('');
261-
262- htmlSegments = [];
263- for (i = 0, l = reverseAncestors.length; i < l; i++) {
264- htmlSegments.push(ancestorTagHTML(reverseAncestors[i], true));
265- }
266- trailingAncestorHtml = htmlSegments.join('');
267-
268- return JSON.stringify({
269- html: clone.outerHTML,
270- leadingAncestorHtml: leadingAncestorHtml,
271- trailingAncestorHtml: trailingAncestorHtml,
272- css: css,
273- ancestorCss: ancestorCss
274- });
275- }
276-
277- return init();
278-}
279-
280-var selectionDiv = document.createElement('div')
281-var selection = window.getSelection().getRangeAt(0)
282-var startNode = selection.startContainer.parentNode.cloneNode(true)
283-var endNode = selection.endContainer.parentNode.cloneNode(true)
284-
285-selectionDiv.appendChild(startNode)
286-selectionDiv.appendChild(endNode)
287-
288-Snapshooter(selectionDiv)

Built with git-ssb-web