UNPKG

209 kBJavaScriptView Raw
1(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
2'use strict';
3
4var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
5
6var _rasterizehtml = require('rasterizehtml');
7
8var _rasterizehtml2 = _interopRequireDefault(_rasterizehtml);
9
10function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
12function shadeColor(color, percent) {
13 var f = parseInt(color.slice(1), 16),
14 t = percent < 0 ? 0 : 255,
15 p = percent < 0 ? percent * -1 : percent;
16 var R = f >> 16,
17 G = f >> 8 & 0x00FF,
18 B = f & 0x0000FF;
19 return '#' + (0x1000000 + (Math.round((t - R) * p) + R) * 0x10000 + (Math.round((t - G) * p) + G) * 0x100 + (Math.round((t - B) * p) + B)).toString(16).slice(1);
20}
21
22function collectElements(article, selectors) {
23 var q = '';
24 [// default
25 ['heading', 'h1,h2,h3,h4,h5,h6'], ['paragraph', 'p']
26 //, ['sentence', ''] FIXME: sentence must be nested in p
27 , ['material', 'ul,ol,pre,table,blockquote']].forEach(function (d) {
28 var key = d[0];
29 q += ',' + (selectors[key] || d[1]);
30 });
31 return article.querySelectorAll(q.slice(1));
32}
33
34function fetchResultData(endpointURL, csrfToken, resolveCallback, rejectCallback) {
35 var credentials = { csrfToken: csrfToken };
36 if (!credentials.csrfToken) {
37 credentials.csrfToken = '';
38 }
39 var getJSON = function getJSON(url) {
40 var emptyData = { 'p': [] };
41 return new Promise(function (resolve, reject) {
42 var xhr = new XMLHttpRequest();
43 xhr.open('GET', url, true);
44 xhr.setRequestHeader('Accept', 'application/json');
45 xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
46 if (credentials.csrfToken !== '') {
47 xhr.setRequestHeader('X-CSRF-Token', credentials.csrfToken);
48 }
49 xhr.responseType = 'text';
50 xhr.onerror = function () {
51 var status = xhr.status;
52 reject(emptyData);
53 };
54 xhr.onload = function () {
55 var status = xhr.status,
56 response = xhr.response;
57 if (status === 200) {
58 response = response.replace(/^\]\)\}while\(1\);<\/x>/, '');
59 resolve(JSON.parse(response));
60 } else {
61 console.error('[ERROR] GET status: ', status);
62 reject(emptyData);
63 }
64 };
65 xhr.send();
66 });
67 };
68 return getJSON(endpointURL).then(function (data) {
69 return resolveCallback(data);
70 }, function (data) {
71 return rejectCallback(data);
72 });
73}
74
75function buildHTML(data, elements, styles) {
76 var html = '<html><head>';
77 html += Array.from(styles).map(function (s) {
78 // apply original style(s)
79 return s.outerHTML;
80 }).join('');
81 html += '</head><body>';
82
83 var colors0 = [// heatmap 11
84 '#2d96db', '#4aa8a6', '#64b977', '#99c95d', '#c5d062', '#f6d866', '#fab252', '#fd8e3e', '#fe6f43', '#fd515b', '#fb1b2a'];
85
86 var colors1 = [// pastel 12
87 '#9e0142', '#d0384d', '#ee6445', '#fa9c58', '#fdcd7b', '#fef0a7', '#f3faad', '#d0ec9c', '#98d5a4', '#5cb7a9', '#3582ba', '#5e4fa2'];
88
89 var colors = colors0 // heatmap (for now)
90 ,
91 pIndex = 0;
92 html += Array.from(elements).map(function (e) {
93 var n = document.importNode(e, true);
94 if (n.nodeName !== 'IMG') {
95 if (n.nodeName === 'P' && 'p' in data) {
96 // BETA only paragraph support
97 var v = data['p'][String(pIndex)];
98 if (v !== undefined) {
99 var color = '#ffffff';
100 try {
101 var i = Math.round(parseFloat(v) * 10);
102 color = shadeColor(colors[i], 0.55);
103 } catch (_e) {
104 console.error(e);
105 }
106 n.style.background = color;
107 n.style.backgroundColor = 'rgba(' + color + ', 0.9)';
108 }
109 pIndex += 1;
110 }
111 }
112 return n.outerHTML;
113 }).join('');
114 html += '</body></html>';
115 return html;
116}
117
118function makeCanvas(width, height) {
119 var container = document.getElementById('scrolliris_canvas_container'),
120 canvas = document.createElement('canvas');
121 canvas.setAttribute('id', 'scrolliris_canvas');
122 canvas.setAttribute('width', width * 0.5);
123 canvas.setAttribute('height', height * 0.5);
124 container.appendChild(canvas);
125 return canvas;
126}
127
128function drawCanvas(canvas, html, width, height, margin) {
129 _rasterizehtml2.default.drawHTML(html, canvas, {
130 zoom: 0.5,
131 width: width,
132 height: height
133 });
134
135 var dragging = false,
136 lastY = void 0,
137 marginTop = 0,
138 event = {};
139
140 canvas.addEventListener('mousedown', function (e) {
141 var evt = e || event;
142 canvas.style.cursor = 'grabbing';
143 dragging = true;
144 lastY = evt.clientY;
145 e.preventDefault();
146 }, false);
147
148 canvas.addEventListener('mouseup', function (e) {
149 canvas.style.cursor = 'grab';
150 dragging = false;
151 }, false);
152
153 window.addEventListener('mousemove', function (e) {
154 var evt = e || event;
155 if (dragging) {
156 var delta = evt.clientY - lastY;
157 lastY = evt.clientY;
158 marginTop += delta;
159 if (marginTop > 0) {
160 marginTop = 0;
161 } else if (marginTop < margin) {
162 marginTop = margin;
163 }
164 canvas.style.marginTop = marginTop + 'px';
165 }
166 e.preventDefault();
167 }, false);
168
169 window.addEventListener('mouseout', function (e) {
170 canvas.style.cursor = 'grab';
171 dragging = false;
172 }, false);
173}
174
175(function (doc, ctx) {
176 var config = {},
177 settings = {},
178 options = {};
179
180 if (ctx.hasOwnProperty('config') && _typeof(ctx.config) === 'object') {
181 config = ctx['config'];
182 // pass
183 }
184 if (ctx.hasOwnProperty('settings') && _typeof(ctx.options) === 'object') {
185 settings = ctx['settings'];
186 if (!settings.endpointURL) {
187 console.error('endpointURL is required');
188 return false;
189 }
190 }
191 if (ctx.hasOwnProperty('options') && _typeof(ctx.options) === 'object') {
192 options = ctx['options'];
193 }
194
195 var selectors = options.selectors || {};
196 var article = doc.querySelector(selectors.article || 'body article');
197
198 // collect elements and styles
199 var elements = collectElements(article, selectors),
200 styles = doc.querySelectorAll('style') || [];
201
202 // NOTE:
203 // Use <article>'s clientHeight?
204 var elm = doc.documentElement;
205 var docWidth = Math.max(doc.body.clientWidth, elm.clientWidth, elm.scrollWidth);
206 var docHeight = Math.max(doc.body.clientHeight, elm.clientHeight, elm.scrollHeight);
207
208 var draw = function draw(data) {
209 var html = buildHTML(data, elements, styles),
210 canvas = makeCanvas(docWidth, docHeight);
211 // draw minimap
212 var canvasHeight = 290 // container main area
213 ,
214 headerHeight = 22,
215 footerHeiht = 22,
216 frameMargin = 9 // {left|bottom} 9px
217 ,
218 scale = 0.5;
219 var margin = -1 * (docHeight * scale / canvasHeight * 100) + (headerHeight + footerHeiht + frameMargin);
220 drawCanvas(canvas, html, docWidth, docHeight, margin);
221 };
222
223 fetchResultData(settings.endpointURL, settings.csrfToken, function (data) {
224 // resolve
225 draw(data);
226 }, function (data) {
227 // reject
228 // FIXME
229 draw(data);
230 });
231})(window.parent.document, (window.ScrollirisReadabilityReflector || {}).Context);
232
233},{"rasterizehtml":39}],2:[function(require,module,exports){
234// UMD header
235(function (root, factory) {
236 if (typeof define === 'function' && define.amd) {
237 define(factory);
238 } else if (typeof exports === 'object') {
239 module.exports = factory();
240 } else {
241 root.ayepromise = factory();
242 }
243}(this, function () {
244 'use strict';
245
246 var ayepromise = {};
247
248 /* Wrap an arbitrary number of functions and allow only one of them to be
249 executed and only once */
250 var once = function () {
251 var wasCalled = false;
252
253 return function wrapper(wrappedFunction) {
254 return function () {
255 if (wasCalled) {
256 return;
257 }
258 wasCalled = true;
259 wrappedFunction.apply(null, arguments);
260 };
261 };
262 };
263
264 var getThenableIfExists = function (obj) {
265 // Make sure we only access the accessor once as required by the spec
266 var then = obj && obj.then;
267
268 if (typeof obj === "object" && typeof then === "function") {
269 // Bind function back to it's object (so fan's of 'this' don't get sad)
270 return function() { return then.apply(obj, arguments); };
271 }
272 };
273
274 var aThenHandler = function (onFulfilled, onRejected) {
275 var defer = ayepromise.defer();
276
277 var doHandlerCall = function (func, value) {
278 setTimeout(function () {
279 var returnValue;
280 try {
281 returnValue = func(value);
282 } catch (e) {
283 defer.reject(e);
284 return;
285 }
286
287 if (returnValue === defer.promise) {
288 defer.reject(new TypeError('Cannot resolve promise with itself'));
289 } else {
290 defer.resolve(returnValue);
291 }
292 }, 1);
293 };
294
295 var callFulfilled = function (value) {
296 if (onFulfilled && onFulfilled.call) {
297 doHandlerCall(onFulfilled, value);
298 } else {
299 defer.resolve(value);
300 }
301 };
302
303 var callRejected = function (value) {
304 if (onRejected && onRejected.call) {
305 doHandlerCall(onRejected, value);
306 } else {
307 defer.reject(value);
308 }
309 };
310
311 return {
312 promise: defer.promise,
313 handle: function (state, value) {
314 if (state === FULFILLED) {
315 callFulfilled(value);
316 } else {
317 callRejected(value);
318 }
319 }
320 };
321 };
322
323 // States
324 var PENDING = 0,
325 FULFILLED = 1,
326 REJECTED = 2;
327
328 ayepromise.defer = function () {
329 var state = PENDING,
330 outcome,
331 thenHandlers = [];
332
333 var doSettle = function (settledState, value) {
334 state = settledState;
335 // persist for handlers registered after settling
336 outcome = value;
337
338 thenHandlers.forEach(function (then) {
339 then.handle(state, outcome);
340 });
341
342 // Discard all references to handlers to be garbage collected
343 thenHandlers = null;
344 };
345
346 var doFulfill = function (value) {
347 doSettle(FULFILLED, value);
348 };
349
350 var doReject = function (error) {
351 doSettle(REJECTED, error);
352 };
353
354 var registerThenHandler = function (onFulfilled, onRejected) {
355 var thenHandler = aThenHandler(onFulfilled, onRejected);
356
357 if (state === PENDING) {
358 thenHandlers.push(thenHandler);
359 } else {
360 thenHandler.handle(state, outcome);
361 }
362
363 return thenHandler.promise;
364 };
365
366 var safelyResolveThenable = function (thenable) {
367 // Either fulfill, reject or reject with error
368 var onceWrapper = once();
369 try {
370 thenable(
371 onceWrapper(transparentlyResolveThenablesAndSettle),
372 onceWrapper(doReject)
373 );
374 } catch (e) {
375 onceWrapper(doReject)(e);
376 }
377 };
378
379 var transparentlyResolveThenablesAndSettle = function (value) {
380 var thenable;
381
382 try {
383 thenable = getThenableIfExists(value);
384 } catch (e) {
385 doReject(e);
386 return;
387 }
388
389 if (thenable) {
390 safelyResolveThenable(thenable);
391 } else {
392 doFulfill(value);
393 }
394 };
395
396 var onceWrapper = once();
397 return {
398 resolve: onceWrapper(transparentlyResolveThenablesAndSettle),
399 reject: onceWrapper(doReject),
400 promise: {
401 then: registerThenHandler,
402 fail: function (onRejected) {
403 return registerThenHandler(null, onRejected);
404 }
405 }
406 };
407 };
408
409 return ayepromise;
410}));
411
412},{}],3:[function(require,module,exports){
413// Copyright Joyent, Inc. and other Node contributors.
414//
415// Permission is hereby granted, free of charge, to any person obtaining a
416// copy of this software and associated documentation files (the
417// "Software"), to deal in the Software without restriction, including
418// without limitation the rights to use, copy, modify, merge, publish,
419// distribute, sublicense, and/or sell copies of the Software, and to permit
420// persons to whom the Software is furnished to do so, subject to the
421// following conditions:
422//
423// The above copyright notice and this permission notice shall be included
424// in all copies or substantial portions of the Software.
425//
426// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
427// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
428// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
429// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
430// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
431// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
432// USE OR OTHER DEALINGS IN THE SOFTWARE.
433
434'use strict';
435
436var punycode = require('punycode');
437var util = require('./util');
438
439exports.parse = urlParse;
440exports.resolve = urlResolve;
441exports.resolveObject = urlResolveObject;
442exports.format = urlFormat;
443
444exports.Url = Url;
445
446function Url() {
447 this.protocol = null;
448 this.slashes = null;
449 this.auth = null;
450 this.host = null;
451 this.port = null;
452 this.hostname = null;
453 this.hash = null;
454 this.search = null;
455 this.query = null;
456 this.pathname = null;
457 this.path = null;
458 this.href = null;
459}
460
461// Reference: RFC 3986, RFC 1808, RFC 2396
462
463// define these here so at least they only have to be
464// compiled once on the first module load.
465var protocolPattern = /^([a-z0-9.+-]+:)/i,
466 portPattern = /:[0-9]*$/,
467
468 // Special case for a simple path URL
469 simplePathPattern = /^(\/\/?(?!\/)[^\?\s]*)(\?[^\s]*)?$/,
470
471 // RFC 2396: characters reserved for delimiting URLs.
472 // We actually just auto-escape these.
473 delims = ['<', '>', '"', '`', ' ', '\r', '\n', '\t'],
474
475 // RFC 2396: characters not allowed for various reasons.
476 unwise = ['{', '}', '|', '\\', '^', '`'].concat(delims),
477
478 // Allowed by RFCs, but cause of XSS attacks. Always escape these.
479 autoEscape = ['\''].concat(unwise),
480 // Characters that are never ever allowed in a hostname.
481 // Note that any invalid chars are also handled, but these
482 // are the ones that are *expected* to be seen, so we fast-path
483 // them.
484 nonHostChars = ['%', '/', '?', ';', '#'].concat(autoEscape),
485 hostEndingChars = ['/', '?', '#'],
486 hostnameMaxLen = 255,
487 hostnamePartPattern = /^[+a-z0-9A-Z_-]{0,63}$/,
488 hostnamePartStart = /^([+a-z0-9A-Z_-]{0,63})(.*)$/,
489 // protocols that can allow "unsafe" and "unwise" chars.
490 unsafeProtocol = {
491 'javascript': true,
492 'javascript:': true
493 },
494 // protocols that never have a hostname.
495 hostlessProtocol = {
496 'javascript': true,
497 'javascript:': true
498 },
499 // protocols that always contain a // bit.
500 slashedProtocol = {
501 'http': true,
502 'https': true,
503 'ftp': true,
504 'gopher': true,
505 'file': true,
506 'http:': true,
507 'https:': true,
508 'ftp:': true,
509 'gopher:': true,
510 'file:': true
511 },
512 querystring = require('querystring');
513
514function urlParse(url, parseQueryString, slashesDenoteHost) {
515 if (url && util.isObject(url) && url instanceof Url) return url;
516
517 var u = new Url;
518 u.parse(url, parseQueryString, slashesDenoteHost);
519 return u;
520}
521
522Url.prototype.parse = function(url, parseQueryString, slashesDenoteHost) {
523 if (!util.isString(url)) {
524 throw new TypeError("Parameter 'url' must be a string, not " + typeof url);
525 }
526
527 // Copy chrome, IE, opera backslash-handling behavior.
528 // Back slashes before the query string get converted to forward slashes
529 // See: https://code.google.com/p/chromium/issues/detail?id=25916
530 var queryIndex = url.indexOf('?'),
531 splitter =
532 (queryIndex !== -1 && queryIndex < url.indexOf('#')) ? '?' : '#',
533 uSplit = url.split(splitter),
534 slashRegex = /\\/g;
535 uSplit[0] = uSplit[0].replace(slashRegex, '/');
536 url = uSplit.join(splitter);
537
538 var rest = url;
539
540 // trim before proceeding.
541 // This is to support parse stuff like " http://foo.com \n"
542 rest = rest.trim();
543
544 if (!slashesDenoteHost && url.split('#').length === 1) {
545 // Try fast path regexp
546 var simplePath = simplePathPattern.exec(rest);
547 if (simplePath) {
548 this.path = rest;
549 this.href = rest;
550 this.pathname = simplePath[1];
551 if (simplePath[2]) {
552 this.search = simplePath[2];
553 if (parseQueryString) {
554 this.query = querystring.parse(this.search.substr(1));
555 } else {
556 this.query = this.search.substr(1);
557 }
558 } else if (parseQueryString) {
559 this.search = '';
560 this.query = {};
561 }
562 return this;
563 }
564 }
565
566 var proto = protocolPattern.exec(rest);
567 if (proto) {
568 proto = proto[0];
569 var lowerProto = proto.toLowerCase();
570 this.protocol = lowerProto;
571 rest = rest.substr(proto.length);
572 }
573
574 // figure out if it's got a host
575 // user@server is *always* interpreted as a hostname, and url
576 // resolution will treat //foo/bar as host=foo,path=bar because that's
577 // how the browser resolves relative URLs.
578 if (slashesDenoteHost || proto || rest.match(/^\/\/[^@\/]+@[^@\/]+/)) {
579 var slashes = rest.substr(0, 2) === '//';
580 if (slashes && !(proto && hostlessProtocol[proto])) {
581 rest = rest.substr(2);
582 this.slashes = true;
583 }
584 }
585
586 if (!hostlessProtocol[proto] &&
587 (slashes || (proto && !slashedProtocol[proto]))) {
588
589 // there's a hostname.
590 // the first instance of /, ?, ;, or # ends the host.
591 //
592 // If there is an @ in the hostname, then non-host chars *are* allowed
593 // to the left of the last @ sign, unless some host-ending character
594 // comes *before* the @-sign.
595 // URLs are obnoxious.
596 //
597 // ex:
598 // http://a@b@c/ => user:a@b host:c
599 // http://a@b?@c => user:a host:c path:/?@c
600
601 // v0.12 TODO(isaacs): This is not quite how Chrome does things.
602 // Review our test case against browsers more comprehensively.
603
604 // find the first instance of any hostEndingChars
605 var hostEnd = -1;
606 for (var i = 0; i < hostEndingChars.length; i++) {
607 var hec = rest.indexOf(hostEndingChars[i]);
608 if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
609 hostEnd = hec;
610 }
611
612 // at this point, either we have an explicit point where the
613 // auth portion cannot go past, or the last @ char is the decider.
614 var auth, atSign;
615 if (hostEnd === -1) {
616 // atSign can be anywhere.
617 atSign = rest.lastIndexOf('@');
618 } else {
619 // atSign must be in auth portion.
620 // http://a@b/c@d => host:b auth:a path:/c@d
621 atSign = rest.lastIndexOf('@', hostEnd);
622 }
623
624 // Now we have a portion which is definitely the auth.
625 // Pull that off.
626 if (atSign !== -1) {
627 auth = rest.slice(0, atSign);
628 rest = rest.slice(atSign + 1);
629 this.auth = decodeURIComponent(auth);
630 }
631
632 // the host is the remaining to the left of the first non-host char
633 hostEnd = -1;
634 for (var i = 0; i < nonHostChars.length; i++) {
635 var hec = rest.indexOf(nonHostChars[i]);
636 if (hec !== -1 && (hostEnd === -1 || hec < hostEnd))
637 hostEnd = hec;
638 }
639 // if we still have not hit it, then the entire thing is a host.
640 if (hostEnd === -1)
641 hostEnd = rest.length;
642
643 this.host = rest.slice(0, hostEnd);
644 rest = rest.slice(hostEnd);
645
646 // pull out port.
647 this.parseHost();
648
649 // we've indicated that there is a hostname,
650 // so even if it's empty, it has to be present.
651 this.hostname = this.hostname || '';
652
653 // if hostname begins with [ and ends with ]
654 // assume that it's an IPv6 address.
655 var ipv6Hostname = this.hostname[0] === '[' &&
656 this.hostname[this.hostname.length - 1] === ']';
657
658 // validate a little.
659 if (!ipv6Hostname) {
660 var hostparts = this.hostname.split(/\./);
661 for (var i = 0, l = hostparts.length; i < l; i++) {
662 var part = hostparts[i];
663 if (!part) continue;
664 if (!part.match(hostnamePartPattern)) {
665 var newpart = '';
666 for (var j = 0, k = part.length; j < k; j++) {
667 if (part.charCodeAt(j) > 127) {
668 // we replace non-ASCII char with a temporary placeholder
669 // we need this to make sure size of hostname is not
670 // broken by replacing non-ASCII by nothing
671 newpart += 'x';
672 } else {
673 newpart += part[j];
674 }
675 }
676 // we test again with ASCII char only
677 if (!newpart.match(hostnamePartPattern)) {
678 var validParts = hostparts.slice(0, i);
679 var notHost = hostparts.slice(i + 1);
680 var bit = part.match(hostnamePartStart);
681 if (bit) {
682 validParts.push(bit[1]);
683 notHost.unshift(bit[2]);
684 }
685 if (notHost.length) {
686 rest = '/' + notHost.join('.') + rest;
687 }
688 this.hostname = validParts.join('.');
689 break;
690 }
691 }
692 }
693 }
694
695 if (this.hostname.length > hostnameMaxLen) {
696 this.hostname = '';
697 } else {
698 // hostnames are always lower case.
699 this.hostname = this.hostname.toLowerCase();
700 }
701
702 if (!ipv6Hostname) {
703 // IDNA Support: Returns a punycoded representation of "domain".
704 // It only converts parts of the domain name that
705 // have non-ASCII characters, i.e. it doesn't matter if
706 // you call it with a domain that already is ASCII-only.
707 this.hostname = punycode.toASCII(this.hostname);
708 }
709
710 var p = this.port ? ':' + this.port : '';
711 var h = this.hostname || '';
712 this.host = h + p;
713 this.href += this.host;
714
715 // strip [ and ] from the hostname
716 // the host field still retains them, though
717 if (ipv6Hostname) {
718 this.hostname = this.hostname.substr(1, this.hostname.length - 2);
719 if (rest[0] !== '/') {
720 rest = '/' + rest;
721 }
722 }
723 }
724
725 // now rest is set to the post-host stuff.
726 // chop off any delim chars.
727 if (!unsafeProtocol[lowerProto]) {
728
729 // First, make 100% sure that any "autoEscape" chars get
730 // escaped, even if encodeURIComponent doesn't think they
731 // need to be.
732 for (var i = 0, l = autoEscape.length; i < l; i++) {
733 var ae = autoEscape[i];
734 if (rest.indexOf(ae) === -1)
735 continue;
736 var esc = encodeURIComponent(ae);
737 if (esc === ae) {
738 esc = escape(ae);
739 }
740 rest = rest.split(ae).join(esc);
741 }
742 }
743
744
745 // chop off from the tail first.
746 var hash = rest.indexOf('#');
747 if (hash !== -1) {
748 // got a fragment string.
749 this.hash = rest.substr(hash);
750 rest = rest.slice(0, hash);
751 }
752 var qm = rest.indexOf('?');
753 if (qm !== -1) {
754 this.search = rest.substr(qm);
755 this.query = rest.substr(qm + 1);
756 if (parseQueryString) {
757 this.query = querystring.parse(this.query);
758 }
759 rest = rest.slice(0, qm);
760 } else if (parseQueryString) {
761 // no query string, but parseQueryString still requested
762 this.search = '';
763 this.query = {};
764 }
765 if (rest) this.pathname = rest;
766 if (slashedProtocol[lowerProto] &&
767 this.hostname && !this.pathname) {
768 this.pathname = '/';
769 }
770
771 //to support http.request
772 if (this.pathname || this.search) {
773 var p = this.pathname || '';
774 var s = this.search || '';
775 this.path = p + s;
776 }
777
778 // finally, reconstruct the href based on what has been validated.
779 this.href = this.format();
780 return this;
781};
782
783// format a parsed object into a url string
784function urlFormat(obj) {
785 // ensure it's an object, and not a string url.
786 // If it's an obj, this is a no-op.
787 // this way, you can call url_format() on strings
788 // to clean up potentially wonky urls.
789 if (util.isString(obj)) obj = urlParse(obj);
790 if (!(obj instanceof Url)) return Url.prototype.format.call(obj);
791 return obj.format();
792}
793
794Url.prototype.format = function() {
795 var auth = this.auth || '';
796 if (auth) {
797 auth = encodeURIComponent(auth);
798 auth = auth.replace(/%3A/i, ':');
799 auth += '@';
800 }
801
802 var protocol = this.protocol || '',
803 pathname = this.pathname || '',
804 hash = this.hash || '',
805 host = false,
806 query = '';
807
808 if (this.host) {
809 host = auth + this.host;
810 } else if (this.hostname) {
811 host = auth + (this.hostname.indexOf(':') === -1 ?
812 this.hostname :
813 '[' + this.hostname + ']');
814 if (this.port) {
815 host += ':' + this.port;
816 }
817 }
818
819 if (this.query &&
820 util.isObject(this.query) &&
821 Object.keys(this.query).length) {
822 query = querystring.stringify(this.query);
823 }
824
825 var search = this.search || (query && ('?' + query)) || '';
826
827 if (protocol && protocol.substr(-1) !== ':') protocol += ':';
828
829 // only the slashedProtocols get the //. Not mailto:, xmpp:, etc.
830 // unless they had them to begin with.
831 if (this.slashes ||
832 (!protocol || slashedProtocol[protocol]) && host !== false) {
833 host = '//' + (host || '');
834 if (pathname && pathname.charAt(0) !== '/') pathname = '/' + pathname;
835 } else if (!host) {
836 host = '';
837 }
838
839 if (hash && hash.charAt(0) !== '#') hash = '#' + hash;
840 if (search && search.charAt(0) !== '?') search = '?' + search;
841
842 pathname = pathname.replace(/[?#]/g, function(match) {
843 return encodeURIComponent(match);
844 });
845 search = search.replace('#', '%23');
846
847 return protocol + host + pathname + search + hash;
848};
849
850function urlResolve(source, relative) {
851 return urlParse(source, false, true).resolve(relative);
852}
853
854Url.prototype.resolve = function(relative) {
855 return this.resolveObject(urlParse(relative, false, true)).format();
856};
857
858function urlResolveObject(source, relative) {
859 if (!source) return relative;
860 return urlParse(source, false, true).resolveObject(relative);
861}
862
863Url.prototype.resolveObject = function(relative) {
864 if (util.isString(relative)) {
865 var rel = new Url();
866 rel.parse(relative, false, true);
867 relative = rel;
868 }
869
870 var result = new Url();
871 var tkeys = Object.keys(this);
872 for (var tk = 0; tk < tkeys.length; tk++) {
873 var tkey = tkeys[tk];
874 result[tkey] = this[tkey];
875 }
876
877 // hash is always overridden, no matter what.
878 // even href="" will remove it.
879 result.hash = relative.hash;
880
881 // if the relative url is empty, then there's nothing left to do here.
882 if (relative.href === '') {
883 result.href = result.format();
884 return result;
885 }
886
887 // hrefs like //foo/bar always cut to the protocol.
888 if (relative.slashes && !relative.protocol) {
889 // take everything except the protocol from relative
890 var rkeys = Object.keys(relative);
891 for (var rk = 0; rk < rkeys.length; rk++) {
892 var rkey = rkeys[rk];
893 if (rkey !== 'protocol')
894 result[rkey] = relative[rkey];
895 }
896
897 //urlParse appends trailing / to urls like http://www.example.com
898 if (slashedProtocol[result.protocol] &&
899 result.hostname && !result.pathname) {
900 result.path = result.pathname = '/';
901 }
902
903 result.href = result.format();
904 return result;
905 }
906
907 if (relative.protocol && relative.protocol !== result.protocol) {
908 // if it's a known url protocol, then changing
909 // the protocol does weird things
910 // first, if it's not file:, then we MUST have a host,
911 // and if there was a path
912 // to begin with, then we MUST have a path.
913 // if it is file:, then the host is dropped,
914 // because that's known to be hostless.
915 // anything else is assumed to be absolute.
916 if (!slashedProtocol[relative.protocol]) {
917 var keys = Object.keys(relative);
918 for (var v = 0; v < keys.length; v++) {
919 var k = keys[v];
920 result[k] = relative[k];
921 }
922 result.href = result.format();
923 return result;
924 }
925
926 result.protocol = relative.protocol;
927 if (!relative.host && !hostlessProtocol[relative.protocol]) {
928 var relPath = (relative.pathname || '').split('/');
929 while (relPath.length && !(relative.host = relPath.shift()));
930 if (!relative.host) relative.host = '';
931 if (!relative.hostname) relative.hostname = '';
932 if (relPath[0] !== '') relPath.unshift('');
933 if (relPath.length < 2) relPath.unshift('');
934 result.pathname = relPath.join('/');
935 } else {
936 result.pathname = relative.pathname;
937 }
938 result.search = relative.search;
939 result.query = relative.query;
940 result.host = relative.host || '';
941 result.auth = relative.auth;
942 result.hostname = relative.hostname || relative.host;
943 result.port = relative.port;
944 // to support http.request
945 if (result.pathname || result.search) {
946 var p = result.pathname || '';
947 var s = result.search || '';
948 result.path = p + s;
949 }
950 result.slashes = result.slashes || relative.slashes;
951 result.href = result.format();
952 return result;
953 }
954
955 var isSourceAbs = (result.pathname && result.pathname.charAt(0) === '/'),
956 isRelAbs = (
957 relative.host ||
958 relative.pathname && relative.pathname.charAt(0) === '/'
959 ),
960 mustEndAbs = (isRelAbs || isSourceAbs ||
961 (result.host && relative.pathname)),
962 removeAllDots = mustEndAbs,
963 srcPath = result.pathname && result.pathname.split('/') || [],
964 relPath = relative.pathname && relative.pathname.split('/') || [],
965 psychotic = result.protocol && !slashedProtocol[result.protocol];
966
967 // if the url is a non-slashed url, then relative
968 // links like ../.. should be able
969 // to crawl up to the hostname, as well. This is strange.
970 // result.protocol has already been set by now.
971 // Later on, put the first path part into the host field.
972 if (psychotic) {
973 result.hostname = '';
974 result.port = null;
975 if (result.host) {
976 if (srcPath[0] === '') srcPath[0] = result.host;
977 else srcPath.unshift(result.host);
978 }
979 result.host = '';
980 if (relative.protocol) {
981 relative.hostname = null;
982 relative.port = null;
983 if (relative.host) {
984 if (relPath[0] === '') relPath[0] = relative.host;
985 else relPath.unshift(relative.host);
986 }
987 relative.host = null;
988 }
989 mustEndAbs = mustEndAbs && (relPath[0] === '' || srcPath[0] === '');
990 }
991
992 if (isRelAbs) {
993 // it's absolute.
994 result.host = (relative.host || relative.host === '') ?
995 relative.host : result.host;
996 result.hostname = (relative.hostname || relative.hostname === '') ?
997 relative.hostname : result.hostname;
998 result.search = relative.search;
999 result.query = relative.query;
1000 srcPath = relPath;
1001 // fall through to the dot-handling below.
1002 } else if (relPath.length) {
1003 // it's relative
1004 // throw away the existing file, and take the new path instead.
1005 if (!srcPath) srcPath = [];
1006 srcPath.pop();
1007 srcPath = srcPath.concat(relPath);
1008 result.search = relative.search;
1009 result.query = relative.query;
1010 } else if (!util.isNullOrUndefined(relative.search)) {
1011 // just pull out the search.
1012 // like href='?foo'.
1013 // Put this after the other two cases because it simplifies the booleans
1014 if (psychotic) {
1015 result.hostname = result.host = srcPath.shift();
1016 //occationaly the auth can get stuck only in host
1017 //this especially happens in cases like
1018 //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
1019 var authInHost = result.host && result.host.indexOf('@') > 0 ?
1020 result.host.split('@') : false;
1021 if (authInHost) {
1022 result.auth = authInHost.shift();
1023 result.host = result.hostname = authInHost.shift();
1024 }
1025 }
1026 result.search = relative.search;
1027 result.query = relative.query;
1028 //to support http.request
1029 if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
1030 result.path = (result.pathname ? result.pathname : '') +
1031 (result.search ? result.search : '');
1032 }
1033 result.href = result.format();
1034 return result;
1035 }
1036
1037 if (!srcPath.length) {
1038 // no path at all. easy.
1039 // we've already handled the other stuff above.
1040 result.pathname = null;
1041 //to support http.request
1042 if (result.search) {
1043 result.path = '/' + result.search;
1044 } else {
1045 result.path = null;
1046 }
1047 result.href = result.format();
1048 return result;
1049 }
1050
1051 // if a url ENDs in . or .., then it must get a trailing slash.
1052 // however, if it ends in anything else non-slashy,
1053 // then it must NOT get a trailing slash.
1054 var last = srcPath.slice(-1)[0];
1055 var hasTrailingSlash = (
1056 (result.host || relative.host || srcPath.length > 1) &&
1057 (last === '.' || last === '..') || last === '');
1058
1059 // strip single dots, resolve double dots to parent dir
1060 // if the path tries to go above the root, `up` ends up > 0
1061 var up = 0;
1062 for (var i = srcPath.length; i >= 0; i--) {
1063 last = srcPath[i];
1064 if (last === '.') {
1065 srcPath.splice(i, 1);
1066 } else if (last === '..') {
1067 srcPath.splice(i, 1);
1068 up++;
1069 } else if (up) {
1070 srcPath.splice(i, 1);
1071 up--;
1072 }
1073 }
1074
1075 // if the path is allowed to go above the root, restore leading ..s
1076 if (!mustEndAbs && !removeAllDots) {
1077 for (; up--; up) {
1078 srcPath.unshift('..');
1079 }
1080 }
1081
1082 if (mustEndAbs && srcPath[0] !== '' &&
1083 (!srcPath[0] || srcPath[0].charAt(0) !== '/')) {
1084 srcPath.unshift('');
1085 }
1086
1087 if (hasTrailingSlash && (srcPath.join('/').substr(-1) !== '/')) {
1088 srcPath.push('');
1089 }
1090
1091 var isAbsolute = srcPath[0] === '' ||
1092 (srcPath[0] && srcPath[0].charAt(0) === '/');
1093
1094 // put the host back
1095 if (psychotic) {
1096 result.hostname = result.host = isAbsolute ? '' :
1097 srcPath.length ? srcPath.shift() : '';
1098 //occationaly the auth can get stuck only in host
1099 //this especially happens in cases like
1100 //url.resolveObject('mailto:local1@domain1', 'local2@domain2')
1101 var authInHost = result.host && result.host.indexOf('@') > 0 ?
1102 result.host.split('@') : false;
1103 if (authInHost) {
1104 result.auth = authInHost.shift();
1105 result.host = result.hostname = authInHost.shift();
1106 }
1107 }
1108
1109 mustEndAbs = mustEndAbs || (result.host && srcPath.length);
1110
1111 if (mustEndAbs && !isAbsolute) {
1112 srcPath.unshift('');
1113 }
1114
1115 if (!srcPath.length) {
1116 result.pathname = null;
1117 result.path = null;
1118 } else {
1119 result.pathname = srcPath.join('/');
1120 }
1121
1122 //to support request.http
1123 if (!util.isNull(result.pathname) || !util.isNull(result.search)) {
1124 result.path = (result.pathname ? result.pathname : '') +
1125 (result.search ? result.search : '');
1126 }
1127 result.auth = relative.auth || result.auth;
1128 result.slashes = result.slashes || relative.slashes;
1129 result.href = result.format();
1130 return result;
1131};
1132
1133Url.prototype.parseHost = function() {
1134 var host = this.host;
1135 var port = portPattern.exec(host);
1136 if (port) {
1137 port = port[0];
1138 if (port !== ':') {
1139 this.port = port.substr(1);
1140 }
1141 host = host.substr(0, host.length - port.length);
1142 }
1143 if (host) this.hostname = host;
1144};
1145
1146},{"./util":4,"punycode":35,"querystring":38}],4:[function(require,module,exports){
1147'use strict';
1148
1149module.exports = {
1150 isString: function(arg) {
1151 return typeof(arg) === 'string';
1152 },
1153 isObject: function(arg) {
1154 return typeof(arg) === 'object' && arg !== null;
1155 },
1156 isNull: function(arg) {
1157 return arg === null;
1158 },
1159 isNullOrUndefined: function(arg) {
1160 return arg == null;
1161 }
1162};
1163
1164},{}],5:[function(require,module,exports){
1165module.exports = (function() {
1166 /*
1167 * Generated by PEG.js 0.8.0.
1168 *
1169 * http://pegjs.majda.cz/
1170 */
1171
1172 function peg$subclass(child, parent) {
1173 function ctor() { this.constructor = child; }
1174 ctor.prototype = parent.prototype;
1175 child.prototype = new ctor();
1176 }
1177
1178 function SyntaxError(message, expected, found, offset, line, column) {
1179 this.message = message;
1180 this.expected = expected;
1181 this.found = found;
1182 this.offset = offset;
1183 this.line = line;
1184 this.column = column;
1185
1186 this.name = "SyntaxError";
1187 }
1188
1189 peg$subclass(SyntaxError, Error);
1190
1191 function parse(input) {
1192 var options = arguments.length > 1 ? arguments[1] : {},
1193
1194 peg$FAILED = {},
1195
1196 peg$startRuleFunctions = { start: peg$parsestart },
1197 peg$startRuleFunction = peg$parsestart,
1198
1199 peg$c0 = [],
1200 peg$c1 = function() { return []},
1201 peg$c2 = peg$FAILED,
1202 peg$c3 = ",",
1203 peg$c4 = { type: "literal", value: ",", description: "\",\"" },
1204 peg$c5 = function(x, xs) { return [x].concat(xs); },
1205 peg$c6 = function(entry) { return [entry]; },
1206 peg$c7 = function(url, format) { return {url: url, format: format}; },
1207 peg$c8 = function(url) { return {url: url}; },
1208 peg$c9 = "url(",
1209 peg$c10 = { type: "literal", value: "url(", description: "\"url(\"" },
1210 peg$c11 = ")",
1211 peg$c12 = { type: "literal", value: ")", description: "\")\"" },
1212 peg$c13 = function(value) { return value; },
1213 peg$c14 = "format(",
1214 peg$c15 = { type: "literal", value: "format(", description: "\"format(\"" },
1215 peg$c16 = "local(",
1216 peg$c17 = { type: "literal", value: "local(", description: "\"local(\"" },
1217 peg$c18 = function(value) { return {local: value}; },
1218 peg$c19 = /^[^)]/,
1219 peg$c20 = { type: "class", value: "[^)]", description: "[^)]" },
1220 peg$c21 = function(chars) { return util.extractValue(chars.join("")); },
1221 peg$c22 = /^[ \t\r\n\f]/,
1222 peg$c23 = { type: "class", value: "[ \\t\\r\\n\\f]", description: "[ \\t\\r\\n\\f]" },
1223
1224 peg$currPos = 0,
1225 peg$reportedPos = 0,
1226 peg$cachedPos = 0,
1227 peg$cachedPosDetails = { line: 1, column: 1, seenCR: false },
1228 peg$maxFailPos = 0,
1229 peg$maxFailExpected = [],
1230 peg$silentFails = 0,
1231
1232 peg$result;
1233
1234 if ("startRule" in options) {
1235 if (!(options.startRule in peg$startRuleFunctions)) {
1236 throw new Error("Can't start parsing from rule \"" + options.startRule + "\".");
1237 }
1238
1239 peg$startRuleFunction = peg$startRuleFunctions[options.startRule];
1240 }
1241
1242 function text() {
1243 return input.substring(peg$reportedPos, peg$currPos);
1244 }
1245
1246 function offset() {
1247 return peg$reportedPos;
1248 }
1249
1250 function line() {
1251 return peg$computePosDetails(peg$reportedPos).line;
1252 }
1253
1254 function column() {
1255 return peg$computePosDetails(peg$reportedPos).column;
1256 }
1257
1258 function expected(description) {
1259 throw peg$buildException(
1260 null,
1261 [{ type: "other", description: description }],
1262 peg$reportedPos
1263 );
1264 }
1265
1266 function error(message) {
1267 throw peg$buildException(message, null, peg$reportedPos);
1268 }
1269
1270 function peg$computePosDetails(pos) {
1271 function advance(details, startPos, endPos) {
1272 var p, ch;
1273
1274 for (p = startPos; p < endPos; p++) {
1275 ch = input.charAt(p);
1276 if (ch === "\n") {
1277 if (!details.seenCR) { details.line++; }
1278 details.column = 1;
1279 details.seenCR = false;
1280 } else if (ch === "\r" || ch === "\u2028" || ch === "\u2029") {
1281 details.line++;
1282 details.column = 1;
1283 details.seenCR = true;
1284 } else {
1285 details.column++;
1286 details.seenCR = false;
1287 }
1288 }
1289 }
1290
1291 if (peg$cachedPos !== pos) {
1292 if (peg$cachedPos > pos) {
1293 peg$cachedPos = 0;
1294 peg$cachedPosDetails = { line: 1, column: 1, seenCR: false };
1295 }
1296 advance(peg$cachedPosDetails, peg$cachedPos, pos);
1297 peg$cachedPos = pos;
1298 }
1299
1300 return peg$cachedPosDetails;
1301 }
1302
1303 function peg$fail(expected) {
1304 if (peg$currPos < peg$maxFailPos) { return; }
1305
1306 if (peg$currPos > peg$maxFailPos) {
1307 peg$maxFailPos = peg$currPos;
1308 peg$maxFailExpected = [];
1309 }
1310
1311 peg$maxFailExpected.push(expected);
1312 }
1313
1314 function peg$buildException(message, expected, pos) {
1315 function cleanupExpected(expected) {
1316 var i = 1;
1317
1318 expected.sort(function(a, b) {
1319 if (a.description < b.description) {
1320 return -1;
1321 } else if (a.description > b.description) {
1322 return 1;
1323 } else {
1324 return 0;
1325 }
1326 });
1327
1328 while (i < expected.length) {
1329 if (expected[i - 1] === expected[i]) {
1330 expected.splice(i, 1);
1331 } else {
1332 i++;
1333 }
1334 }
1335 }
1336
1337 function buildMessage(expected, found) {
1338 function stringEscape(s) {
1339 function hex(ch) { return ch.charCodeAt(0).toString(16).toUpperCase(); }
1340
1341 return s
1342 .replace(/\\/g, '\\\\')
1343 .replace(/"/g, '\\"')
1344 .replace(/\x08/g, '\\b')
1345 .replace(/\t/g, '\\t')
1346 .replace(/\n/g, '\\n')
1347 .replace(/\f/g, '\\f')
1348 .replace(/\r/g, '\\r')
1349 .replace(/[\x00-\x07\x0B\x0E\x0F]/g, function(ch) { return '\\x0' + hex(ch); })
1350 .replace(/[\x10-\x1F\x80-\xFF]/g, function(ch) { return '\\x' + hex(ch); })
1351 .replace(/[\u0180-\u0FFF]/g, function(ch) { return '\\u0' + hex(ch); })
1352 .replace(/[\u1080-\uFFFF]/g, function(ch) { return '\\u' + hex(ch); });
1353 }
1354
1355 var expectedDescs = new Array(expected.length),
1356 expectedDesc, foundDesc, i;
1357
1358 for (i = 0; i < expected.length; i++) {
1359 expectedDescs[i] = expected[i].description;
1360 }
1361
1362 expectedDesc = expected.length > 1
1363 ? expectedDescs.slice(0, -1).join(", ")
1364 + " or "
1365 + expectedDescs[expected.length - 1]
1366 : expectedDescs[0];
1367
1368 foundDesc = found ? "\"" + stringEscape(found) + "\"" : "end of input";
1369
1370 return "Expected " + expectedDesc + " but " + foundDesc + " found.";
1371 }
1372
1373 var posDetails = peg$computePosDetails(pos),
1374 found = pos < input.length ? input.charAt(pos) : null;
1375
1376 if (expected !== null) {
1377 cleanupExpected(expected);
1378 }
1379
1380 return new SyntaxError(
1381 message !== null ? message : buildMessage(expected, found),
1382 expected,
1383 found,
1384 pos,
1385 posDetails.line,
1386 posDetails.column
1387 );
1388 }
1389
1390 function peg$parsestart() {
1391 var s0, s1;
1392
1393 s0 = peg$parsesourceEntries();
1394 if (s0 === peg$FAILED) {
1395 s0 = peg$currPos;
1396 s1 = [];
1397 if (s1 !== peg$FAILED) {
1398 peg$reportedPos = s0;
1399 s1 = peg$c1();
1400 }
1401 s0 = s1;
1402 }
1403
1404 return s0;
1405 }
1406
1407 function peg$parsesourceEntries() {
1408 var s0, s1, s2, s3, s4, s5;
1409
1410 s0 = peg$currPos;
1411 s1 = peg$parsesourceEntry();
1412 if (s1 !== peg$FAILED) {
1413 s2 = [];
1414 s3 = peg$parsewhitespace();
1415 while (s3 !== peg$FAILED) {
1416 s2.push(s3);
1417 s3 = peg$parsewhitespace();
1418 }
1419 if (s2 !== peg$FAILED) {
1420 if (input.charCodeAt(peg$currPos) === 44) {
1421 s3 = peg$c3;
1422 peg$currPos++;
1423 } else {
1424 s3 = peg$FAILED;
1425 if (peg$silentFails === 0) { peg$fail(peg$c4); }
1426 }
1427 if (s3 !== peg$FAILED) {
1428 s4 = [];
1429 s5 = peg$parsewhitespace();
1430 while (s5 !== peg$FAILED) {
1431 s4.push(s5);
1432 s5 = peg$parsewhitespace();
1433 }
1434 if (s4 !== peg$FAILED) {
1435 s5 = peg$parsesourceEntries();
1436 if (s5 !== peg$FAILED) {
1437 peg$reportedPos = s0;
1438 s1 = peg$c5(s1, s5);
1439 s0 = s1;
1440 } else {
1441 peg$currPos = s0;
1442 s0 = peg$c2;
1443 }
1444 } else {
1445 peg$currPos = s0;
1446 s0 = peg$c2;
1447 }
1448 } else {
1449 peg$currPos = s0;
1450 s0 = peg$c2;
1451 }
1452 } else {
1453 peg$currPos = s0;
1454 s0 = peg$c2;
1455 }
1456 } else {
1457 peg$currPos = s0;
1458 s0 = peg$c2;
1459 }
1460 if (s0 === peg$FAILED) {
1461 s0 = peg$currPos;
1462 s1 = peg$parsesourceEntry();
1463 if (s1 !== peg$FAILED) {
1464 peg$reportedPos = s0;
1465 s1 = peg$c6(s1);
1466 }
1467 s0 = s1;
1468 }
1469
1470 return s0;
1471 }
1472
1473 function peg$parsesourceEntry() {
1474 var s0;
1475
1476 s0 = peg$parseurlEntry();
1477 if (s0 === peg$FAILED) {
1478 s0 = peg$parselocalEntry();
1479 }
1480
1481 return s0;
1482 }
1483
1484 function peg$parseurlEntry() {
1485 var s0, s1, s2, s3;
1486
1487 s0 = peg$currPos;
1488 s1 = peg$parseurl();
1489 if (s1 !== peg$FAILED) {
1490 s2 = [];
1491 s3 = peg$parsewhitespace();
1492 if (s3 !== peg$FAILED) {
1493 while (s3 !== peg$FAILED) {
1494 s2.push(s3);
1495 s3 = peg$parsewhitespace();
1496 }
1497 } else {
1498 s2 = peg$c2;
1499 }
1500 if (s2 !== peg$FAILED) {
1501 s3 = peg$parseformat();
1502 if (s3 !== peg$FAILED) {
1503 peg$reportedPos = s0;
1504 s1 = peg$c7(s1, s3);
1505 s0 = s1;
1506 } else {
1507 peg$currPos = s0;
1508 s0 = peg$c2;
1509 }
1510 } else {
1511 peg$currPos = s0;
1512 s0 = peg$c2;
1513 }
1514 } else {
1515 peg$currPos = s0;
1516 s0 = peg$c2;
1517 }
1518 if (s0 === peg$FAILED) {
1519 s0 = peg$currPos;
1520 s1 = peg$parseurl();
1521 if (s1 !== peg$FAILED) {
1522 peg$reportedPos = s0;
1523 s1 = peg$c8(s1);
1524 }
1525 s0 = s1;
1526 }
1527
1528 return s0;
1529 }
1530
1531 function peg$parseurl() {
1532 var s0, s1, s2, s3;
1533
1534 s0 = peg$currPos;
1535 if (input.substr(peg$currPos, 4) === peg$c9) {
1536 s1 = peg$c9;
1537 peg$currPos += 4;
1538 } else {
1539 s1 = peg$FAILED;
1540 if (peg$silentFails === 0) { peg$fail(peg$c10); }
1541 }
1542 if (s1 !== peg$FAILED) {
1543 s2 = peg$parsevalue();
1544 if (s2 !== peg$FAILED) {
1545 if (input.charCodeAt(peg$currPos) === 41) {
1546 s3 = peg$c11;
1547 peg$currPos++;
1548 } else {
1549 s3 = peg$FAILED;
1550 if (peg$silentFails === 0) { peg$fail(peg$c12); }
1551 }
1552 if (s3 !== peg$FAILED) {
1553 peg$reportedPos = s0;
1554 s1 = peg$c13(s2);
1555 s0 = s1;
1556 } else {
1557 peg$currPos = s0;
1558 s0 = peg$c2;
1559 }
1560 } else {
1561 peg$currPos = s0;
1562 s0 = peg$c2;
1563 }
1564 } else {
1565 peg$currPos = s0;
1566 s0 = peg$c2;
1567 }
1568
1569 return s0;
1570 }
1571
1572 function peg$parseformat() {
1573 var s0, s1, s2, s3;
1574
1575 s0 = peg$currPos;
1576 if (input.substr(peg$currPos, 7) === peg$c14) {
1577 s1 = peg$c14;
1578 peg$currPos += 7;
1579 } else {
1580 s1 = peg$FAILED;
1581 if (peg$silentFails === 0) { peg$fail(peg$c15); }
1582 }
1583 if (s1 !== peg$FAILED) {
1584 s2 = peg$parsevalue();
1585 if (s2 !== peg$FAILED) {
1586 if (input.charCodeAt(peg$currPos) === 41) {
1587 s3 = peg$c11;
1588 peg$currPos++;
1589 } else {
1590 s3 = peg$FAILED;
1591 if (peg$silentFails === 0) { peg$fail(peg$c12); }
1592 }
1593 if (s3 !== peg$FAILED) {
1594 peg$reportedPos = s0;
1595 s1 = peg$c13(s2);
1596 s0 = s1;
1597 } else {
1598 peg$currPos = s0;
1599 s0 = peg$c2;
1600 }
1601 } else {
1602 peg$currPos = s0;
1603 s0 = peg$c2;
1604 }
1605 } else {
1606 peg$currPos = s0;
1607 s0 = peg$c2;
1608 }
1609
1610 return s0;
1611 }
1612
1613 function peg$parselocalEntry() {
1614 var s0, s1, s2, s3;
1615
1616 s0 = peg$currPos;
1617 if (input.substr(peg$currPos, 6) === peg$c16) {
1618 s1 = peg$c16;
1619 peg$currPos += 6;
1620 } else {
1621 s1 = peg$FAILED;
1622 if (peg$silentFails === 0) { peg$fail(peg$c17); }
1623 }
1624 if (s1 !== peg$FAILED) {
1625 s2 = peg$parsevalue();
1626 if (s2 !== peg$FAILED) {
1627 if (input.charCodeAt(peg$currPos) === 41) {
1628 s3 = peg$c11;
1629 peg$currPos++;
1630 } else {
1631 s3 = peg$FAILED;
1632 if (peg$silentFails === 0) { peg$fail(peg$c12); }
1633 }
1634 if (s3 !== peg$FAILED) {
1635 peg$reportedPos = s0;
1636 s1 = peg$c18(s2);
1637 s0 = s1;
1638 } else {
1639 peg$currPos = s0;
1640 s0 = peg$c2;
1641 }
1642 } else {
1643 peg$currPos = s0;
1644 s0 = peg$c2;
1645 }
1646 } else {
1647 peg$currPos = s0;
1648 s0 = peg$c2;
1649 }
1650
1651 return s0;
1652 }
1653
1654 function peg$parsevalue() {
1655 var s0, s1, s2;
1656
1657 s0 = peg$currPos;
1658 s1 = [];
1659 if (peg$c19.test(input.charAt(peg$currPos))) {
1660 s2 = input.charAt(peg$currPos);
1661 peg$currPos++;
1662 } else {
1663 s2 = peg$FAILED;
1664 if (peg$silentFails === 0) { peg$fail(peg$c20); }
1665 }
1666 if (s2 !== peg$FAILED) {
1667 while (s2 !== peg$FAILED) {
1668 s1.push(s2);
1669 if (peg$c19.test(input.charAt(peg$currPos))) {
1670 s2 = input.charAt(peg$currPos);
1671 peg$currPos++;
1672 } else {
1673 s2 = peg$FAILED;
1674 if (peg$silentFails === 0) { peg$fail(peg$c20); }
1675 }
1676 }
1677 } else {
1678 s1 = peg$c2;
1679 }
1680 if (s1 !== peg$FAILED) {
1681 peg$reportedPos = s0;
1682 s1 = peg$c21(s1);
1683 }
1684 s0 = s1;
1685
1686 return s0;
1687 }
1688
1689 function peg$parsewhitespace() {
1690 var s0;
1691
1692 if (peg$c22.test(input.charAt(peg$currPos))) {
1693 s0 = input.charAt(peg$currPos);
1694 peg$currPos++;
1695 } else {
1696 s0 = peg$FAILED;
1697 if (peg$silentFails === 0) { peg$fail(peg$c23); }
1698 }
1699
1700 return s0;
1701 }
1702
1703
1704 var util = require('../util');
1705
1706
1707 peg$result = peg$startRuleFunction();
1708
1709 if (peg$result !== peg$FAILED && peg$currPos === input.length) {
1710 return peg$result;
1711 } else {
1712 if (peg$result !== peg$FAILED && peg$currPos < input.length) {
1713 peg$fail({ type: "end", description: "end of input" });
1714 }
1715
1716 throw peg$buildException(null, peg$maxFailExpected, peg$maxFailPos);
1717 }
1718 }
1719
1720 return {
1721 SyntaxError: SyntaxError,
1722 parse: parse
1723 };
1724})();
1725
1726},{"../util":7}],6:[function(require,module,exports){
1727var grammar = require('./grammar');
1728
1729
1730exports.SyntaxError = function (message, offset) {
1731 this.message = message;
1732 this.offset = offset;
1733};
1734
1735exports.parse = function (fontFaceSourceValue) {
1736 try {
1737 return grammar.parse(fontFaceSourceValue);
1738 } catch (e) {
1739 throw new exports.SyntaxError(e.message, e.offset);
1740 }
1741};
1742
1743exports.serialize = function (parsedFontFaceSources) {
1744 return parsedFontFaceSources.map(function (sourceItem) {
1745 var itemValue;
1746
1747 if (sourceItem.url) {
1748 itemValue = 'url("' + sourceItem.url + '")';
1749 if (sourceItem.format) {
1750 itemValue += ' format("' + sourceItem.format + '")';
1751 }
1752 } else {
1753 itemValue = 'local("' + sourceItem.local + '")';
1754 }
1755 return itemValue;
1756 }).join(', ');
1757};
1758
1759},{"./grammar":5}],7:[function(require,module,exports){
1760var trimCSSWhitespace = function (value) {
1761 var whitespaceRegex = /^[\t\r\f\n ]*(.+?)[\t\r\f\n ]*$/;
1762
1763 return value.replace(whitespaceRegex, "$1");
1764};
1765
1766var unquoteString = function (quotedUrl) {
1767 var doubleQuoteRegex = /^"(.*)"$/,
1768 singleQuoteRegex = /^'(.*)'$/;
1769
1770 if (doubleQuoteRegex.test(quotedUrl)) {
1771 return quotedUrl.replace(doubleQuoteRegex, "$1");
1772 } else {
1773 if (singleQuoteRegex.test(quotedUrl)) {
1774 return quotedUrl.replace(singleQuoteRegex, "$1");
1775 } else {
1776 return quotedUrl;
1777 }
1778 }
1779};
1780
1781exports.extractValue = function (value) {
1782 return unquoteString(trimCSSWhitespace(value));
1783};
1784
1785},{}],8:[function(require,module,exports){
1786/*
1787Copyright (c) 2014, Yahoo! Inc. All rights reserved.
1788Copyrights licensed under the New BSD License.
1789See the accompanying LICENSE file for terms.
1790*/
1791
1792'use strict';
1793
1794exports.match = matchQuery;
1795exports.parse = parseQuery;
1796
1797// -----------------------------------------------------------------------------
1798
1799var RE_MEDIA_QUERY = /(?:(only|not)?\s*([^\s\(\)]+)(?:\s*and)?\s*)?(.+)?/i,
1800 RE_MQ_EXPRESSION = /\(\s*([^\s\:\)]+)\s*(?:\:\s*([^\s\)]+))?\s*\)/,
1801 RE_MQ_FEATURE = /^(?:(min|max)-)?(.+)/,
1802 RE_LENGTH_UNIT = /(em|rem|px|cm|mm|in|pt|pc)?$/,
1803 RE_RESOLUTION_UNIT = /(dpi|dpcm|dppx)?$/;
1804
1805function matchQuery(mediaQuery, values) {
1806 return parseQuery(mediaQuery).some(function (query) {
1807 var inverse = query.inverse;
1808
1809 // Either the parsed or specified `type` is "all", or the types must be
1810 // equal for a match.
1811 var typeMatch = query.type === 'all' || values.type === query.type;
1812
1813 // Quit early when `type` doesn't match, but take "not" into account.
1814 if ((typeMatch && inverse) || !(typeMatch || inverse)) {
1815 return false;
1816 }
1817
1818 var expressionsMatch = query.expressions.every(function (expression) {
1819 var feature = expression.feature,
1820 modifier = expression.modifier,
1821 expValue = expression.value,
1822 value = values[feature];
1823
1824 // Missing or falsy values don't match.
1825 if (!value) { return false; }
1826
1827 switch (feature) {
1828 case 'orientation':
1829 case 'scan':
1830 return value.toLowerCase() === expValue.toLowerCase();
1831
1832 case 'width':
1833 case 'height':
1834 case 'device-width':
1835 case 'device-height':
1836 expValue = toPx(expValue);
1837 value = toPx(value);
1838 break;
1839
1840 case 'resolution':
1841 expValue = toDpi(expValue);
1842 value = toDpi(value);
1843 break;
1844
1845 case 'aspect-ratio':
1846 case 'device-aspect-ratio':
1847 case /* Deprecated */ 'device-pixel-ratio':
1848 expValue = toDecimal(expValue);
1849 value = toDecimal(value);
1850 break;
1851
1852 case 'grid':
1853 case 'color':
1854 case 'color-index':
1855 case 'monochrome':
1856 expValue = parseInt(expValue, 10) || 1;
1857 value = parseInt(value, 10) || 0;
1858 break;
1859 }
1860
1861 switch (modifier) {
1862 case 'min': return value >= expValue;
1863 case 'max': return value <= expValue;
1864 default : return value === expValue;
1865 }
1866 });
1867
1868 return (expressionsMatch && !inverse) || (!expressionsMatch && inverse);
1869 });
1870}
1871
1872function parseQuery(mediaQuery) {
1873 return mediaQuery.split(',').map(function (query) {
1874 query = query.trim();
1875
1876 var captures = query.match(RE_MEDIA_QUERY),
1877 modifier = captures[1],
1878 type = captures[2],
1879 expressions = captures[3] || '',
1880 parsed = {};
1881
1882 parsed.inverse = !!modifier && modifier.toLowerCase() === 'not';
1883 parsed.type = type ? type.toLowerCase() : 'all';
1884
1885 // Split expressions into a list.
1886 expressions = expressions.match(/\([^\)]+\)/g) || [];
1887
1888 parsed.expressions = expressions.map(function (expression) {
1889 var captures = expression.match(RE_MQ_EXPRESSION),
1890 feature = captures[1].toLowerCase().match(RE_MQ_FEATURE);
1891
1892 return {
1893 modifier: feature[1],
1894 feature : feature[2],
1895 value : captures[2]
1896 };
1897 });
1898
1899 return parsed;
1900 });
1901}
1902
1903// -- Utilities ----------------------------------------------------------------
1904
1905function toDecimal(ratio) {
1906 var decimal = Number(ratio),
1907 numbers;
1908
1909 if (!decimal) {
1910 numbers = ratio.match(/^(\d+)\s*\/\s*(\d+)$/);
1911 decimal = numbers[1] / numbers[2];
1912 }
1913
1914 return decimal;
1915}
1916
1917function toDpi(resolution) {
1918 var value = parseFloat(resolution),
1919 units = String(resolution).match(RE_RESOLUTION_UNIT)[1];
1920
1921 switch (units) {
1922 case 'dpcm': return value / 2.54;
1923 case 'dppx': return value * 96;
1924 default : return value;
1925 }
1926}
1927
1928function toPx(length) {
1929 var value = parseFloat(length),
1930 units = String(length).match(RE_LENGTH_UNIT)[1];
1931
1932 switch (units) {
1933 case 'em' : return value * 16;
1934 case 'rem': return value * 16;
1935 case 'cm' : return value * 96 / 2.54;
1936 case 'mm' : return value * 96 / 2.54 / 10;
1937 case 'in' : return value * 96;
1938 case 'pt' : return value * 72;
1939 case 'pc' : return value * 72 / 12;
1940 default : return value;
1941 }
1942}
1943
1944},{}],9:[function(require,module,exports){
1945//.CommonJS
1946var CSSOM = {
1947 CSSRule: require("./CSSRule").CSSRule,
1948 MatcherList: require("./MatcherList").MatcherList
1949};
1950///CommonJS
1951
1952
1953/**
1954 * @constructor
1955 * @see https://developer.mozilla.org/en/CSS/@-moz-document
1956 */
1957CSSOM.CSSDocumentRule = function CSSDocumentRule() {
1958 CSSOM.CSSRule.call(this);
1959 this.matcher = new CSSOM.MatcherList();
1960 this.cssRules = [];
1961};
1962
1963CSSOM.CSSDocumentRule.prototype = new CSSOM.CSSRule();
1964CSSOM.CSSDocumentRule.prototype.constructor = CSSOM.CSSDocumentRule;
1965CSSOM.CSSDocumentRule.prototype.type = 10;
1966//FIXME
1967//CSSOM.CSSDocumentRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
1968//CSSOM.CSSDocumentRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
1969
1970Object.defineProperty(CSSOM.CSSDocumentRule.prototype, "cssText", {
1971 get: function() {
1972 var cssTexts = [];
1973 for (var i=0, length=this.cssRules.length; i < length; i++) {
1974 cssTexts.push(this.cssRules[i].cssText);
1975 }
1976 return "@-moz-document " + this.matcher.matcherText + " {" + cssTexts.join("") + "}";
1977 }
1978});
1979
1980
1981//.CommonJS
1982exports.CSSDocumentRule = CSSOM.CSSDocumentRule;
1983///CommonJS
1984
1985},{"./CSSRule":16,"./MatcherList":22}],10:[function(require,module,exports){
1986//.CommonJS
1987var CSSOM = {
1988 CSSStyleDeclaration: require("./CSSStyleDeclaration").CSSStyleDeclaration,
1989 CSSRule: require("./CSSRule").CSSRule
1990};
1991///CommonJS
1992
1993
1994/**
1995 * @constructor
1996 * @see http://dev.w3.org/csswg/cssom/#css-font-face-rule
1997 */
1998CSSOM.CSSFontFaceRule = function CSSFontFaceRule() {
1999 CSSOM.CSSRule.call(this);
2000 this.style = new CSSOM.CSSStyleDeclaration();
2001 this.style.parentRule = this;
2002};
2003
2004CSSOM.CSSFontFaceRule.prototype = new CSSOM.CSSRule();
2005CSSOM.CSSFontFaceRule.prototype.constructor = CSSOM.CSSFontFaceRule;
2006CSSOM.CSSFontFaceRule.prototype.type = 5;
2007//FIXME
2008//CSSOM.CSSFontFaceRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
2009//CSSOM.CSSFontFaceRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
2010
2011// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSFontFaceRule.cpp
2012Object.defineProperty(CSSOM.CSSFontFaceRule.prototype, "cssText", {
2013 get: function() {
2014 return "@font-face {" + this.style.cssText + "}";
2015 }
2016});
2017
2018
2019//.CommonJS
2020exports.CSSFontFaceRule = CSSOM.CSSFontFaceRule;
2021///CommonJS
2022
2023},{"./CSSRule":16,"./CSSStyleDeclaration":17}],11:[function(require,module,exports){
2024//.CommonJS
2025var CSSOM = {
2026 CSSRule: require("./CSSRule").CSSRule
2027};
2028///CommonJS
2029
2030
2031/**
2032 * @constructor
2033 * @see http://www.w3.org/TR/shadow-dom/#host-at-rule
2034 */
2035CSSOM.CSSHostRule = function CSSHostRule() {
2036 CSSOM.CSSRule.call(this);
2037 this.cssRules = [];
2038};
2039
2040CSSOM.CSSHostRule.prototype = new CSSOM.CSSRule();
2041CSSOM.CSSHostRule.prototype.constructor = CSSOM.CSSHostRule;
2042CSSOM.CSSHostRule.prototype.type = 1001;
2043//FIXME
2044//CSSOM.CSSHostRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
2045//CSSOM.CSSHostRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
2046
2047Object.defineProperty(CSSOM.CSSHostRule.prototype, "cssText", {
2048 get: function() {
2049 var cssTexts = [];
2050 for (var i=0, length=this.cssRules.length; i < length; i++) {
2051 cssTexts.push(this.cssRules[i].cssText);
2052 }
2053 return "@host {" + cssTexts.join("") + "}";
2054 }
2055});
2056
2057
2058//.CommonJS
2059exports.CSSHostRule = CSSOM.CSSHostRule;
2060///CommonJS
2061
2062},{"./CSSRule":16}],12:[function(require,module,exports){
2063//.CommonJS
2064var CSSOM = {
2065 CSSRule: require("./CSSRule").CSSRule,
2066 CSSStyleSheet: require("./CSSStyleSheet").CSSStyleSheet,
2067 MediaList: require("./MediaList").MediaList
2068};
2069///CommonJS
2070
2071
2072/**
2073 * @constructor
2074 * @see http://dev.w3.org/csswg/cssom/#cssimportrule
2075 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSImportRule
2076 */
2077CSSOM.CSSImportRule = function CSSImportRule() {
2078 CSSOM.CSSRule.call(this);
2079 this.href = "";
2080 this.media = new CSSOM.MediaList();
2081 this.styleSheet = new CSSOM.CSSStyleSheet();
2082};
2083
2084CSSOM.CSSImportRule.prototype = new CSSOM.CSSRule();
2085CSSOM.CSSImportRule.prototype.constructor = CSSOM.CSSImportRule;
2086CSSOM.CSSImportRule.prototype.type = 3;
2087
2088Object.defineProperty(CSSOM.CSSImportRule.prototype, "cssText", {
2089 get: function() {
2090 var mediaText = this.media.mediaText;
2091 return "@import url(" + this.href + ")" + (mediaText ? " " + mediaText : "") + ";";
2092 },
2093 set: function(cssText) {
2094 var i = 0;
2095
2096 /**
2097 * @import url(partial.css) screen, handheld;
2098 * || |
2099 * after-import media
2100 * |
2101 * url
2102 */
2103 var state = '';
2104
2105 var buffer = '';
2106 var index;
2107 for (var character; (character = cssText.charAt(i)); i++) {
2108
2109 switch (character) {
2110 case ' ':
2111 case '\t':
2112 case '\r':
2113 case '\n':
2114 case '\f':
2115 if (state === 'after-import') {
2116 state = 'url';
2117 } else {
2118 buffer += character;
2119 }
2120 break;
2121
2122 case '@':
2123 if (!state && cssText.indexOf('@import', i) === i) {
2124 state = 'after-import';
2125 i += 'import'.length;
2126 buffer = '';
2127 }
2128 break;
2129
2130 case 'u':
2131 if (state === 'url' && cssText.indexOf('url(', i) === i) {
2132 index = cssText.indexOf(')', i + 1);
2133 if (index === -1) {
2134 throw i + ': ")" not found';
2135 }
2136 i += 'url('.length;
2137 var url = cssText.slice(i, index);
2138 if (url[0] === url[url.length - 1]) {
2139 if (url[0] === '"' || url[0] === "'") {
2140 url = url.slice(1, -1);
2141 }
2142 }
2143 this.href = url;
2144 i = index;
2145 state = 'media';
2146 }
2147 break;
2148
2149 case '"':
2150 if (state === 'url') {
2151 index = cssText.indexOf('"', i + 1);
2152 if (!index) {
2153 throw i + ": '\"' not found";
2154 }
2155 this.href = cssText.slice(i + 1, index);
2156 i = index;
2157 state = 'media';
2158 }
2159 break;
2160
2161 case "'":
2162 if (state === 'url') {
2163 index = cssText.indexOf("'", i + 1);
2164 if (!index) {
2165 throw i + ': "\'" not found';
2166 }
2167 this.href = cssText.slice(i + 1, index);
2168 i = index;
2169 state = 'media';
2170 }
2171 break;
2172
2173 case ';':
2174 if (state === 'media') {
2175 if (buffer) {
2176 this.media.mediaText = buffer.trim();
2177 }
2178 }
2179 break;
2180
2181 default:
2182 if (state === 'media') {
2183 buffer += character;
2184 }
2185 break;
2186 }
2187 }
2188 }
2189});
2190
2191
2192//.CommonJS
2193exports.CSSImportRule = CSSOM.CSSImportRule;
2194///CommonJS
2195
2196},{"./CSSRule":16,"./CSSStyleSheet":19,"./MediaList":23}],13:[function(require,module,exports){
2197//.CommonJS
2198var CSSOM = {
2199 CSSRule: require("./CSSRule").CSSRule,
2200 CSSStyleDeclaration: require('./CSSStyleDeclaration').CSSStyleDeclaration
2201};
2202///CommonJS
2203
2204
2205/**
2206 * @constructor
2207 * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframeRule
2208 */
2209CSSOM.CSSKeyframeRule = function CSSKeyframeRule() {
2210 CSSOM.CSSRule.call(this);
2211 this.keyText = '';
2212 this.style = new CSSOM.CSSStyleDeclaration();
2213 this.style.parentRule = this;
2214};
2215
2216CSSOM.CSSKeyframeRule.prototype = new CSSOM.CSSRule();
2217CSSOM.CSSKeyframeRule.prototype.constructor = CSSOM.CSSKeyframeRule;
2218CSSOM.CSSKeyframeRule.prototype.type = 9;
2219//FIXME
2220//CSSOM.CSSKeyframeRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
2221//CSSOM.CSSKeyframeRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
2222
2223// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframeRule.cpp
2224Object.defineProperty(CSSOM.CSSKeyframeRule.prototype, "cssText", {
2225 get: function() {
2226 return this.keyText + " {" + this.style.cssText + "} ";
2227 }
2228});
2229
2230
2231//.CommonJS
2232exports.CSSKeyframeRule = CSSOM.CSSKeyframeRule;
2233///CommonJS
2234
2235},{"./CSSRule":16,"./CSSStyleDeclaration":17}],14:[function(require,module,exports){
2236//.CommonJS
2237var CSSOM = {
2238 CSSRule: require("./CSSRule").CSSRule
2239};
2240///CommonJS
2241
2242
2243/**
2244 * @constructor
2245 * @see http://www.w3.org/TR/css3-animations/#DOM-CSSKeyframesRule
2246 */
2247CSSOM.CSSKeyframesRule = function CSSKeyframesRule() {
2248 CSSOM.CSSRule.call(this);
2249 this.name = '';
2250 this.cssRules = [];
2251};
2252
2253CSSOM.CSSKeyframesRule.prototype = new CSSOM.CSSRule();
2254CSSOM.CSSKeyframesRule.prototype.constructor = CSSOM.CSSKeyframesRule;
2255CSSOM.CSSKeyframesRule.prototype.type = 8;
2256//FIXME
2257//CSSOM.CSSKeyframesRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
2258//CSSOM.CSSKeyframesRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
2259
2260// http://www.opensource.apple.com/source/WebCore/WebCore-955.66.1/css/WebKitCSSKeyframesRule.cpp
2261Object.defineProperty(CSSOM.CSSKeyframesRule.prototype, "cssText", {
2262 get: function() {
2263 var cssTexts = [];
2264 for (var i=0, length=this.cssRules.length; i < length; i++) {
2265 cssTexts.push(" " + this.cssRules[i].cssText);
2266 }
2267 return "@" + (this._vendorPrefix || '') + "keyframes " + this.name + " { \n" + cssTexts.join("\n") + "\n}";
2268 }
2269});
2270
2271
2272//.CommonJS
2273exports.CSSKeyframesRule = CSSOM.CSSKeyframesRule;
2274///CommonJS
2275
2276},{"./CSSRule":16}],15:[function(require,module,exports){
2277//.CommonJS
2278var CSSOM = {
2279 CSSRule: require("./CSSRule").CSSRule,
2280 MediaList: require("./MediaList").MediaList
2281};
2282///CommonJS
2283
2284
2285/**
2286 * @constructor
2287 * @see http://dev.w3.org/csswg/cssom/#cssmediarule
2288 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSMediaRule
2289 */
2290CSSOM.CSSMediaRule = function CSSMediaRule() {
2291 CSSOM.CSSRule.call(this);
2292 this.media = new CSSOM.MediaList();
2293 this.cssRules = [];
2294};
2295
2296CSSOM.CSSMediaRule.prototype = new CSSOM.CSSRule();
2297CSSOM.CSSMediaRule.prototype.constructor = CSSOM.CSSMediaRule;
2298CSSOM.CSSMediaRule.prototype.type = 4;
2299//FIXME
2300//CSSOM.CSSMediaRule.prototype.insertRule = CSSStyleSheet.prototype.insertRule;
2301//CSSOM.CSSMediaRule.prototype.deleteRule = CSSStyleSheet.prototype.deleteRule;
2302
2303// http://opensource.apple.com/source/WebCore/WebCore-658.28/css/CSSMediaRule.cpp
2304Object.defineProperty(CSSOM.CSSMediaRule.prototype, "cssText", {
2305 get: function() {
2306 var cssTexts = [];
2307 for (var i=0, length=this.cssRules.length; i < length; i++) {
2308 cssTexts.push(this.cssRules[i].cssText);
2309 }
2310 return "@media " + this.media.mediaText + " {" + cssTexts.join("") + "}";
2311 }
2312});
2313
2314
2315//.CommonJS
2316exports.CSSMediaRule = CSSOM.CSSMediaRule;
2317///CommonJS
2318
2319},{"./CSSRule":16,"./MediaList":23}],16:[function(require,module,exports){
2320//.CommonJS
2321var CSSOM = {};
2322///CommonJS
2323
2324
2325/**
2326 * @constructor
2327 * @see http://dev.w3.org/csswg/cssom/#the-cssrule-interface
2328 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSRule
2329 */
2330CSSOM.CSSRule = function CSSRule() {
2331 this.parentRule = null;
2332 this.parentStyleSheet = null;
2333};
2334
2335CSSOM.CSSRule.UNKNOWN_RULE = 0; // obsolete
2336CSSOM.CSSRule.STYLE_RULE = 1;
2337CSSOM.CSSRule.CHARSET_RULE = 2; // obsolete
2338CSSOM.CSSRule.IMPORT_RULE = 3;
2339CSSOM.CSSRule.MEDIA_RULE = 4;
2340CSSOM.CSSRule.FONT_FACE_RULE = 5;
2341CSSOM.CSSRule.PAGE_RULE = 6;
2342CSSOM.CSSRule.KEYFRAMES_RULE = 7;
2343CSSOM.CSSRule.KEYFRAME_RULE = 8;
2344CSSOM.CSSRule.MARGIN_RULE = 9;
2345CSSOM.CSSRule.NAMESPACE_RULE = 10;
2346CSSOM.CSSRule.COUNTER_STYLE_RULE = 11;
2347CSSOM.CSSRule.SUPPORTS_RULE = 12;
2348CSSOM.CSSRule.DOCUMENT_RULE = 13;
2349CSSOM.CSSRule.FONT_FEATURE_VALUES_RULE = 14;
2350CSSOM.CSSRule.VIEWPORT_RULE = 15;
2351CSSOM.CSSRule.REGION_STYLE_RULE = 16;
2352
2353
2354CSSOM.CSSRule.prototype = {
2355 constructor: CSSOM.CSSRule
2356 //FIXME
2357};
2358
2359
2360//.CommonJS
2361exports.CSSRule = CSSOM.CSSRule;
2362///CommonJS
2363
2364},{}],17:[function(require,module,exports){
2365//.CommonJS
2366var CSSOM = {};
2367///CommonJS
2368
2369
2370/**
2371 * @constructor
2372 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration
2373 */
2374CSSOM.CSSStyleDeclaration = function CSSStyleDeclaration(){
2375 this.length = 0;
2376 this.parentRule = null;
2377
2378 // NON-STANDARD
2379 this._importants = {};
2380};
2381
2382
2383CSSOM.CSSStyleDeclaration.prototype = {
2384
2385 constructor: CSSOM.CSSStyleDeclaration,
2386
2387 /**
2388 *
2389 * @param {string} name
2390 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-getPropertyValue
2391 * @return {string} the value of the property if it has been explicitly set for this declaration block.
2392 * Returns the empty string if the property has not been set.
2393 */
2394 getPropertyValue: function(name) {
2395 return this[name] || "";
2396 },
2397
2398 /**
2399 *
2400 * @param {string} name
2401 * @param {string} value
2402 * @param {string} [priority=null] "important" or null
2403 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-setProperty
2404 */
2405 setProperty: function(name, value, priority) {
2406 if (this[name]) {
2407 // Property already exist. Overwrite it.
2408 var index = Array.prototype.indexOf.call(this, name);
2409 if (index < 0) {
2410 this[this.length] = name;
2411 this.length++;
2412 }
2413 } else {
2414 // New property.
2415 this[this.length] = name;
2416 this.length++;
2417 }
2418 this[name] = value + "";
2419 this._importants[name] = priority;
2420 },
2421
2422 /**
2423 *
2424 * @param {string} name
2425 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleDeclaration-removeProperty
2426 * @return {string} the value of the property if it has been explicitly set for this declaration block.
2427 * Returns the empty string if the property has not been set or the property name does not correspond to a known CSS property.
2428 */
2429 removeProperty: function(name) {
2430 if (!(name in this)) {
2431 return "";
2432 }
2433 var index = Array.prototype.indexOf.call(this, name);
2434 if (index < 0) {
2435 return "";
2436 }
2437 var prevValue = this[name];
2438 this[name] = "";
2439
2440 // That's what WebKit and Opera do
2441 Array.prototype.splice.call(this, index, 1);
2442
2443 // That's what Firefox does
2444 //this[index] = ""
2445
2446 return prevValue;
2447 },
2448
2449 getPropertyCSSValue: function() {
2450 //FIXME
2451 },
2452
2453 /**
2454 *
2455 * @param {String} name
2456 */
2457 getPropertyPriority: function(name) {
2458 return this._importants[name] || "";
2459 },
2460
2461
2462 /**
2463 * element.style.overflow = "auto"
2464 * element.style.getPropertyShorthand("overflow-x")
2465 * -> "overflow"
2466 */
2467 getPropertyShorthand: function() {
2468 //FIXME
2469 },
2470
2471 isPropertyImplicit: function() {
2472 //FIXME
2473 },
2474
2475 // Doesn't work in IE < 9
2476 get cssText(){
2477 var properties = [];
2478 for (var i=0, length=this.length; i < length; ++i) {
2479 var name = this[i];
2480 var value = this.getPropertyValue(name);
2481 var priority = this.getPropertyPriority(name);
2482 if (priority) {
2483 priority = " !" + priority;
2484 }
2485 properties[i] = name + ": " + value + priority + ";";
2486 }
2487 return properties.join(" ");
2488 },
2489
2490 set cssText(text){
2491 var i, name;
2492 for (i = this.length; i--;) {
2493 name = this[i];
2494 this[name] = "";
2495 }
2496 Array.prototype.splice.call(this, 0, this.length);
2497 this._importants = {};
2498
2499 var dummyRule = CSSOM.parse('#bogus{' + text + '}').cssRules[0].style;
2500 var length = dummyRule.length;
2501 for (i = 0; i < length; ++i) {
2502 name = dummyRule[i];
2503 this.setProperty(dummyRule[i], dummyRule.getPropertyValue(name), dummyRule.getPropertyPriority(name));
2504 }
2505 }
2506};
2507
2508
2509//.CommonJS
2510exports.CSSStyleDeclaration = CSSOM.CSSStyleDeclaration;
2511CSSOM.parse = require('./parse').parse; // Cannot be included sooner due to the mutual dependency between parse.js and CSSStyleDeclaration.js
2512///CommonJS
2513
2514},{"./parse":27}],18:[function(require,module,exports){
2515//.CommonJS
2516var CSSOM = {
2517 CSSStyleDeclaration: require("./CSSStyleDeclaration").CSSStyleDeclaration,
2518 CSSRule: require("./CSSRule").CSSRule
2519};
2520///CommonJS
2521
2522
2523/**
2524 * @constructor
2525 * @see http://dev.w3.org/csswg/cssom/#cssstylerule
2526 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleRule
2527 */
2528CSSOM.CSSStyleRule = function CSSStyleRule() {
2529 CSSOM.CSSRule.call(this);
2530 this.selectorText = "";
2531 this.style = new CSSOM.CSSStyleDeclaration();
2532 this.style.parentRule = this;
2533};
2534
2535CSSOM.CSSStyleRule.prototype = new CSSOM.CSSRule();
2536CSSOM.CSSStyleRule.prototype.constructor = CSSOM.CSSStyleRule;
2537CSSOM.CSSStyleRule.prototype.type = 1;
2538
2539Object.defineProperty(CSSOM.CSSStyleRule.prototype, "cssText", {
2540 get: function() {
2541 var text;
2542 if (this.selectorText) {
2543 text = this.selectorText + " {" + this.style.cssText + "}";
2544 } else {
2545 text = "";
2546 }
2547 return text;
2548 },
2549 set: function(cssText) {
2550 var rule = CSSOM.CSSStyleRule.parse(cssText);
2551 this.style = rule.style;
2552 this.selectorText = rule.selectorText;
2553 }
2554});
2555
2556
2557/**
2558 * NON-STANDARD
2559 * lightweight version of parse.js.
2560 * @param {string} ruleText
2561 * @return CSSStyleRule
2562 */
2563CSSOM.CSSStyleRule.parse = function(ruleText) {
2564 var i = 0;
2565 var state = "selector";
2566 var index;
2567 var j = i;
2568 var buffer = "";
2569
2570 var SIGNIFICANT_WHITESPACE = {
2571 "selector": true,
2572 "value": true
2573 };
2574
2575 var styleRule = new CSSOM.CSSStyleRule();
2576 var name, priority="";
2577
2578 for (var character; (character = ruleText.charAt(i)); i++) {
2579
2580 switch (character) {
2581
2582 case " ":
2583 case "\t":
2584 case "\r":
2585 case "\n":
2586 case "\f":
2587 if (SIGNIFICANT_WHITESPACE[state]) {
2588 // Squash 2 or more white-spaces in the row into 1
2589 switch (ruleText.charAt(i - 1)) {
2590 case " ":
2591 case "\t":
2592 case "\r":
2593 case "\n":
2594 case "\f":
2595 break;
2596 default:
2597 buffer += " ";
2598 break;
2599 }
2600 }
2601 break;
2602
2603 // String
2604 case '"':
2605 j = i + 1;
2606 index = ruleText.indexOf('"', j) + 1;
2607 if (!index) {
2608 throw '" is missing';
2609 }
2610 buffer += ruleText.slice(i, index);
2611 i = index - 1;
2612 break;
2613
2614 case "'":
2615 j = i + 1;
2616 index = ruleText.indexOf("'", j) + 1;
2617 if (!index) {
2618 throw "' is missing";
2619 }
2620 buffer += ruleText.slice(i, index);
2621 i = index - 1;
2622 break;
2623
2624 // Comment
2625 case "/":
2626 if (ruleText.charAt(i + 1) === "*") {
2627 i += 2;
2628 index = ruleText.indexOf("*/", i);
2629 if (index === -1) {
2630 throw new SyntaxError("Missing */");
2631 } else {
2632 i = index + 1;
2633 }
2634 } else {
2635 buffer += character;
2636 }
2637 break;
2638
2639 case "{":
2640 if (state === "selector") {
2641 styleRule.selectorText = buffer.trim();
2642 buffer = "";
2643 state = "name";
2644 }
2645 break;
2646
2647 case ":":
2648 if (state === "name") {
2649 name = buffer.trim();
2650 buffer = "";
2651 state = "value";
2652 } else {
2653 buffer += character;
2654 }
2655 break;
2656
2657 case "!":
2658 if (state === "value" && ruleText.indexOf("!important", i) === i) {
2659 priority = "important";
2660 i += "important".length;
2661 } else {
2662 buffer += character;
2663 }
2664 break;
2665
2666 case ";":
2667 if (state === "value") {
2668 styleRule.style.setProperty(name, buffer.trim(), priority);
2669 priority = "";
2670 buffer = "";
2671 state = "name";
2672 } else {
2673 buffer += character;
2674 }
2675 break;
2676
2677 case "}":
2678 if (state === "value") {
2679 styleRule.style.setProperty(name, buffer.trim(), priority);
2680 priority = "";
2681 buffer = "";
2682 } else if (state === "name") {
2683 break;
2684 } else {
2685 buffer += character;
2686 }
2687 state = "selector";
2688 break;
2689
2690 default:
2691 buffer += character;
2692 break;
2693
2694 }
2695 }
2696
2697 return styleRule;
2698
2699};
2700
2701
2702//.CommonJS
2703exports.CSSStyleRule = CSSOM.CSSStyleRule;
2704///CommonJS
2705
2706},{"./CSSRule":16,"./CSSStyleDeclaration":17}],19:[function(require,module,exports){
2707//.CommonJS
2708var CSSOM = {
2709 StyleSheet: require("./StyleSheet").StyleSheet,
2710 CSSStyleRule: require("./CSSStyleRule").CSSStyleRule
2711};
2712///CommonJS
2713
2714
2715/**
2716 * @constructor
2717 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet
2718 */
2719CSSOM.CSSStyleSheet = function CSSStyleSheet() {
2720 CSSOM.StyleSheet.call(this);
2721 this.cssRules = [];
2722};
2723
2724
2725CSSOM.CSSStyleSheet.prototype = new CSSOM.StyleSheet();
2726CSSOM.CSSStyleSheet.prototype.constructor = CSSOM.CSSStyleSheet;
2727
2728
2729/**
2730 * Used to insert a new rule into the style sheet. The new rule now becomes part of the cascade.
2731 *
2732 * sheet = new Sheet("body {margin: 0}")
2733 * sheet.toString()
2734 * -> "body{margin:0;}"
2735 * sheet.insertRule("img {border: none}", 0)
2736 * -> 0
2737 * sheet.toString()
2738 * -> "img{border:none;}body{margin:0;}"
2739 *
2740 * @param {string} rule
2741 * @param {number} index
2742 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-insertRule
2743 * @return {number} The index within the style sheet's rule collection of the newly inserted rule.
2744 */
2745CSSOM.CSSStyleSheet.prototype.insertRule = function(rule, index) {
2746 if (index < 0 || index > this.cssRules.length) {
2747 throw new RangeError("INDEX_SIZE_ERR");
2748 }
2749 var cssRule = CSSOM.parse(rule).cssRules[0];
2750 cssRule.parentStyleSheet = this;
2751 this.cssRules.splice(index, 0, cssRule);
2752 return index;
2753};
2754
2755
2756/**
2757 * Used to delete a rule from the style sheet.
2758 *
2759 * sheet = new Sheet("img{border:none} body{margin:0}")
2760 * sheet.toString()
2761 * -> "img{border:none;}body{margin:0;}"
2762 * sheet.deleteRule(0)
2763 * sheet.toString()
2764 * -> "body{margin:0;}"
2765 *
2766 * @param {number} index within the style sheet's rule list of the rule to remove.
2767 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSStyleSheet-deleteRule
2768 */
2769CSSOM.CSSStyleSheet.prototype.deleteRule = function(index) {
2770 if (index < 0 || index >= this.cssRules.length) {
2771 throw new RangeError("INDEX_SIZE_ERR");
2772 }
2773 this.cssRules.splice(index, 1);
2774};
2775
2776
2777/**
2778 * NON-STANDARD
2779 * @return {string} serialize stylesheet
2780 */
2781CSSOM.CSSStyleSheet.prototype.toString = function() {
2782 var result = "";
2783 var rules = this.cssRules;
2784 for (var i=0; i<rules.length; i++) {
2785 result += rules[i].cssText + "\n";
2786 }
2787 return result;
2788};
2789
2790
2791//.CommonJS
2792exports.CSSStyleSheet = CSSOM.CSSStyleSheet;
2793CSSOM.parse = require('./parse').parse; // Cannot be included sooner due to the mutual dependency between parse.js and CSSStyleSheet.js
2794///CommonJS
2795
2796},{"./CSSStyleRule":18,"./StyleSheet":24,"./parse":27}],20:[function(require,module,exports){
2797//.CommonJS
2798var CSSOM = {};
2799///CommonJS
2800
2801
2802/**
2803 * @constructor
2804 * @see http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
2805 *
2806 * TODO: add if needed
2807 */
2808CSSOM.CSSValue = function CSSValue() {
2809};
2810
2811CSSOM.CSSValue.prototype = {
2812 constructor: CSSOM.CSSValue,
2813
2814 // @see: http://www.w3.org/TR/DOM-Level-2-Style/css.html#CSS-CSSValue
2815 set cssText(text) {
2816 var name = this._getConstructorName();
2817
2818 throw new Error('DOMException: property "cssText" of "' + name + '" is readonly and can not be replaced with "' + text + '"!');
2819 },
2820
2821 get cssText() {
2822 var name = this._getConstructorName();
2823
2824 throw new Error('getter "cssText" of "' + name + '" is not implemented!');
2825 },
2826
2827 _getConstructorName: function() {
2828 var s = this.constructor.toString(),
2829 c = s.match(/function\s([^\(]+)/),
2830 name = c[1];
2831
2832 return name;
2833 }
2834};
2835
2836
2837//.CommonJS
2838exports.CSSValue = CSSOM.CSSValue;
2839///CommonJS
2840
2841},{}],21:[function(require,module,exports){
2842//.CommonJS
2843var CSSOM = {
2844 CSSValue: require('./CSSValue').CSSValue
2845};
2846///CommonJS
2847
2848
2849/**
2850 * @constructor
2851 * @see http://msdn.microsoft.com/en-us/library/ms537634(v=vs.85).aspx
2852 *
2853 */
2854CSSOM.CSSValueExpression = function CSSValueExpression(token, idx) {
2855 this._token = token;
2856 this._idx = idx;
2857};
2858
2859CSSOM.CSSValueExpression.prototype = new CSSOM.CSSValue();
2860CSSOM.CSSValueExpression.prototype.constructor = CSSOM.CSSValueExpression;
2861
2862/**
2863 * parse css expression() value
2864 *
2865 * @return {Object}
2866 * - error:
2867 * or
2868 * - idx:
2869 * - expression:
2870 *
2871 * Example:
2872 *
2873 * .selector {
2874 * zoom: expression(documentElement.clientWidth > 1000 ? '1000px' : 'auto');
2875 * }
2876 */
2877CSSOM.CSSValueExpression.prototype.parse = function() {
2878 var token = this._token,
2879 idx = this._idx;
2880
2881 var character = '',
2882 expression = '',
2883 error = '',
2884 info,
2885 paren = [];
2886
2887
2888 for (; ; ++idx) {
2889 character = token.charAt(idx);
2890
2891 // end of token
2892 if (character === '') {
2893 error = 'css expression error: unfinished expression!';
2894 break;
2895 }
2896
2897 switch(character) {
2898 case '(':
2899 paren.push(character);
2900 expression += character;
2901 break;
2902
2903 case ')':
2904 paren.pop(character);
2905 expression += character;
2906 break;
2907
2908 case '/':
2909 if ((info = this._parseJSComment(token, idx))) { // comment?
2910 if (info.error) {
2911 error = 'css expression error: unfinished comment in expression!';
2912 } else {
2913 idx = info.idx;
2914 // ignore the comment
2915 }
2916 } else if ((info = this._parseJSRexExp(token, idx))) { // regexp
2917 idx = info.idx;
2918 expression += info.text;
2919 } else { // other
2920 expression += character;
2921 }
2922 break;
2923
2924 case "'":
2925 case '"':
2926 info = this._parseJSString(token, idx, character);
2927 if (info) { // string
2928 idx = info.idx;
2929 expression += info.text;
2930 } else {
2931 expression += character;
2932 }
2933 break;
2934
2935 default:
2936 expression += character;
2937 break;
2938 }
2939
2940 if (error) {
2941 break;
2942 }
2943
2944 // end of expression
2945 if (paren.length === 0) {
2946 break;
2947 }
2948 }
2949
2950 var ret;
2951 if (error) {
2952 ret = {
2953 error: error
2954 };
2955 } else {
2956 ret = {
2957 idx: idx,
2958 expression: expression
2959 };
2960 }
2961
2962 return ret;
2963};
2964
2965
2966/**
2967 *
2968 * @return {Object|false}
2969 * - idx:
2970 * - text:
2971 * or
2972 * - error:
2973 * or
2974 * false
2975 *
2976 */
2977CSSOM.CSSValueExpression.prototype._parseJSComment = function(token, idx) {
2978 var nextChar = token.charAt(idx + 1),
2979 text;
2980
2981 if (nextChar === '/' || nextChar === '*') {
2982 var startIdx = idx,
2983 endIdx,
2984 commentEndChar;
2985
2986 if (nextChar === '/') { // line comment
2987 commentEndChar = '\n';
2988 } else if (nextChar === '*') { // block comment
2989 commentEndChar = '*/';
2990 }
2991
2992 endIdx = token.indexOf(commentEndChar, startIdx + 1 + 1);
2993 if (endIdx !== -1) {
2994 endIdx = endIdx + commentEndChar.length - 1;
2995 text = token.substring(idx, endIdx + 1);
2996 return {
2997 idx: endIdx,
2998 text: text
2999 };
3000 } else {
3001 var error = 'css expression error: unfinished comment in expression!';
3002 return {
3003 error: error
3004 };
3005 }
3006 } else {
3007 return false;
3008 }
3009};
3010
3011
3012/**
3013 *
3014 * @return {Object|false}
3015 * - idx:
3016 * - text:
3017 * or
3018 * false
3019 *
3020 */
3021CSSOM.CSSValueExpression.prototype._parseJSString = function(token, idx, sep) {
3022 var endIdx = this._findMatchedIdx(token, idx, sep),
3023 text;
3024
3025 if (endIdx === -1) {
3026 return false;
3027 } else {
3028 text = token.substring(idx, endIdx + sep.length);
3029
3030 return {
3031 idx: endIdx,
3032 text: text
3033 };
3034 }
3035};
3036
3037
3038/**
3039 * parse regexp in css expression
3040 *
3041 * @return {Object|false}
3042 * - idx:
3043 * - regExp:
3044 * or
3045 * false
3046 */
3047
3048/*
3049
3050all legal RegExp
3051
3052/a/
3053(/a/)
3054[/a/]
3055[12, /a/]
3056
3057!/a/
3058
3059+/a/
3060-/a/
3061* /a/
3062/ /a/
3063%/a/
3064
3065===/a/
3066!==/a/
3067==/a/
3068!=/a/
3069>/a/
3070>=/a/
3071</a/
3072<=/a/
3073
3074&/a/
3075|/a/
3076^/a/
3077~/a/
3078<</a/
3079>>/a/
3080>>>/a/
3081
3082&&/a/
3083||/a/
3084?/a/
3085=/a/
3086,/a/
3087
3088 delete /a/
3089 in /a/
3090instanceof /a/
3091 new /a/
3092 typeof /a/
3093 void /a/
3094
3095*/
3096CSSOM.CSSValueExpression.prototype._parseJSRexExp = function(token, idx) {
3097 var before = token.substring(0, idx).replace(/\s+$/, ""),
3098 legalRegx = [
3099 /^$/,
3100 /\($/,
3101 /\[$/,
3102 /\!$/,
3103 /\+$/,
3104 /\-$/,
3105 /\*$/,
3106 /\/\s+/,
3107 /\%$/,
3108 /\=$/,
3109 /\>$/,
3110 /<$/,
3111 /\&$/,
3112 /\|$/,
3113 /\^$/,
3114 /\~$/,
3115 /\?$/,
3116 /\,$/,
3117 /delete$/,
3118 /in$/,
3119 /instanceof$/,
3120 /new$/,
3121 /typeof$/,
3122 /void$/
3123 ];
3124
3125 var isLegal = legalRegx.some(function(reg) {
3126 return reg.test(before);
3127 });
3128
3129 if (!isLegal) {
3130 return false;
3131 } else {
3132 var sep = '/';
3133
3134 // same logic as string
3135 return this._parseJSString(token, idx, sep);
3136 }
3137};
3138
3139
3140/**
3141 *
3142 * find next sep(same line) index in `token`
3143 *
3144 * @return {Number}
3145 *
3146 */
3147CSSOM.CSSValueExpression.prototype._findMatchedIdx = function(token, idx, sep) {
3148 var startIdx = idx,
3149 endIdx;
3150
3151 var NOT_FOUND = -1;
3152
3153 while(true) {
3154 endIdx = token.indexOf(sep, startIdx + 1);
3155
3156 if (endIdx === -1) { // not found
3157 endIdx = NOT_FOUND;
3158 break;
3159 } else {
3160 var text = token.substring(idx + 1, endIdx),
3161 matched = text.match(/\\+$/);
3162 if (!matched || matched[0] % 2 === 0) { // not escaped
3163 break;
3164 } else {
3165 startIdx = endIdx;
3166 }
3167 }
3168 }
3169
3170 // boundary must be in the same line(js sting or regexp)
3171 var nextNewLineIdx = token.indexOf('\n', idx + 1);
3172 if (nextNewLineIdx < endIdx) {
3173 endIdx = NOT_FOUND;
3174 }
3175
3176
3177 return endIdx;
3178};
3179
3180
3181
3182
3183//.CommonJS
3184exports.CSSValueExpression = CSSOM.CSSValueExpression;
3185///CommonJS
3186
3187},{"./CSSValue":20}],22:[function(require,module,exports){
3188//.CommonJS
3189var CSSOM = {};
3190///CommonJS
3191
3192
3193/**
3194 * @constructor
3195 * @see https://developer.mozilla.org/en/CSS/@-moz-document
3196 */
3197CSSOM.MatcherList = function MatcherList(){
3198 this.length = 0;
3199};
3200
3201CSSOM.MatcherList.prototype = {
3202
3203 constructor: CSSOM.MatcherList,
3204
3205 /**
3206 * @return {string}
3207 */
3208 get matcherText() {
3209 return Array.prototype.join.call(this, ", ");
3210 },
3211
3212 /**
3213 * @param {string} value
3214 */
3215 set matcherText(value) {
3216 // just a temporary solution, actually it may be wrong by just split the value with ',', because a url can include ','.
3217 var values = value.split(",");
3218 var length = this.length = values.length;
3219 for (var i=0; i<length; i++) {
3220 this[i] = values[i].trim();
3221 }
3222 },
3223
3224 /**
3225 * @param {string} matcher
3226 */
3227 appendMatcher: function(matcher) {
3228 if (Array.prototype.indexOf.call(this, matcher) === -1) {
3229 this[this.length] = matcher;
3230 this.length++;
3231 }
3232 },
3233
3234 /**
3235 * @param {string} matcher
3236 */
3237 deleteMatcher: function(matcher) {
3238 var index = Array.prototype.indexOf.call(this, matcher);
3239 if (index !== -1) {
3240 Array.prototype.splice.call(this, index, 1);
3241 }
3242 }
3243
3244};
3245
3246
3247//.CommonJS
3248exports.MatcherList = CSSOM.MatcherList;
3249///CommonJS
3250
3251},{}],23:[function(require,module,exports){
3252//.CommonJS
3253var CSSOM = {};
3254///CommonJS
3255
3256
3257/**
3258 * @constructor
3259 * @see http://dev.w3.org/csswg/cssom/#the-medialist-interface
3260 */
3261CSSOM.MediaList = function MediaList(){
3262 this.length = 0;
3263};
3264
3265CSSOM.MediaList.prototype = {
3266
3267 constructor: CSSOM.MediaList,
3268
3269 /**
3270 * @return {string}
3271 */
3272 get mediaText() {
3273 return Array.prototype.join.call(this, ", ");
3274 },
3275
3276 /**
3277 * @param {string} value
3278 */
3279 set mediaText(value) {
3280 var values = value.split(",");
3281 var length = this.length = values.length;
3282 for (var i=0; i<length; i++) {
3283 this[i] = values[i].trim();
3284 }
3285 },
3286
3287 /**
3288 * @param {string} medium
3289 */
3290 appendMedium: function(medium) {
3291 if (Array.prototype.indexOf.call(this, medium) === -1) {
3292 this[this.length] = medium;
3293 this.length++;
3294 }
3295 },
3296
3297 /**
3298 * @param {string} medium
3299 */
3300 deleteMedium: function(medium) {
3301 var index = Array.prototype.indexOf.call(this, medium);
3302 if (index !== -1) {
3303 Array.prototype.splice.call(this, index, 1);
3304 }
3305 }
3306
3307};
3308
3309
3310//.CommonJS
3311exports.MediaList = CSSOM.MediaList;
3312///CommonJS
3313
3314},{}],24:[function(require,module,exports){
3315//.CommonJS
3316var CSSOM = {};
3317///CommonJS
3318
3319
3320/**
3321 * @constructor
3322 * @see http://dev.w3.org/csswg/cssom/#the-stylesheet-interface
3323 */
3324CSSOM.StyleSheet = function StyleSheet() {
3325 this.parentStyleSheet = null;
3326};
3327
3328
3329//.CommonJS
3330exports.StyleSheet = CSSOM.StyleSheet;
3331///CommonJS
3332
3333},{}],25:[function(require,module,exports){
3334//.CommonJS
3335var CSSOM = {
3336 CSSStyleSheet: require("./CSSStyleSheet").CSSStyleSheet,
3337 CSSStyleRule: require("./CSSStyleRule").CSSStyleRule,
3338 CSSMediaRule: require("./CSSMediaRule").CSSMediaRule,
3339 CSSStyleDeclaration: require("./CSSStyleDeclaration").CSSStyleDeclaration,
3340 CSSKeyframeRule: require('./CSSKeyframeRule').CSSKeyframeRule,
3341 CSSKeyframesRule: require('./CSSKeyframesRule').CSSKeyframesRule
3342};
3343///CommonJS
3344
3345
3346/**
3347 * Produces a deep copy of stylesheet — the instance variables of stylesheet are copied recursively.
3348 * @param {CSSStyleSheet|CSSOM.CSSStyleSheet} stylesheet
3349 * @nosideeffects
3350 * @return {CSSOM.CSSStyleSheet}
3351 */
3352CSSOM.clone = function clone(stylesheet) {
3353
3354 var cloned = new CSSOM.CSSStyleSheet();
3355
3356 var rules = stylesheet.cssRules;
3357 if (!rules) {
3358 return cloned;
3359 }
3360
3361 var RULE_TYPES = {
3362 1: CSSOM.CSSStyleRule,
3363 4: CSSOM.CSSMediaRule,
3364 //3: CSSOM.CSSImportRule,
3365 //5: CSSOM.CSSFontFaceRule,
3366 //6: CSSOM.CSSPageRule,
3367 8: CSSOM.CSSKeyframesRule,
3368 9: CSSOM.CSSKeyframeRule
3369 };
3370
3371 for (var i=0, rulesLength=rules.length; i < rulesLength; i++) {
3372 var rule = rules[i];
3373 var ruleClone = cloned.cssRules[i] = new RULE_TYPES[rule.type]();
3374
3375 var style = rule.style;
3376 if (style) {
3377 var styleClone = ruleClone.style = new CSSOM.CSSStyleDeclaration();
3378 for (var j=0, styleLength=style.length; j < styleLength; j++) {
3379 var name = styleClone[j] = style[j];
3380 styleClone[name] = style[name];
3381 styleClone._importants[name] = style.getPropertyPriority(name);
3382 }
3383 styleClone.length = style.length;
3384 }
3385
3386 if (rule.hasOwnProperty('keyText')) {
3387 ruleClone.keyText = rule.keyText;
3388 }
3389
3390 if (rule.hasOwnProperty('selectorText')) {
3391 ruleClone.selectorText = rule.selectorText;
3392 }
3393
3394 if (rule.hasOwnProperty('mediaText')) {
3395 ruleClone.mediaText = rule.mediaText;
3396 }
3397
3398 if (rule.hasOwnProperty('cssRules')) {
3399 ruleClone.cssRules = clone(rule).cssRules;
3400 }
3401 }
3402
3403 return cloned;
3404
3405};
3406
3407//.CommonJS
3408exports.clone = CSSOM.clone;
3409///CommonJS
3410
3411},{"./CSSKeyframeRule":13,"./CSSKeyframesRule":14,"./CSSMediaRule":15,"./CSSStyleDeclaration":17,"./CSSStyleRule":18,"./CSSStyleSheet":19}],26:[function(require,module,exports){
3412exports.CSSStyleDeclaration = require("./CSSStyleDeclaration").CSSStyleDeclaration;
3413exports.CSSRule = require("./CSSRule").CSSRule;
3414exports.CSSStyleRule = require("./CSSStyleRule").CSSStyleRule;
3415exports.CSSImportRule = require("./CSSImportRule").CSSImportRule;
3416exports.MediaList = require("./MediaList").MediaList;
3417exports.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule;
3418exports.StyleSheet = require("./StyleSheet").StyleSheet;
3419exports.CSSStyleSheet = require("./CSSStyleSheet").CSSStyleSheet;
3420exports.parse = require("./parse").parse;
3421exports.clone = require("./clone").clone;
3422
3423},{"./CSSImportRule":12,"./CSSMediaRule":15,"./CSSRule":16,"./CSSStyleDeclaration":17,"./CSSStyleRule":18,"./CSSStyleSheet":19,"./MediaList":23,"./StyleSheet":24,"./clone":25,"./parse":27}],27:[function(require,module,exports){
3424//.CommonJS
3425var CSSOM = {};
3426///CommonJS
3427
3428
3429/**
3430 * @param {string} token
3431 */
3432CSSOM.parse = function parse(token) {
3433
3434 var i = 0;
3435
3436 /**
3437 "before-selector" or
3438 "selector" or
3439 "atRule" or
3440 "atBlock" or
3441 "before-name" or
3442 "name" or
3443 "before-value" or
3444 "value"
3445 */
3446 var state = "before-selector";
3447
3448 var index;
3449 var buffer = "";
3450 var valueParenthesisDepth = 0;
3451
3452 var SIGNIFICANT_WHITESPACE = {
3453 "selector": true,
3454 "value": true,
3455 "value-parenthesis": true,
3456 "atRule": true,
3457 "importRule-begin": true,
3458 "importRule": true,
3459 "atBlock": true,
3460 'documentRule-begin': true
3461 };
3462
3463 var styleSheet = new CSSOM.CSSStyleSheet();
3464
3465 // @type CSSStyleSheet|CSSMediaRule|CSSFontFaceRule|CSSKeyframesRule|CSSDocumentRule
3466 var currentScope = styleSheet;
3467
3468 // @type CSSMediaRule|CSSKeyframesRule|CSSDocumentRule
3469 var parentRule;
3470
3471 var name, priority="", styleRule, mediaRule, importRule, fontFaceRule, keyframesRule, documentRule, hostRule;
3472
3473 var atKeyframesRegExp = /@(-(?:\w+-)+)?keyframes/g;
3474
3475 var parseError = function(message) {
3476 var lines = token.substring(0, i).split('\n');
3477 var lineCount = lines.length;
3478 var charCount = lines.pop().length + 1;
3479 var error = new Error(message + ' (line ' + lineCount + ', char ' + charCount + ')');
3480 error.line = lineCount;
3481 /* jshint sub : true */
3482 error['char'] = charCount;
3483 error.styleSheet = styleSheet;
3484 throw error;
3485 };
3486
3487 for (var character; (character = token.charAt(i)); i++) {
3488
3489 switch (character) {
3490
3491 case " ":
3492 case "\t":
3493 case "\r":
3494 case "\n":
3495 case "\f":
3496 if (SIGNIFICANT_WHITESPACE[state]) {
3497 buffer += character;
3498 }
3499 break;
3500
3501 // String
3502 case '"':
3503 index = i + 1;
3504 do {
3505 index = token.indexOf('"', index) + 1;
3506 if (!index) {
3507 parseError('Unmatched "');
3508 }
3509 } while (token[index - 2] === '\\');
3510 buffer += token.slice(i, index);
3511 i = index - 1;
3512 switch (state) {
3513 case 'before-value':
3514 state = 'value';
3515 break;
3516 case 'importRule-begin':
3517 state = 'importRule';
3518 break;
3519 }
3520 break;
3521
3522 case "'":
3523 index = i + 1;
3524 do {
3525 index = token.indexOf("'", index) + 1;
3526 if (!index) {
3527 parseError("Unmatched '");
3528 }
3529 } while (token[index - 2] === '\\');
3530 buffer += token.slice(i, index);
3531 i = index - 1;
3532 switch (state) {
3533 case 'before-value':
3534 state = 'value';
3535 break;
3536 case 'importRule-begin':
3537 state = 'importRule';
3538 break;
3539 }
3540 break;
3541
3542 // Comment
3543 case "/":
3544 if (token.charAt(i + 1) === "*") {
3545 i += 2;
3546 index = token.indexOf("*/", i);
3547 if (index === -1) {
3548 parseError("Missing */");
3549 } else {
3550 i = index + 1;
3551 }
3552 } else {
3553 buffer += character;
3554 }
3555 if (state === "importRule-begin") {
3556 buffer += " ";
3557 state = "importRule";
3558 }
3559 break;
3560
3561 // At-rule
3562 case "@":
3563 if (token.indexOf("@-moz-document", i) === i) {
3564 state = "documentRule-begin";
3565 documentRule = new CSSOM.CSSDocumentRule();
3566 documentRule.__starts = i;
3567 i += "-moz-document".length;
3568 buffer = "";
3569 break;
3570 } else if (token.indexOf("@media", i) === i) {
3571 state = "atBlock";
3572 mediaRule = new CSSOM.CSSMediaRule();
3573 mediaRule.__starts = i;
3574 i += "media".length;
3575 buffer = "";
3576 break;
3577 } else if (token.indexOf("@host", i) === i) {
3578 state = "hostRule-begin";
3579 i += "host".length;
3580 hostRule = new CSSOM.CSSHostRule();
3581 hostRule.__starts = i;
3582 buffer = "";
3583 break;
3584 } else if (token.indexOf("@import", i) === i) {
3585 state = "importRule-begin";
3586 i += "import".length;
3587 buffer += "@import";
3588 break;
3589 } else if (token.indexOf("@font-face", i) === i) {
3590 state = "fontFaceRule-begin";
3591 i += "font-face".length;
3592 fontFaceRule = new CSSOM.CSSFontFaceRule();
3593 fontFaceRule.__starts = i;
3594 buffer = "";
3595 break;
3596 } else {
3597 atKeyframesRegExp.lastIndex = i;
3598 var matchKeyframes = atKeyframesRegExp.exec(token);
3599 if (matchKeyframes && matchKeyframes.index === i) {
3600 state = "keyframesRule-begin";
3601 keyframesRule = new CSSOM.CSSKeyframesRule();
3602 keyframesRule.__starts = i;
3603 keyframesRule._vendorPrefix = matchKeyframes[1]; // Will come out as undefined if no prefix was found
3604 i += matchKeyframes[0].length - 1;
3605 buffer = "";
3606 break;
3607 } else if (state === "selector") {
3608 state = "atRule";
3609 }
3610 }
3611 buffer += character;
3612 break;
3613
3614 case "{":
3615 if (state === "selector" || state === "atRule") {
3616 styleRule.selectorText = buffer.trim();
3617 styleRule.style.__starts = i;
3618 buffer = "";
3619 state = "before-name";
3620 } else if (state === "atBlock") {
3621 mediaRule.media.mediaText = buffer.trim();
3622 currentScope = parentRule = mediaRule;
3623 mediaRule.parentStyleSheet = styleSheet;
3624 buffer = "";
3625 state = "before-selector";
3626 } else if (state === "hostRule-begin") {
3627 currentScope = parentRule = hostRule;
3628 hostRule.parentStyleSheet = styleSheet;
3629 buffer = "";
3630 state = "before-selector";
3631 } else if (state === "fontFaceRule-begin") {
3632 if (parentRule) {
3633 fontFaceRule.parentRule = parentRule;
3634 }
3635 fontFaceRule.parentStyleSheet = styleSheet;
3636 styleRule = fontFaceRule;
3637 buffer = "";
3638 state = "before-name";
3639 } else if (state === "keyframesRule-begin") {
3640 keyframesRule.name = buffer.trim();
3641 if (parentRule) {
3642 keyframesRule.parentRule = parentRule;
3643 }
3644 keyframesRule.parentStyleSheet = styleSheet;
3645 currentScope = parentRule = keyframesRule;
3646 buffer = "";
3647 state = "keyframeRule-begin";
3648 } else if (state === "keyframeRule-begin") {
3649 styleRule = new CSSOM.CSSKeyframeRule();
3650 styleRule.keyText = buffer.trim();
3651 styleRule.__starts = i;
3652 buffer = "";
3653 state = "before-name";
3654 } else if (state === "documentRule-begin") {
3655 // FIXME: what if this '{' is in the url text of the match function?
3656 documentRule.matcher.matcherText = buffer.trim();
3657 if (parentRule) {
3658 documentRule.parentRule = parentRule;
3659 }
3660 currentScope = parentRule = documentRule;
3661 documentRule.parentStyleSheet = styleSheet;
3662 buffer = "";
3663 state = "before-selector";
3664 }
3665 break;
3666
3667 case ":":
3668 if (state === "name") {
3669 name = buffer.trim();
3670 buffer = "";
3671 state = "before-value";
3672 } else {
3673 buffer += character;
3674 }
3675 break;
3676
3677 case "(":
3678 if (state === 'value') {
3679 // ie css expression mode
3680 if (buffer.trim() === 'expression') {
3681 var info = (new CSSOM.CSSValueExpression(token, i)).parse();
3682
3683 if (info.error) {
3684 parseError(info.error);
3685 } else {
3686 buffer += info.expression;
3687 i = info.idx;
3688 }
3689 } else {
3690 state = 'value-parenthesis';
3691 //always ensure this is reset to 1 on transition
3692 //from value to value-parenthesis
3693 valueParenthesisDepth = 1;
3694 buffer += character;
3695 }
3696 } else if (state === 'value-parenthesis') {
3697 valueParenthesisDepth++;
3698 buffer += character;
3699 } else {
3700 buffer += character;
3701 }
3702 break;
3703
3704 case ")":
3705 if (state === 'value-parenthesis') {
3706 valueParenthesisDepth--;
3707 if (valueParenthesisDepth === 0) state = 'value';
3708 }
3709 buffer += character;
3710 break;
3711
3712 case "!":
3713 if (state === "value" && token.indexOf("!important", i) === i) {
3714 priority = "important";
3715 i += "important".length;
3716 } else {
3717 buffer += character;
3718 }
3719 break;
3720
3721 case ";":
3722 switch (state) {
3723 case "value":
3724 styleRule.style.setProperty(name, buffer.trim(), priority);
3725 priority = "";
3726 buffer = "";
3727 state = "before-name";
3728 break;
3729 case "atRule":
3730 buffer = "";
3731 state = "before-selector";
3732 break;
3733 case "importRule":
3734 importRule = new CSSOM.CSSImportRule();
3735 importRule.parentStyleSheet = importRule.styleSheet.parentStyleSheet = styleSheet;
3736 importRule.cssText = buffer + character;
3737 styleSheet.cssRules.push(importRule);
3738 buffer = "";
3739 state = "before-selector";
3740 break;
3741 default:
3742 buffer += character;
3743 break;
3744 }
3745 break;
3746
3747 case "}":
3748 switch (state) {
3749 case "value":
3750 styleRule.style.setProperty(name, buffer.trim(), priority);
3751 priority = "";
3752 /* falls through */
3753 case "before-name":
3754 case "name":
3755 styleRule.__ends = i + 1;
3756 if (parentRule) {
3757 styleRule.parentRule = parentRule;
3758 }
3759 styleRule.parentStyleSheet = styleSheet;
3760 currentScope.cssRules.push(styleRule);
3761 buffer = "";
3762 if (currentScope.constructor === CSSOM.CSSKeyframesRule) {
3763 state = "keyframeRule-begin";
3764 } else {
3765 state = "before-selector";
3766 }
3767 break;
3768 case "keyframeRule-begin":
3769 case "before-selector":
3770 case "selector":
3771 // End of media/document rule.
3772 if (!parentRule) {
3773 parseError("Unexpected }");
3774 }
3775 currentScope.__ends = i + 1;
3776 // Nesting rules aren't supported yet
3777 styleSheet.cssRules.push(currentScope);
3778 currentScope = styleSheet;
3779 parentRule = null;
3780 buffer = "";
3781 state = "before-selector";
3782 break;
3783 }
3784 break;
3785
3786 default:
3787 switch (state) {
3788 case "before-selector":
3789 state = "selector";
3790 styleRule = new CSSOM.CSSStyleRule();
3791 styleRule.__starts = i;
3792 break;
3793 case "before-name":
3794 state = "name";
3795 break;
3796 case "before-value":
3797 state = "value";
3798 break;
3799 case "importRule-begin":
3800 state = "importRule";
3801 break;
3802 }
3803 buffer += character;
3804 break;
3805 }
3806 }
3807
3808 return styleSheet;
3809};
3810
3811
3812//.CommonJS
3813exports.parse = CSSOM.parse;
3814// The following modules cannot be included sooner due to the mutual dependency with parse.js
3815CSSOM.CSSStyleSheet = require("./CSSStyleSheet").CSSStyleSheet;
3816CSSOM.CSSStyleRule = require("./CSSStyleRule").CSSStyleRule;
3817CSSOM.CSSImportRule = require("./CSSImportRule").CSSImportRule;
3818CSSOM.CSSMediaRule = require("./CSSMediaRule").CSSMediaRule;
3819CSSOM.CSSFontFaceRule = require("./CSSFontFaceRule").CSSFontFaceRule;
3820CSSOM.CSSHostRule = require("./CSSHostRule").CSSHostRule;
3821CSSOM.CSSStyleDeclaration = require('./CSSStyleDeclaration').CSSStyleDeclaration;
3822CSSOM.CSSKeyframeRule = require('./CSSKeyframeRule').CSSKeyframeRule;
3823CSSOM.CSSKeyframesRule = require('./CSSKeyframesRule').CSSKeyframesRule;
3824CSSOM.CSSValueExpression = require('./CSSValueExpression').CSSValueExpression;
3825CSSOM.CSSDocumentRule = require('./CSSDocumentRule').CSSDocumentRule;
3826///CommonJS
3827
3828},{"./CSSDocumentRule":9,"./CSSFontFaceRule":10,"./CSSHostRule":11,"./CSSImportRule":12,"./CSSKeyframeRule":13,"./CSSKeyframesRule":14,"./CSSMediaRule":15,"./CSSStyleDeclaration":17,"./CSSStyleRule":18,"./CSSStyleSheet":19,"./CSSValueExpression":21}],28:[function(require,module,exports){
3829// Simple, stupid "background"/"background-image" value parser that just aims at exposing the image URLs
3830"use strict";
3831
3832var cssSupport = require('./cssSupport');
3833
3834
3835var trimCSSWhitespace = function (url) {
3836 var whitespaceRegex = /^[\t\r\f\n ]*(.+?)[\t\r\f\n ]*$/;
3837
3838 return url.replace(whitespaceRegex, "$1");
3839};
3840
3841// TODO exporting this for the sake of unit testing. Should rather test the background value parser explicitly.
3842exports.extractCssUrl = function (cssUrl) {
3843 var urlRegex = /^url\(([^\)]+)\)/,
3844 quotedUrl;
3845
3846 if (!urlRegex.test(cssUrl)) {
3847 throw new Error("Invalid url");
3848 }
3849
3850 quotedUrl = urlRegex.exec(cssUrl)[1];
3851 return cssSupport.unquoteString(trimCSSWhitespace(quotedUrl));
3852};
3853
3854var sliceBackgroundDeclaration = function (backgroundDeclarationText) {
3855 var functionParamRegexS = "\\s*(?:\"[^\"]*\"|'[^']*'|[^\\(]+)\\s*",
3856 valueRegexS = "(" + "url\\(" + functionParamRegexS + "\\)" + "|" + "[^,\\s]+" + ")",
3857 simpleSingularBackgroundRegexS = "(?:\\s*" + valueRegexS + ")+",
3858 simpleBackgroundRegexS = "^\\s*(" + simpleSingularBackgroundRegexS + ")" +
3859 "(?:\\s*,\\s*(" + simpleSingularBackgroundRegexS + "))*" +
3860 "\\s*$",
3861 simpleSingularBackgroundRegex = new RegExp(simpleSingularBackgroundRegexS, "g"),
3862 outerRepeatedMatch,
3863 backgroundLayers = [],
3864 getValues = function (singularBackgroundDeclaration) {
3865 var valueRegex = new RegExp(valueRegexS, "g"),
3866 backgroundValues = [],
3867 repeatedMatch;
3868
3869 repeatedMatch = valueRegex.exec(singularBackgroundDeclaration);
3870 while (repeatedMatch) {
3871 backgroundValues.push(repeatedMatch[1]);
3872 repeatedMatch = valueRegex.exec(singularBackgroundDeclaration);
3873 }
3874 return backgroundValues;
3875 };
3876
3877 if (backgroundDeclarationText.match(new RegExp(simpleBackgroundRegexS))) {
3878 outerRepeatedMatch = simpleSingularBackgroundRegex.exec(backgroundDeclarationText);
3879 while (outerRepeatedMatch) {
3880 backgroundLayers.push(getValues(outerRepeatedMatch[0]));
3881 outerRepeatedMatch = simpleSingularBackgroundRegex.exec(backgroundDeclarationText);
3882 }
3883
3884 return backgroundLayers;
3885 }
3886 return [];
3887};
3888
3889var findBackgroundImageUrlInValues = function (values) {
3890 var i, url;
3891
3892 for(i = 0; i < values.length; i++) {
3893 try {
3894 url = exports.extractCssUrl(values[i]);
3895 return {
3896 url: url,
3897 idx: i
3898 };
3899 } catch (e) {}
3900 }
3901};
3902
3903exports.parse = function (backgroundValue) {
3904 var backgroundLayers = sliceBackgroundDeclaration(backgroundValue);
3905
3906 return backgroundLayers.map(function (backgroundLayerValues) {
3907 var urlMatch = findBackgroundImageUrlInValues(backgroundLayerValues);
3908
3909 if (urlMatch) {
3910 return {
3911 preUrl: backgroundLayerValues.slice(0, urlMatch.idx),
3912 url: urlMatch.url,
3913 postUrl: backgroundLayerValues.slice(urlMatch.idx+1),
3914 };
3915 } else {
3916 return {
3917 preUrl: backgroundLayerValues
3918 };
3919 }
3920 });
3921};
3922
3923exports.serialize = function (parsedBackground) {
3924 var backgroundLayers = parsedBackground.map(function (backgroundLayer) {
3925 var values = [].concat(backgroundLayer.preUrl);
3926
3927 if (backgroundLayer.url) {
3928 values.push('url("' + backgroundLayer.url + '")');
3929 }
3930 if (backgroundLayer.postUrl) {
3931 values = values.concat(backgroundLayer.postUrl);
3932 }
3933
3934 return values.join(' ');
3935 });
3936
3937 return backgroundLayers.join(', ');
3938};
3939
3940},{"./cssSupport":29}],29:[function(require,module,exports){
3941"use strict";
3942
3943var cssom;
3944
3945try {
3946 cssom = require('cssom');
3947} catch (e) {
3948}
3949
3950
3951exports.unquoteString = function (quotedUrl) {
3952 var doubleQuoteRegex = /^"(.*)"$/,
3953 singleQuoteRegex = /^'(.*)'$/;
3954
3955 if (doubleQuoteRegex.test(quotedUrl)) {
3956 return quotedUrl.replace(doubleQuoteRegex, "$1");
3957 } else {
3958 if (singleQuoteRegex.test(quotedUrl)) {
3959 return quotedUrl.replace(singleQuoteRegex, "$1");
3960 } else {
3961 return quotedUrl;
3962 }
3963 }
3964};
3965
3966var rulesForCssTextFromBrowser = function (styleContent) {
3967 var doc = document.implementation.createHTMLDocument(""),
3968 styleElement = document.createElement("style"),
3969 rules;
3970
3971 styleElement.textContent = styleContent;
3972 // the style will only be parsed once it is added to a document
3973 doc.body.appendChild(styleElement);
3974 rules = styleElement.sheet.cssRules;
3975
3976 return Array.prototype.slice.call(rules);
3977};
3978
3979var browserHasBackgroundImageUrlIssue = (function () {
3980 // Checks for http://code.google.com/p/chromium/issues/detail?id=161644
3981 var rules = rulesForCssTextFromBrowser('a{background:url(i)}');
3982 return !rules.length || rules[0].cssText.indexOf('url()') >= 0;
3983}());
3984
3985var browserHasFontFaceUrlIssue = (function () {
3986 // Checks for https://bugs.chromium.org/p/chromium/issues/detail?id=588129
3987 var rules = rulesForCssTextFromBrowser('@font-face { font-family: "f"; src: url("f"); }');
3988 return !rules.length || /url\(['"]*\)/.test(rules[0].cssText);
3989}());
3990
3991var browserHasBackgroundImageUrlSetIssue = (function () {
3992 // Checks for https://bugs.chromium.org/p/chromium/issues/detail?id=660663
3993 var rules = rulesForCssTextFromBrowser('a{background:url(old)}');
3994 rules[0].style.setProperty('background', 'url(new)', '');
3995 return rules[0].style.getPropertyValue('background').indexOf('old') >= 0;
3996}());
3997
3998exports.rulesForCssText = function (styleContent) {
3999 if ((browserHasBackgroundImageUrlIssue || browserHasFontFaceUrlIssue || browserHasBackgroundImageUrlSetIssue) && cssom && cssom.parse) {
4000 return cssom.parse(styleContent).cssRules;
4001 } else {
4002 return rulesForCssTextFromBrowser(styleContent);
4003 }
4004};
4005
4006exports.cssRulesToText = function (cssRules) {
4007 return cssRules.reduce(function (cssText, rule) {
4008 return cssText + rule.cssText;
4009 }, '');
4010};
4011
4012exports.exchangeRule = function (cssRules, rule, newRuleText) {
4013 var ruleIdx = cssRules.indexOf(rule);
4014
4015 // We create a new document and stylesheet to parse the rule,
4016 // instead of relying on rule.parentStyleSheet, because
4017 // rule.parentStyleSheet may be null
4018 // (https://github.com/cburgmer/inlineresources/issues/3)
4019 cssRules[ruleIdx] = exports.rulesForCssText(newRuleText)[0];
4020};
4021
4022// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=443978
4023exports.changeFontFaceRuleSrc = function (cssRules, rule, newSrc) {
4024 var newRuleText = '@font-face { font-family: ' + rule.style.getPropertyValue("font-family") + '; ';
4025
4026 if (rule.style.getPropertyValue("font-style")) {
4027 newRuleText += 'font-style: ' + rule.style.getPropertyValue("font-style") + '; ';
4028 }
4029
4030 if (rule.style.getPropertyValue("font-weight")) {
4031 newRuleText += 'font-weight: ' + rule.style.getPropertyValue("font-weight") + '; ';
4032 }
4033
4034 newRuleText += 'src: ' + newSrc + '}';
4035 exports.exchangeRule(cssRules, rule, newRuleText);
4036};
4037
4038},{"cssom":26}],30:[function(require,module,exports){
4039"use strict";
4040
4041var util = require('./util'),
4042 inlineImage = require('./inlineImage'),
4043 inlineScript = require('./inlineScript'),
4044 inlineCss = require('./inlineCss'),
4045 cssSupport = require('./cssSupport');
4046
4047
4048var getUrlBasePath = function (url) {
4049 return util.joinUrl(url, '.');
4050};
4051
4052var parameterHashFunction = function (params) {
4053 // HACK JSON.stringify is poor man's hashing;
4054 // same objects might not receive same result as key order is not guaranteed
4055 var a = params.map(function (param, idx) {
4056 // Only include options relevant for method
4057 if (idx === (params.length - 1)) {
4058 param = {
4059 // Two different HTML pages on the same path level have the same base path, but a different URL
4060 baseUrl: getUrlBasePath(param.baseUrl)
4061 };
4062 }
4063 return JSON.stringify(param);
4064 });
4065 return a;
4066};
4067
4068var memoizeFunctionOnCaching = function (func, options) {
4069 if ((options.cache !== false && options.cache !== 'none') && options.cacheBucket) {
4070 return util.memoize(func, parameterHashFunction, options.cacheBucket);
4071 } else {
4072 return func;
4073 }
4074};
4075
4076/* Style inlining */
4077
4078var requestExternalsForStylesheet = function (styleContent, alreadyLoadedCssUrls, options) {
4079 var cssRules = cssSupport.rulesForCssText(styleContent);
4080
4081 return inlineCss.loadCSSImportsForRules(cssRules, alreadyLoadedCssUrls, options).then(function (cssImportResult) {
4082 return inlineCss.loadAndInlineCSSResourcesForRules(cssRules, options).then(function (cssResourcesResult) {
4083 var errors = cssImportResult.errors.concat(cssResourcesResult.errors),
4084 hasChanges = cssImportResult.hasChanges || cssResourcesResult.hasChanges;
4085
4086 if (hasChanges) {
4087 styleContent = cssSupport.cssRulesToText(cssRules);
4088 }
4089
4090 return {
4091 hasChanges: hasChanges,
4092 content: styleContent,
4093 errors: errors
4094 };
4095 });
4096 });
4097};
4098
4099var loadAndInlineCssForStyle = function (style, options, alreadyLoadedCssUrls) {
4100 var styleContent = style.textContent,
4101 processExternals = memoizeFunctionOnCaching(requestExternalsForStylesheet, options);
4102
4103 return processExternals(styleContent, alreadyLoadedCssUrls, options).then(function (result) {
4104 if (result.hasChanges) {
4105 style.childNodes[0].nodeValue = result.content;
4106 }
4107
4108 return util.cloneArray(result.errors);
4109 });
4110};
4111
4112var getCssStyleElements = function (doc) {
4113 var styles = doc.getElementsByTagName("style");
4114
4115 return Array.prototype.filter.call(styles, function (style) {
4116 return !style.attributes.type || style.attributes.type.value === "text/css";
4117 });
4118};
4119
4120exports.loadAndInlineStyles = function (doc, options) {
4121 var styles = getCssStyleElements(doc),
4122 allErrors = [],
4123 alreadyLoadedCssUrls = [],
4124 inlineOptions;
4125
4126 inlineOptions = util.clone(options);
4127 inlineOptions.baseUrl = inlineOptions.baseUrl || util.getDocumentBaseUrl(doc);
4128
4129 return util.all(styles.map(function (style) {
4130 return loadAndInlineCssForStyle(style, inlineOptions, alreadyLoadedCssUrls).then(function (errors) {
4131 allErrors = allErrors.concat(errors);
4132 });
4133 })).then(function () {
4134 return allErrors;
4135 });
4136};
4137
4138/* CSS link inlining */
4139
4140var substituteLinkWithInlineStyle = function (oldLinkNode, styleContent) {
4141 var parent = oldLinkNode.parentNode,
4142 styleNode;
4143
4144 styleContent = styleContent.trim();
4145 if (styleContent) {
4146 styleNode = oldLinkNode.ownerDocument.createElement("style");
4147 styleNode.type = "text/css";
4148 styleNode.appendChild(oldLinkNode.ownerDocument.createTextNode(styleContent));
4149
4150 parent.insertBefore(styleNode, oldLinkNode);
4151 }
4152
4153 parent.removeChild(oldLinkNode);
4154};
4155
4156var requestStylesheetAndInlineResources = function (url, options) {
4157 return util.ajax(url, options)
4158 .then(function (content) {
4159 var cssRules = cssSupport.rulesForCssText(content);
4160
4161 return {
4162 content: content,
4163 cssRules: cssRules
4164 };
4165 })
4166 .then(function (result) {
4167 var hasChangesFromPathAdjustment = inlineCss.adjustPathsOfCssResources(url, result.cssRules);
4168
4169 return {
4170 content: result.content,
4171 cssRules: result.cssRules,
4172 hasChanges: hasChangesFromPathAdjustment
4173 };
4174 })
4175 .then(function (result) {
4176 return inlineCss.loadCSSImportsForRules(result.cssRules, [], options)
4177 .then(function (cssImportResult) {
4178 return {
4179 content: result.content,
4180 cssRules: result.cssRules,
4181 hasChanges: result.hasChanges || cssImportResult.hasChanges,
4182 errors: cssImportResult.errors
4183 };
4184 });
4185 })
4186 .then(function (result) {
4187 return inlineCss.loadAndInlineCSSResourcesForRules(result.cssRules, options)
4188 .then(function (cssResourcesResult) {
4189 return {
4190 content: result.content,
4191 cssRules: result.cssRules,
4192 hasChanges: result.hasChanges || cssResourcesResult.hasChanges,
4193 errors: result.errors.concat(cssResourcesResult.errors)
4194 };
4195 });
4196 })
4197 .then(function (result) {
4198 var content = result.content;
4199 if (result.hasChanges) {
4200 content = cssSupport.cssRulesToText(result.cssRules);
4201 }
4202 return {
4203 content: content,
4204 errors: result.errors
4205 };
4206 });
4207};
4208
4209var loadLinkedCSS = function (link, options) {
4210 var cssHref = link.attributes.href.value,
4211 documentBaseUrl = util.getDocumentBaseUrl(link.ownerDocument),
4212 ajaxOptions = util.clone(options);
4213
4214 if (!ajaxOptions.baseUrl && documentBaseUrl) {
4215 ajaxOptions.baseUrl = documentBaseUrl;
4216 }
4217
4218 var processStylesheet = memoizeFunctionOnCaching(requestStylesheetAndInlineResources, options);
4219
4220 return processStylesheet(cssHref, ajaxOptions).then(function (result) {
4221 return {
4222 content: result.content,
4223 errors: util.cloneArray(result.errors)
4224 };
4225 });
4226};
4227
4228var getCssStylesheetLinks = function (doc) {
4229 var links = doc.getElementsByTagName("link");
4230
4231 return Array.prototype.filter.call(links, function (link) {
4232 return link.attributes.rel && link.attributes.rel.value === "stylesheet" &&
4233 (!link.attributes.type || link.attributes.type.value === "text/css");
4234 });
4235};
4236
4237exports.loadAndInlineCssLinks = function (doc, options) {
4238 var links = getCssStylesheetLinks(doc),
4239 errors = [];
4240
4241 return util.all(links.map(function (link) {
4242 return loadLinkedCSS(link, options).then(function(result) {
4243 substituteLinkWithInlineStyle(link, result.content + "\n");
4244
4245 errors = errors.concat(result.errors);
4246 }, function (e) {
4247 errors.push({
4248 resourceType: "stylesheet",
4249 url: e.url,
4250 msg: "Unable to load stylesheet " + e.url
4251 });
4252 });
4253 })).then(function () {
4254 return errors;
4255 });
4256};
4257
4258/* Main */
4259
4260exports.loadAndInlineImages = inlineImage.inline;
4261exports.loadAndInlineScript = inlineScript.inline;
4262
4263exports.inlineReferences = function (doc, options) {
4264 var allErrors = [],
4265 inlineFuncs = [
4266 exports.loadAndInlineImages,
4267 exports.loadAndInlineStyles,
4268 exports.loadAndInlineCssLinks];
4269
4270 if (options.inlineScripts !== false) {
4271 inlineFuncs.push(exports.loadAndInlineScript);
4272 }
4273
4274 return util.all(inlineFuncs.map(function (func) {
4275 return func(doc, options)
4276 .then(function (errors) {
4277 allErrors = allErrors.concat(errors);
4278 });
4279 })).then(function () {
4280 return allErrors;
4281 });
4282};
4283
4284},{"./cssSupport":29,"./inlineCss":31,"./inlineImage":32,"./inlineScript":33,"./util":34}],31:[function(require,module,exports){
4285"use strict";
4286
4287var ayepromise = require('ayepromise'),
4288 util = require('./util'),
4289 cssSupport = require('./cssSupport'),
4290 backgroundValueParser = require('./backgroundValueParser'),
4291 fontFaceSrcValueParser = require('css-font-face-src');
4292
4293
4294var updateCssPropertyValue = function (rule, property, value) {
4295 rule.style.setProperty(property, value, rule.style.getPropertyPriority(property));
4296};
4297
4298var findBackgroundImageRules = function (cssRules) {
4299 return cssRules.filter(function (rule) {
4300 return rule.type === window.CSSRule.STYLE_RULE && (rule.style.getPropertyValue('background-image') || rule.style.getPropertyValue('background'));
4301 });
4302};
4303
4304var findBackgroundDeclarations = function (rules) {
4305 var backgroundDeclarations = [];
4306
4307 rules.forEach(function (rule) {
4308 if (rule.style.getPropertyValue('background-image')) {
4309 backgroundDeclarations.push({
4310 property: 'background-image',
4311 value: rule.style.getPropertyValue('background-image'),
4312 rule: rule
4313 });
4314 } else if (rule.style.getPropertyValue('background')) {
4315 backgroundDeclarations.push({
4316 property: 'background',
4317 value: rule.style.getPropertyValue('background'),
4318 rule: rule
4319 });
4320 }
4321 });
4322
4323 return backgroundDeclarations;
4324};
4325
4326var findFontFaceRules = function (cssRules) {
4327 return cssRules.filter(function (rule) {
4328 return rule.type === window.CSSRule.FONT_FACE_RULE && rule.style.getPropertyValue("src");
4329 });
4330};
4331
4332var findCSSImportRules = function (cssRules) {
4333 return cssRules.filter(function (rule) {
4334 return rule.type === window.CSSRule.IMPORT_RULE && rule.href;
4335 });
4336};
4337
4338var findExternalBackgroundUrls = function (parsedBackground) {
4339 var matchIndices = [];
4340
4341 parsedBackground.forEach(function (backgroundLayer, i) {
4342 if (backgroundLayer.url && !util.isDataUri(backgroundLayer.url)) {
4343 matchIndices.push(i);
4344 }
4345 });
4346
4347 return matchIndices;
4348};
4349
4350var findExternalFontFaceUrls = function (parsedFontFaceSources) {
4351 var sourceIndices = [];
4352 parsedFontFaceSources.forEach(function (sourceItem, i) {
4353 if (sourceItem.url && !util.isDataUri(sourceItem.url)) {
4354 sourceIndices.push(i);
4355 }
4356 });
4357 return sourceIndices;
4358};
4359
4360exports.adjustPathsOfCssResources = function (baseUrl, cssRules) {
4361 var backgroundRules = findBackgroundImageRules(cssRules),
4362 backgroundDeclarations = findBackgroundDeclarations(backgroundRules),
4363 change = false;
4364
4365 backgroundDeclarations.forEach(function (declaration) {
4366 var parsedBackground = backgroundValueParser.parse(declaration.value),
4367 externalBackgroundIndices = findExternalBackgroundUrls(parsedBackground),
4368 backgroundValue;
4369
4370 if (externalBackgroundIndices.length > 0) {
4371 externalBackgroundIndices.forEach(function (backgroundLayerIndex) {
4372 var relativeUrl = parsedBackground[backgroundLayerIndex].url,
4373 url = util.joinUrl(baseUrl, relativeUrl);
4374 parsedBackground[backgroundLayerIndex].url = url;
4375 });
4376
4377 backgroundValue = backgroundValueParser.serialize(parsedBackground);
4378
4379 updateCssPropertyValue(declaration.rule, declaration.property, backgroundValue);
4380
4381 change = true;
4382 }
4383 });
4384 findFontFaceRules(cssRules).forEach(function (rule) {
4385 var fontFaceSrcDeclaration = rule.style.getPropertyValue("src"),
4386 parsedFontFaceSources, externalFontFaceUrlIndices;
4387
4388 try {
4389 parsedFontFaceSources = fontFaceSrcValueParser.parse(fontFaceSrcDeclaration);
4390 } catch (e) {
4391 return;
4392 }
4393 externalFontFaceUrlIndices = findExternalFontFaceUrls(parsedFontFaceSources);
4394
4395 if (externalFontFaceUrlIndices.length > 0) {
4396 externalFontFaceUrlIndices.forEach(function (fontFaceUrlIndex) {
4397 var relativeUrl = parsedFontFaceSources[fontFaceUrlIndex].url,
4398 url = util.joinUrl(baseUrl, relativeUrl);
4399
4400 parsedFontFaceSources[fontFaceUrlIndex].url = url;
4401 });
4402
4403 cssSupport.changeFontFaceRuleSrc(cssRules, rule, fontFaceSrcValueParser.serialize(parsedFontFaceSources));
4404
4405 change = true;
4406 }
4407 });
4408 findCSSImportRules(cssRules).forEach(function (rule) {
4409 var cssUrl = rule.href,
4410 url = util.joinUrl(baseUrl, cssUrl);
4411
4412 cssSupport.exchangeRule(cssRules, rule, "@import url(" + url + ");");
4413
4414 change = true;
4415 });
4416
4417 return change;
4418};
4419
4420/* CSS import inlining */
4421
4422var substituteRule = function (cssRules, rule, newCssRules) {
4423 var position = cssRules.indexOf(rule);
4424
4425 cssRules.splice(position, 1);
4426
4427 newCssRules.forEach(function (newRule, i) {
4428 cssRules.splice(position + i, 0, newRule);
4429 });
4430};
4431
4432var fulfilledPromise = function (value) {
4433 var defer = ayepromise.defer();
4434 defer.resolve(value);
4435 return defer.promise;
4436};
4437
4438var loadAndInlineCSSImport = function (cssRules, rule, alreadyLoadedCssUrls, options) {
4439 var url = rule.href,
4440 cssHrefRelativeToDoc;
4441
4442 url = cssSupport.unquoteString(url);
4443
4444 cssHrefRelativeToDoc = util.joinUrl(options.baseUrl, url);
4445
4446 if (alreadyLoadedCssUrls.indexOf(cssHrefRelativeToDoc) >= 0) {
4447 // Remove URL by adding empty string
4448 substituteRule(cssRules, rule, []);
4449 return fulfilledPromise([]);
4450 } else {
4451 alreadyLoadedCssUrls.push(cssHrefRelativeToDoc);
4452 }
4453
4454 return util.ajax(url, options)
4455 .then(function (cssText) {
4456 var externalCssRules = cssSupport.rulesForCssText(cssText);
4457
4458 // Recursively follow @import statements
4459 return exports.loadCSSImportsForRules(externalCssRules, alreadyLoadedCssUrls, options)
4460 .then(function (result) {
4461 exports.adjustPathsOfCssResources(url, externalCssRules);
4462
4463 substituteRule(cssRules, rule, externalCssRules);
4464
4465 return result.errors;
4466 });
4467 }, function (e) {
4468 throw {
4469 resourceType: "stylesheet",
4470 url: e.url,
4471 msg: "Unable to load stylesheet " + e.url
4472 };
4473 });
4474};
4475
4476exports.loadCSSImportsForRules = function (cssRules, alreadyLoadedCssUrls, options) {
4477 var rulesToInline = findCSSImportRules(cssRules),
4478 errors = [],
4479 hasChanges = false;
4480
4481 return util.all(rulesToInline.map(function (rule) {
4482 return loadAndInlineCSSImport(cssRules, rule, alreadyLoadedCssUrls, options).then(function (moreErrors) {
4483 errors = errors.concat(moreErrors);
4484
4485 hasChanges = true;
4486 }, function (e) {
4487 errors.push(e);
4488 });
4489 })).then(function () {
4490 return {
4491 hasChanges: hasChanges,
4492 errors: errors
4493 };
4494 });
4495};
4496
4497/* CSS linked resource inlining */
4498
4499var loadAndInlineBackgroundImages = function (backgroundValue, options) {
4500 var parsedBackground = backgroundValueParser.parse(backgroundValue),
4501 externalBackgroundLayerIndices = findExternalBackgroundUrls(parsedBackground),
4502 hasChanges = false;
4503
4504 return util.collectAndReportErrors(externalBackgroundLayerIndices.map(function (backgroundLayerIndex) {
4505 var url = parsedBackground[backgroundLayerIndex].url;
4506
4507 return util.getDataURIForImageURL(url, options)
4508 .then(function (dataURI) {
4509 parsedBackground[backgroundLayerIndex].url = dataURI;
4510
4511 hasChanges = true;
4512 }, function (e) {
4513 throw {
4514 resourceType: "backgroundImage",
4515 url: e.url,
4516 msg: "Unable to load background-image " + e.url
4517 };
4518 });
4519 })).then(function (errors) {
4520 return {
4521 backgroundValue: backgroundValueParser.serialize(parsedBackground),
4522 hasChanges: hasChanges,
4523 errors: errors
4524 };
4525 });
4526};
4527
4528var iterateOverRulesAndInlineBackgroundImages = function (cssRules, options) {
4529 var rulesToInline = findBackgroundImageRules(cssRules),
4530 backgroundDeclarations = findBackgroundDeclarations(rulesToInline),
4531 errors = [],
4532 cssHasChanges = false;
4533
4534 return util.all(backgroundDeclarations.map(function (declaration) {
4535 return loadAndInlineBackgroundImages(declaration.value, options)
4536 .then(function (result) {
4537 if (result.hasChanges) {
4538 updateCssPropertyValue(declaration.rule, declaration.property, result.backgroundValue);
4539
4540 cssHasChanges = true;
4541 }
4542
4543 errors = errors.concat(result.errors);
4544 });
4545 })).then(function () {
4546 return {
4547 hasChanges: cssHasChanges,
4548 errors: errors
4549 };
4550 });
4551};
4552
4553var loadAndInlineFontFace = function (srcDeclarationValue, options) {
4554 var hasChanges = false,
4555 parsedFontFaceSources, externalFontFaceUrlIndices;
4556
4557 try {
4558 parsedFontFaceSources = fontFaceSrcValueParser.parse(srcDeclarationValue);
4559 } catch (e) {
4560 parsedFontFaceSources = [];
4561 }
4562 externalFontFaceUrlIndices = findExternalFontFaceUrls(parsedFontFaceSources);
4563
4564 return util.collectAndReportErrors(externalFontFaceUrlIndices.map(function (urlIndex) {
4565 var fontSrc = parsedFontFaceSources[urlIndex],
4566 format = fontSrc.format || "woff";
4567
4568 return util.binaryAjax(fontSrc.url, options)
4569 .then(function (content) {
4570 var base64Content = btoa(content);
4571 fontSrc.url = 'data:font/' + format + ';base64,' + base64Content;
4572
4573 hasChanges = true;
4574 }, function (e) {
4575 throw {
4576 resourceType: "fontFace",
4577 url: e.url,
4578 msg: "Unable to load font-face " + e.url
4579 };
4580 });
4581 })).then(function (errors) {
4582 return {
4583 srcDeclarationValue: fontFaceSrcValueParser.serialize(parsedFontFaceSources),
4584 hasChanges: hasChanges,
4585 errors: errors
4586 };
4587 });
4588};
4589
4590var iterateOverRulesAndInlineFontFace = function (cssRules, options) {
4591 var rulesToInline = findFontFaceRules(cssRules),
4592 errors = [],
4593 hasChanges = false;
4594
4595 return util.all(rulesToInline.map(function (rule) {
4596 var srcDeclarationValue = rule.style.getPropertyValue("src");
4597
4598 return loadAndInlineFontFace(srcDeclarationValue, options).then(function (result) {
4599 if (result.hasChanges) {
4600 cssSupport.changeFontFaceRuleSrc(cssRules, rule, result.srcDeclarationValue);
4601
4602 hasChanges = true;
4603 }
4604
4605 errors = errors.concat(result.errors);
4606 });
4607 })).then(function () {
4608 return {
4609 hasChanges: hasChanges,
4610 errors: errors
4611 };
4612 });
4613};
4614
4615exports.loadAndInlineCSSResourcesForRules = function (cssRules, options) {
4616 var hasChanges = false,
4617 errors = [];
4618
4619 return util.all([iterateOverRulesAndInlineBackgroundImages, iterateOverRulesAndInlineFontFace].map(function (func) {
4620 return func(cssRules, options)
4621 .then(function (result) {
4622 hasChanges = hasChanges || result.hasChanges;
4623 errors = errors.concat(result.errors);
4624 });
4625 })).then(function () {
4626 return {
4627 hasChanges: hasChanges,
4628 errors: errors
4629 };
4630 });
4631};
4632
4633},{"./backgroundValueParser":28,"./cssSupport":29,"./util":34,"ayepromise":2,"css-font-face-src":6}],32:[function(require,module,exports){
4634"use strict";
4635
4636var util = require('./util');
4637
4638
4639var encodeImageAsDataURI = function (image, options) {
4640 var url = null;
4641 if(image.hasAttribute('src')){
4642 url = image.getAttribute('src');
4643 }
4644 else if(image.hasAttributeNS('http://www.w3.org/1999/xlink','href')){
4645 url = image.getAttributeNS('http://www.w3.org/1999/xlink','href');
4646 }
4647 else if(image.hasAttribute('href')){
4648 url = image.getAttribute('href');
4649 }
4650 var documentBase = util.getDocumentBaseUrl(image.ownerDocument),
4651 ajaxOptions = util.clone(options);
4652
4653 if (!ajaxOptions.baseUrl && documentBase) {
4654 ajaxOptions.baseUrl = documentBase;
4655 }
4656
4657 return util.getDataURIForImageURL(url, ajaxOptions)
4658 .then(function (dataURI) {
4659 return dataURI;
4660 }, function (e) {
4661 throw {
4662 resourceType: "image",
4663 url: e.url,
4664 msg: "Unable to load image " + e.url
4665 };
4666 });
4667};
4668
4669var filterExternalImages = function (images) {
4670 return images.filter(function (image) {
4671 var url = null;
4672 if(image.hasAttribute('src')){
4673 url = image.getAttribute('src');
4674 }
4675 else if(image.hasAttributeNS('http://www.w3.org/1999/xlink','href')){
4676 url = image.getAttributeNS('http://www.w3.org/1999/xlink','href');
4677 }
4678 else if(image.hasAttribute('href')){
4679 url = image.getAttribute('href');
4680 }
4681
4682 return url !== null && !util.isDataUri(url);
4683 });
4684};
4685
4686var filterInputsForImageType = function (inputs) {
4687 return Array.prototype.filter.call(inputs, function (input) {
4688 return input.type === "image";
4689 });
4690};
4691
4692var toArray = function (arrayLike) {
4693 return Array.prototype.slice.call(arrayLike);
4694};
4695
4696exports.inline = function (doc, options) {
4697 var images = toArray(doc.getElementsByTagName("img")),
4698 svgImages = toArray(doc.getElementsByTagName("image")),
4699 imageInputs = filterInputsForImageType(doc.getElementsByTagName("input"));
4700
4701 images = images.concat(svgImages);
4702 images = images.concat(imageInputs);
4703 var externalImages = filterExternalImages(images);
4704
4705 return util.collectAndReportErrors(externalImages.map(function (image) {
4706 return encodeImageAsDataURI(image, options).then(function (dataURI) {
4707 if(!!image.attributes.src){
4708 image.attributes.src.value = dataURI;
4709 }
4710 else if(!!image.attributes['xlink:href']){
4711 image.attributes['xlink:href'].value = dataURI;
4712 }
4713 else if(!!image.attributes.href){
4714 image.attributes.href.value = dataURI;
4715 }
4716 });
4717 }));
4718};
4719
4720},{"./util":34}],33:[function(require,module,exports){
4721"use strict";
4722
4723var util = require('./util');
4724
4725
4726var loadLinkedScript = function (script, options) {
4727 var src = script.attributes.src.value,
4728 documentBase = util.getDocumentBaseUrl(script.ownerDocument),
4729 ajaxOptions = util.clone(options);
4730
4731 if (!ajaxOptions.baseUrl && documentBase) {
4732 ajaxOptions.baseUrl = documentBase;
4733 }
4734
4735 return util.ajax(src, ajaxOptions)
4736 .fail(function (e) {
4737 throw {
4738 resourceType: "script",
4739 url: e.url,
4740 msg: "Unable to load script " + e.url
4741 };
4742 });
4743};
4744
4745var escapeClosingTags = function (text) {
4746 // http://stackoverflow.com/questions/9246382/escaping-script-tag-inside-javascript
4747 return text.replace(/<\//g, '<\\/');
4748};
4749
4750var substituteExternalScriptWithInline = function (scriptNode, jsCode) {
4751 scriptNode.attributes.removeNamedItem('src');
4752 scriptNode.textContent = escapeClosingTags(jsCode);
4753};
4754
4755var getScripts = function (doc) {
4756 var scripts = doc.getElementsByTagName("script");
4757
4758 return Array.prototype.filter.call(scripts, function (script) {
4759 return !!script.attributes.src;
4760 });
4761};
4762
4763exports.inline = function (doc, options) {
4764 var scripts = getScripts(doc);
4765
4766 return util.collectAndReportErrors(scripts.map(function (script) {
4767 return loadLinkedScript(script, options).then(function (jsCode) {
4768 substituteExternalScriptWithInline(script, jsCode);
4769 });
4770 }));
4771};
4772
4773},{"./util":34}],34:[function(require,module,exports){
4774"use strict";
4775
4776var url = require('url'),
4777 ayepromise = require('ayepromise');
4778
4779
4780exports.getDocumentBaseUrl = function (doc) {
4781 if (doc.baseURI !== 'about:blank') {
4782 return doc.baseURI;
4783 }
4784
4785 return null;
4786};
4787
4788exports.clone = function (object) {
4789 var theClone = {},
4790 i;
4791 for (i in object) {
4792 if (object.hasOwnProperty(i)) {
4793 theClone[i] = object[i];
4794 }
4795 }
4796 return theClone;
4797};
4798
4799exports.cloneArray = function (nodeList) {
4800 return Array.prototype.slice.apply(nodeList, [0]);
4801};
4802
4803exports.joinUrl = function (baseUrl, relUrl) {
4804 if (!baseUrl) {
4805 return relUrl;
4806 }
4807 return url.resolve(baseUrl, relUrl);
4808};
4809
4810exports.isDataUri = function (url) {
4811 return (/^data:/).test(url);
4812};
4813
4814exports.all = function (promises) {
4815 var defer = ayepromise.defer(),
4816 pendingPromiseCount = promises.length,
4817 resolvedValues = [];
4818
4819 if (promises.length === 0) {
4820 defer.resolve([]);
4821 return defer.promise;
4822 }
4823
4824 promises.forEach(function (promise, idx) {
4825 promise.then(function (value) {
4826 pendingPromiseCount -= 1;
4827 resolvedValues[idx] = value;
4828
4829 if (pendingPromiseCount === 0) {
4830 defer.resolve(resolvedValues);
4831 }
4832 }, function (e) {
4833 defer.reject(e);
4834 });
4835 });
4836 return defer.promise;
4837};
4838
4839exports.collectAndReportErrors = function (promises) {
4840 var errors = [];
4841
4842 return exports.all(promises.map(function (promise) {
4843 return promise.fail(function (e) {
4844 errors.push(e);
4845 });
4846 })).then(function () {
4847 return errors;
4848 });
4849};
4850
4851var lastCacheDate = null;
4852
4853var getUncachableURL = function (url, cache) {
4854 if (cache === false || cache === 'none' || cache === 'repeated') {
4855 if (lastCacheDate === null || cache !== 'repeated') {
4856 lastCacheDate = Date.now();
4857 }
4858 return url + "?_=" + lastCacheDate;
4859 } else {
4860 return url;
4861 }
4862};
4863
4864exports.ajax = function (url, options) {
4865 var ajaxRequest = new window.XMLHttpRequest(),
4866 defer = ayepromise.defer(),
4867 joinedUrl = exports.joinUrl(options.baseUrl, url),
4868 augmentedUrl;
4869
4870 var doReject = function () {
4871 defer.reject({
4872 msg: 'Unable to load url',
4873 url: joinedUrl
4874 });
4875 };
4876
4877 augmentedUrl = getUncachableURL(joinedUrl, options.cache);
4878
4879 ajaxRequest.addEventListener("load", function () {
4880 if (ajaxRequest.status === 200 || ajaxRequest.status === 0) {
4881 defer.resolve(ajaxRequest.response);
4882 } else {
4883 doReject();
4884 }
4885 }, false);
4886
4887 ajaxRequest.addEventListener("error", doReject, false);
4888
4889 try {
4890 ajaxRequest.open('GET', augmentedUrl, true);
4891 ajaxRequest.overrideMimeType(options.mimeType);
4892 ajaxRequest.send(null);
4893 } catch (e) {
4894 doReject();
4895 }
4896
4897 return defer.promise;
4898};
4899
4900exports.binaryAjax = function (url, options) {
4901 var ajaxOptions = exports.clone(options);
4902
4903 ajaxOptions.mimeType = 'text/plain; charset=x-user-defined';
4904
4905 return exports.ajax(url, ajaxOptions)
4906 .then(function (content) {
4907 var binaryContent = "";
4908
4909 for (var i = 0; i < content.length; i++) {
4910 binaryContent += String.fromCharCode(content.charCodeAt(i) & 0xFF);
4911 }
4912
4913 return binaryContent;
4914 });
4915};
4916
4917var detectMimeType = function (content) {
4918 var startsWith = function (string, substring) {
4919 return string.substring(0, substring.length) === substring;
4920 };
4921
4922 if (startsWith(content, '<?xml') || startsWith(content, '<svg')) {
4923 return 'image/svg+xml';
4924 }
4925 return 'image/png';
4926};
4927
4928exports.getDataURIForImageURL = function (url, options) {
4929 return exports.binaryAjax(url, options)
4930 .then(function (content) {
4931 var base64Content = btoa(content),
4932 mimeType = detectMimeType(content);
4933
4934 return 'data:' + mimeType + ';base64,' + base64Content;
4935 });
4936};
4937
4938var uniqueIdList = [];
4939
4940var constantUniqueIdFor = function (element) {
4941 // HACK, using a list results in O(n), but how do we hash a function?
4942 if (uniqueIdList.indexOf(element) < 0) {
4943 uniqueIdList.push(element);
4944 }
4945 return uniqueIdList.indexOf(element);
4946};
4947
4948exports.memoize = function (func, hasher, memo) {
4949 if (typeof memo !== "object") {
4950 throw new Error("cacheBucket is not an object");
4951 }
4952
4953 return function () {
4954 var args = Array.prototype.slice.call(arguments);
4955
4956 var argumentHash = hasher(args),
4957 funcHash = constantUniqueIdFor(func),
4958 retValue;
4959
4960 if (memo[funcHash] && memo[funcHash][argumentHash]) {
4961 return memo[funcHash][argumentHash];
4962 } else {
4963 retValue = func.apply(null, args);
4964
4965 memo[funcHash] = memo[funcHash] || {};
4966 memo[funcHash][argumentHash] = retValue;
4967
4968 return retValue;
4969 }
4970 };
4971};
4972
4973},{"ayepromise":2,"url":3}],35:[function(require,module,exports){
4974(function (global){
4975/*! https://mths.be/punycode v1.3.2 by @mathias */
4976;(function(root) {
4977
4978 /** Detect free variables */
4979 var freeExports = typeof exports == 'object' && exports &&
4980 !exports.nodeType && exports;
4981 var freeModule = typeof module == 'object' && module &&
4982 !module.nodeType && module;
4983 var freeGlobal = typeof global == 'object' && global;
4984 if (
4985 freeGlobal.global === freeGlobal ||
4986 freeGlobal.window === freeGlobal ||
4987 freeGlobal.self === freeGlobal
4988 ) {
4989 root = freeGlobal;
4990 }
4991
4992 /**
4993 * The `punycode` object.
4994 * @name punycode
4995 * @type Object
4996 */
4997 var punycode,
4998
4999 /** Highest positive signed 32-bit float value */
5000 maxInt = 2147483647, // aka. 0x7FFFFFFF or 2^31-1
5001
5002 /** Bootstring parameters */
5003 base = 36,
5004 tMin = 1,
5005 tMax = 26,
5006 skew = 38,
5007 damp = 700,
5008 initialBias = 72,
5009 initialN = 128, // 0x80
5010 delimiter = '-', // '\x2D'
5011
5012 /** Regular expressions */
5013 regexPunycode = /^xn--/,
5014 regexNonASCII = /[^\x20-\x7E]/, // unprintable ASCII chars + non-ASCII chars
5015 regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, // RFC 3490 separators
5016
5017 /** Error messages */
5018 errors = {
5019 'overflow': 'Overflow: input needs wider integers to process',
5020 'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
5021 'invalid-input': 'Invalid input'
5022 },
5023
5024 /** Convenience shortcuts */
5025 baseMinusTMin = base - tMin,
5026 floor = Math.floor,
5027 stringFromCharCode = String.fromCharCode,
5028
5029 /** Temporary variable */
5030 key;
5031
5032 /*--------------------------------------------------------------------------*/
5033
5034 /**
5035 * A generic error utility function.
5036 * @private
5037 * @param {String} type The error type.
5038 * @returns {Error} Throws a `RangeError` with the applicable error message.
5039 */
5040 function error(type) {
5041 throw RangeError(errors[type]);
5042 }
5043
5044 /**
5045 * A generic `Array#map` utility function.
5046 * @private
5047 * @param {Array} array The array to iterate over.
5048 * @param {Function} callback The function that gets called for every array
5049 * item.
5050 * @returns {Array} A new array of values returned by the callback function.
5051 */
5052 function map(array, fn) {
5053 var length = array.length;
5054 var result = [];
5055 while (length--) {
5056 result[length] = fn(array[length]);
5057 }
5058 return result;
5059 }
5060
5061 /**
5062 * A simple `Array#map`-like wrapper to work with domain name strings or email
5063 * addresses.
5064 * @private
5065 * @param {String} domain The domain name or email address.
5066 * @param {Function} callback The function that gets called for every
5067 * character.
5068 * @returns {Array} A new string of characters returned by the callback
5069 * function.
5070 */
5071 function mapDomain(string, fn) {
5072 var parts = string.split('@');
5073 var result = '';
5074 if (parts.length > 1) {
5075 // In email addresses, only the domain name should be punycoded. Leave
5076 // the local part (i.e. everything up to `@`) intact.
5077 result = parts[0] + '@';
5078 string = parts[1];
5079 }
5080 // Avoid `split(regex)` for IE8 compatibility. See #17.
5081 string = string.replace(regexSeparators, '\x2E');
5082 var labels = string.split('.');
5083 var encoded = map(labels, fn).join('.');
5084 return result + encoded;
5085 }
5086
5087 /**
5088 * Creates an array containing the numeric code points of each Unicode
5089 * character in the string. While JavaScript uses UCS-2 internally,
5090 * this function will convert a pair of surrogate halves (each of which
5091 * UCS-2 exposes as separate characters) into a single code point,
5092 * matching UTF-16.
5093 * @see `punycode.ucs2.encode`
5094 * @see <https://mathiasbynens.be/notes/javascript-encoding>
5095 * @memberOf punycode.ucs2
5096 * @name decode
5097 * @param {String} string The Unicode input string (UCS-2).
5098 * @returns {Array} The new array of code points.
5099 */
5100 function ucs2decode(string) {
5101 var output = [],
5102 counter = 0,
5103 length = string.length,
5104 value,
5105 extra;
5106 while (counter < length) {
5107 value = string.charCodeAt(counter++);
5108 if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
5109 // high surrogate, and there is a next character
5110 extra = string.charCodeAt(counter++);
5111 if ((extra & 0xFC00) == 0xDC00) { // low surrogate
5112 output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
5113 } else {
5114 // unmatched surrogate; only append this code unit, in case the next
5115 // code unit is the high surrogate of a surrogate pair
5116 output.push(value);
5117 counter--;
5118 }
5119 } else {
5120 output.push(value);
5121 }
5122 }
5123 return output;
5124 }
5125
5126 /**
5127 * Creates a string based on an array of numeric code points.
5128 * @see `punycode.ucs2.decode`
5129 * @memberOf punycode.ucs2
5130 * @name encode
5131 * @param {Array} codePoints The array of numeric code points.
5132 * @returns {String} The new Unicode string (UCS-2).
5133 */
5134 function ucs2encode(array) {
5135 return map(array, function(value) {
5136 var output = '';
5137 if (value > 0xFFFF) {
5138 value -= 0x10000;
5139 output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800);
5140 value = 0xDC00 | value & 0x3FF;
5141 }
5142 output += stringFromCharCode(value);
5143 return output;
5144 }).join('');
5145 }
5146
5147 /**
5148 * Converts a basic code point into a digit/integer.
5149 * @see `digitToBasic()`
5150 * @private
5151 * @param {Number} codePoint The basic numeric code point value.
5152 * @returns {Number} The numeric value of a basic code point (for use in
5153 * representing integers) in the range `0` to `base - 1`, or `base` if
5154 * the code point does not represent a value.
5155 */
5156 function basicToDigit(codePoint) {
5157 if (codePoint - 48 < 10) {
5158 return codePoint - 22;
5159 }
5160 if (codePoint - 65 < 26) {
5161 return codePoint - 65;
5162 }
5163 if (codePoint - 97 < 26) {
5164 return codePoint - 97;
5165 }
5166 return base;
5167 }
5168
5169 /**
5170 * Converts a digit/integer into a basic code point.
5171 * @see `basicToDigit()`
5172 * @private
5173 * @param {Number} digit The numeric value of a basic code point.
5174 * @returns {Number} The basic code point whose value (when used for
5175 * representing integers) is `digit`, which needs to be in the range
5176 * `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
5177 * used; else, the lowercase form is used. The behavior is undefined
5178 * if `flag` is non-zero and `digit` has no uppercase form.
5179 */
5180 function digitToBasic(digit, flag) {
5181 // 0..25 map to ASCII a..z or A..Z
5182 // 26..35 map to ASCII 0..9
5183 return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
5184 }
5185
5186 /**
5187 * Bias adaptation function as per section 3.4 of RFC 3492.
5188 * http://tools.ietf.org/html/rfc3492#section-3.4
5189 * @private
5190 */
5191 function adapt(delta, numPoints, firstTime) {
5192 var k = 0;
5193 delta = firstTime ? floor(delta / damp) : delta >> 1;
5194 delta += floor(delta / numPoints);
5195 for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
5196 delta = floor(delta / baseMinusTMin);
5197 }
5198 return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
5199 }
5200
5201 /**
5202 * Converts a Punycode string of ASCII-only symbols to a string of Unicode
5203 * symbols.
5204 * @memberOf punycode
5205 * @param {String} input The Punycode string of ASCII-only symbols.
5206 * @returns {String} The resulting string of Unicode symbols.
5207 */
5208 function decode(input) {
5209 // Don't use UCS-2
5210 var output = [],
5211 inputLength = input.length,
5212 out,
5213 i = 0,
5214 n = initialN,
5215 bias = initialBias,
5216 basic,
5217 j,
5218 index,
5219 oldi,
5220 w,
5221 k,
5222 digit,
5223 t,
5224 /** Cached calculation results */
5225 baseMinusT;
5226
5227 // Handle the basic code points: let `basic` be the number of input code
5228 // points before the last delimiter, or `0` if there is none, then copy
5229 // the first basic code points to the output.
5230
5231 basic = input.lastIndexOf(delimiter);
5232 if (basic < 0) {
5233 basic = 0;
5234 }
5235
5236 for (j = 0; j < basic; ++j) {
5237 // if it's not a basic code point
5238 if (input.charCodeAt(j) >= 0x80) {
5239 error('not-basic');
5240 }
5241 output.push(input.charCodeAt(j));
5242 }
5243
5244 // Main decoding loop: start just after the last delimiter if any basic code
5245 // points were copied; start at the beginning otherwise.
5246
5247 for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
5248
5249 // `index` is the index of the next character to be consumed.
5250 // Decode a generalized variable-length integer into `delta`,
5251 // which gets added to `i`. The overflow checking is easier
5252 // if we increase `i` as we go, then subtract off its starting
5253 // value at the end to obtain `delta`.
5254 for (oldi = i, w = 1, k = base; /* no condition */; k += base) {
5255
5256 if (index >= inputLength) {
5257 error('invalid-input');
5258 }
5259
5260 digit = basicToDigit(input.charCodeAt(index++));
5261
5262 if (digit >= base || digit > floor((maxInt - i) / w)) {
5263 error('overflow');
5264 }
5265
5266 i += digit * w;
5267 t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
5268
5269 if (digit < t) {
5270 break;
5271 }
5272
5273 baseMinusT = base - t;
5274 if (w > floor(maxInt / baseMinusT)) {
5275 error('overflow');
5276 }
5277
5278 w *= baseMinusT;
5279
5280 }
5281
5282 out = output.length + 1;
5283 bias = adapt(i - oldi, out, oldi == 0);
5284
5285 // `i` was supposed to wrap around from `out` to `0`,
5286 // incrementing `n` each time, so we'll fix that now:
5287 if (floor(i / out) > maxInt - n) {
5288 error('overflow');
5289 }
5290
5291 n += floor(i / out);
5292 i %= out;
5293
5294 // Insert `n` at position `i` of the output
5295 output.splice(i++, 0, n);
5296
5297 }
5298
5299 return ucs2encode(output);
5300 }
5301
5302 /**
5303 * Converts a string of Unicode symbols (e.g. a domain name label) to a
5304 * Punycode string of ASCII-only symbols.
5305 * @memberOf punycode
5306 * @param {String} input The string of Unicode symbols.
5307 * @returns {String} The resulting Punycode string of ASCII-only symbols.
5308 */
5309 function encode(input) {
5310 var n,
5311 delta,
5312 handledCPCount,
5313 basicLength,
5314 bias,
5315 j,
5316 m,
5317 q,
5318 k,
5319 t,
5320 currentValue,
5321 output = [],
5322 /** `inputLength` will hold the number of code points in `input`. */
5323 inputLength,
5324 /** Cached calculation results */
5325 handledCPCountPlusOne,
5326 baseMinusT,
5327 qMinusT;
5328
5329 // Convert the input in UCS-2 to Unicode
5330 input = ucs2decode(input);
5331
5332 // Cache the length
5333 inputLength = input.length;
5334
5335 // Initialize the state
5336 n = initialN;
5337 delta = 0;
5338 bias = initialBias;
5339
5340 // Handle the basic code points
5341 for (j = 0; j < inputLength; ++j) {
5342 currentValue = input[j];
5343 if (currentValue < 0x80) {
5344 output.push(stringFromCharCode(currentValue));
5345 }
5346 }
5347
5348 handledCPCount = basicLength = output.length;
5349
5350 // `handledCPCount` is the number of code points that have been handled;
5351 // `basicLength` is the number of basic code points.
5352
5353 // Finish the basic string - if it is not empty - with a delimiter
5354 if (basicLength) {
5355 output.push(delimiter);
5356 }
5357
5358 // Main encoding loop:
5359 while (handledCPCount < inputLength) {
5360
5361 // All non-basic code points < n have been handled already. Find the next
5362 // larger one:
5363 for (m = maxInt, j = 0; j < inputLength; ++j) {
5364 currentValue = input[j];
5365 if (currentValue >= n && currentValue < m) {
5366 m = currentValue;
5367 }
5368 }
5369
5370 // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
5371 // but guard against overflow
5372 handledCPCountPlusOne = handledCPCount + 1;
5373 if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
5374 error('overflow');
5375 }
5376
5377 delta += (m - n) * handledCPCountPlusOne;
5378 n = m;
5379
5380 for (j = 0; j < inputLength; ++j) {
5381 currentValue = input[j];
5382
5383 if (currentValue < n && ++delta > maxInt) {
5384 error('overflow');
5385 }
5386
5387 if (currentValue == n) {
5388 // Represent delta as a generalized variable-length integer
5389 for (q = delta, k = base; /* no condition */; k += base) {
5390 t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
5391 if (q < t) {
5392 break;
5393 }
5394 qMinusT = q - t;
5395 baseMinusT = base - t;
5396 output.push(
5397 stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
5398 );
5399 q = floor(qMinusT / baseMinusT);
5400 }
5401
5402 output.push(stringFromCharCode(digitToBasic(q, 0)));
5403 bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
5404 delta = 0;
5405 ++handledCPCount;
5406 }
5407 }
5408
5409 ++delta;
5410 ++n;
5411
5412 }
5413 return output.join('');
5414 }
5415
5416 /**
5417 * Converts a Punycode string representing a domain name or an email address
5418 * to Unicode. Only the Punycoded parts of the input will be converted, i.e.
5419 * it doesn't matter if you call it on a string that has already been
5420 * converted to Unicode.
5421 * @memberOf punycode
5422 * @param {String} input The Punycoded domain name or email address to
5423 * convert to Unicode.
5424 * @returns {String} The Unicode representation of the given Punycode
5425 * string.
5426 */
5427 function toUnicode(input) {
5428 return mapDomain(input, function(string) {
5429 return regexPunycode.test(string)
5430 ? decode(string.slice(4).toLowerCase())
5431 : string;
5432 });
5433 }
5434
5435 /**
5436 * Converts a Unicode string representing a domain name or an email address to
5437 * Punycode. Only the non-ASCII parts of the domain name will be converted,
5438 * i.e. it doesn't matter if you call it with a domain that's already in
5439 * ASCII.
5440 * @memberOf punycode
5441 * @param {String} input The domain name or email address to convert, as a
5442 * Unicode string.
5443 * @returns {String} The Punycode representation of the given domain name or
5444 * email address.
5445 */
5446 function toASCII(input) {
5447 return mapDomain(input, function(string) {
5448 return regexNonASCII.test(string)
5449 ? 'xn--' + encode(string)
5450 : string;
5451 });
5452 }
5453
5454 /*--------------------------------------------------------------------------*/
5455
5456 /** Define the public API */
5457 punycode = {
5458 /**
5459 * A string representing the current Punycode.js version number.
5460 * @memberOf punycode
5461 * @type String
5462 */
5463 'version': '1.3.2',
5464 /**
5465 * An object of methods to convert from JavaScript's internal character
5466 * representation (UCS-2) to Unicode code points, and back.
5467 * @see <https://mathiasbynens.be/notes/javascript-encoding>
5468 * @memberOf punycode
5469 * @type Object
5470 */
5471 'ucs2': {
5472 'decode': ucs2decode,
5473 'encode': ucs2encode
5474 },
5475 'decode': decode,
5476 'encode': encode,
5477 'toASCII': toASCII,
5478 'toUnicode': toUnicode
5479 };
5480
5481 /** Expose `punycode` */
5482 // Some AMD build optimizers, like r.js, check for specific condition patterns
5483 // like the following:
5484 if (
5485 typeof define == 'function' &&
5486 typeof define.amd == 'object' &&
5487 define.amd
5488 ) {
5489 define('punycode', function() {
5490 return punycode;
5491 });
5492 } else if (freeExports && freeModule) {
5493 if (module.exports == freeExports) { // in Node.js or RingoJS v0.8.0+
5494 freeModule.exports = punycode;
5495 } else { // in Narwhal or RingoJS v0.7.0-
5496 for (key in punycode) {
5497 punycode.hasOwnProperty(key) && (freeExports[key] = punycode[key]);
5498 }
5499 }
5500 } else { // in Rhino or a web browser
5501 root.punycode = punycode;
5502 }
5503
5504}(this));
5505
5506}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
5507},{}],36:[function(require,module,exports){
5508// Copyright Joyent, Inc. and other Node contributors.
5509//
5510// Permission is hereby granted, free of charge, to any person obtaining a
5511// copy of this software and associated documentation files (the
5512// "Software"), to deal in the Software without restriction, including
5513// without limitation the rights to use, copy, modify, merge, publish,
5514// distribute, sublicense, and/or sell copies of the Software, and to permit
5515// persons to whom the Software is furnished to do so, subject to the
5516// following conditions:
5517//
5518// The above copyright notice and this permission notice shall be included
5519// in all copies or substantial portions of the Software.
5520//
5521// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
5522// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
5523// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
5524// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
5525// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
5526// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
5527// USE OR OTHER DEALINGS IN THE SOFTWARE.
5528
5529'use strict';
5530
5531// If obj.hasOwnProperty has been overridden, then calling
5532// obj.hasOwnProperty(prop) will break.
5533// See: https://github.com/joyent/node/issues/1707
5534function hasOwnProperty(obj, prop) {
5535 return Object.prototype.hasOwnProperty.call(obj, prop);
5536}
5537
5538module.exports = function(qs, sep, eq, options) {
5539 sep = sep || '&';
5540 eq = eq || '=';
5541 var obj = {};
5542
5543 if (typeof qs !== 'string' || qs.length === 0) {
5544 return obj;
5545 }
5546
5547 var regexp = /\+/g;
5548 qs = qs.split(sep);
5549
5550 var maxKeys = 1000;
5551 if (options && typeof options.maxKeys === 'number') {
5552 maxKeys = options.maxKeys;
5553 }
5554
5555 var len = qs.length;
5556 // maxKeys <= 0 means that we should not limit keys count
5557 if (maxKeys > 0 && len > maxKeys) {
5558 len = maxKeys;
5559 }
5560
5561 for (var i = 0; i < len; ++i) {
5562 var x = qs[i].replace(regexp, '%20'),
5563 idx = x.indexOf(eq),
5564 kstr, vstr, k, v;
5565
5566 if (idx >= 0) {
5567 kstr = x.substr(0, idx);
5568 vstr = x.substr(idx + 1);
5569 } else {
5570 kstr = x;
5571 vstr = '';
5572 }
5573
5574 k = decodeURIComponent(kstr);
5575 v = decodeURIComponent(vstr);
5576
5577 if (!hasOwnProperty(obj, k)) {
5578 obj[k] = v;
5579 } else if (isArray(obj[k])) {
5580 obj[k].push(v);
5581 } else {
5582 obj[k] = [obj[k], v];
5583 }
5584 }
5585
5586 return obj;
5587};
5588
5589var isArray = Array.isArray || function (xs) {
5590 return Object.prototype.toString.call(xs) === '[object Array]';
5591};
5592
5593},{}],37:[function(require,module,exports){
5594// Copyright Joyent, Inc. and other Node contributors.
5595//
5596// Permission is hereby granted, free of charge, to any person obtaining a
5597// copy of this software and associated documentation files (the
5598// "Software"), to deal in the Software without restriction, including
5599// without limitation the rights to use, copy, modify, merge, publish,
5600// distribute, sublicense, and/or sell copies of the Software, and to permit
5601// persons to whom the Software is furnished to do so, subject to the
5602// following conditions:
5603//
5604// The above copyright notice and this permission notice shall be included
5605// in all copies or substantial portions of the Software.
5606//
5607// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
5608// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
5609// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
5610// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
5611// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
5612// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
5613// USE OR OTHER DEALINGS IN THE SOFTWARE.
5614
5615'use strict';
5616
5617var stringifyPrimitive = function(v) {
5618 switch (typeof v) {
5619 case 'string':
5620 return v;
5621
5622 case 'boolean':
5623 return v ? 'true' : 'false';
5624
5625 case 'number':
5626 return isFinite(v) ? v : '';
5627
5628 default:
5629 return '';
5630 }
5631};
5632
5633module.exports = function(obj, sep, eq, name) {
5634 sep = sep || '&';
5635 eq = eq || '=';
5636 if (obj === null) {
5637 obj = undefined;
5638 }
5639
5640 if (typeof obj === 'object') {
5641 return map(objectKeys(obj), function(k) {
5642 var ks = encodeURIComponent(stringifyPrimitive(k)) + eq;
5643 if (isArray(obj[k])) {
5644 return map(obj[k], function(v) {
5645 return ks + encodeURIComponent(stringifyPrimitive(v));
5646 }).join(sep);
5647 } else {
5648 return ks + encodeURIComponent(stringifyPrimitive(obj[k]));
5649 }
5650 }).join(sep);
5651
5652 }
5653
5654 if (!name) return '';
5655 return encodeURIComponent(stringifyPrimitive(name)) + eq +
5656 encodeURIComponent(stringifyPrimitive(obj));
5657};
5658
5659var isArray = Array.isArray || function (xs) {
5660 return Object.prototype.toString.call(xs) === '[object Array]';
5661};
5662
5663function map (xs, f) {
5664 if (xs.map) return xs.map(f);
5665 var res = [];
5666 for (var i = 0; i < xs.length; i++) {
5667 res.push(f(xs[i], i));
5668 }
5669 return res;
5670}
5671
5672var objectKeys = Object.keys || function (obj) {
5673 var res = [];
5674 for (var key in obj) {
5675 if (Object.prototype.hasOwnProperty.call(obj, key)) res.push(key);
5676 }
5677 return res;
5678};
5679
5680},{}],38:[function(require,module,exports){
5681'use strict';
5682
5683exports.decode = exports.parse = require('./decode');
5684exports.encode = exports.stringify = require('./encode');
5685
5686},{"./decode":36,"./encode":37}],39:[function(require,module,exports){
5687/*! rasterizeHTML.js - v1.2.4 - 2016-10-30
5688* http://www.github.com/cburgmer/rasterizeHTML.js
5689* Copyright (c) 2016 Christoph Burgmer; Licensed MIT */
5690(function (root, factory) {
5691 if (typeof define === 'function' && define.amd) {
5692 // AMD. Register as an anonymous module unless amdModuleId is set
5693 define(["url","css-mediaquery","xmlserializer","sane-domparser-error","ayepromise","inlineresources"], function (a0,b1,c2,d3,e4,f5) {
5694 return (root['rasterizeHTML'] = factory(a0,b1,c2,d3,e4,f5));
5695 });
5696 } else if (typeof exports === 'object') {
5697 // Node. Does not work with strict CommonJS, but
5698 // only CommonJS-like environments that support module.exports,
5699 // like Node.
5700 module.exports = factory(require("url"),require("css-mediaquery"),require("xmlserializer"),require("sane-domparser-error"),require("ayepromise"),require("inlineresources"));
5701 } else {
5702 root['rasterizeHTML'] = factory(root["url"],root["cssMediaQuery"],root["xmlserializer"],root["sanedomparsererror"],root["ayepromise"],root["inlineresources"]);
5703 }
5704}(this, function (url, cssMediaQuery, xmlserializer, sanedomparsererror, ayepromise, inlineresources) {
5705
5706var util = (function (url) {
5707 "use strict";
5708
5709 var module = {};
5710
5711 var uniqueIdList = [];
5712
5713 module.joinUrl = function (baseUrl, relUrl) {
5714 if (!baseUrl) {
5715 return relUrl;
5716 }
5717 return url.resolve(baseUrl, relUrl);
5718 };
5719
5720 module.getConstantUniqueIdFor = function (element) {
5721 // HACK, using a list results in O(n), but how do we hash e.g. a DOM node?
5722 if (uniqueIdList.indexOf(element) < 0) {
5723 uniqueIdList.push(element);
5724 }
5725 return uniqueIdList.indexOf(element);
5726 };
5727
5728 module.clone = function (object) {
5729 var theClone = {},
5730 i;
5731 for (i in object) {
5732 if (object.hasOwnProperty(i)) {
5733 theClone[i] = object[i];
5734 }
5735 }
5736 return theClone;
5737 };
5738
5739 var isObject = function (obj) {
5740 return typeof obj === "object" && obj !== null;
5741 };
5742
5743 var isCanvas = function (obj) {
5744 return isObject(obj) &&
5745 Object.prototype.toString.apply(obj).match(/\[object (Canvas|HTMLCanvasElement)\]/i);
5746 };
5747
5748 // args: canvas, options
5749 module.parseOptionalParameters = function (args) {
5750 var parameters = {
5751 canvas: null,
5752 options: {}
5753 };
5754
5755 if (args[0] == null || isCanvas(args[0])) {
5756 parameters.canvas = args[0] || null;
5757
5758 parameters.options = module.clone(args[1]);
5759 } else {
5760 parameters.options = module.clone(args[0]);
5761 }
5762
5763 return parameters;
5764 };
5765
5766 return module;
5767}(url));
5768
5769// Proxy objects by monkey patching
5770var proxies = (function (util, ayepromise) {
5771 "use strict";
5772
5773 var module = {};
5774
5775 var monkeyPatchInstanceMethod = function (object, methodName, proxyFunc) {
5776 var originalFunc = object[methodName];
5777
5778 object[methodName] = function () {
5779 var args = Array.prototype.slice.call(arguments);
5780
5781 return proxyFunc.apply(this, [args, originalFunc]);
5782 };
5783
5784 return originalFunc;
5785 };
5786
5787 // Bases all XHR calls on the given base URL
5788 module.baseUrlRespectingXhr = function (XHRObject, baseUrl) {
5789 var xhrConstructor = function () {
5790 var xhr = new XHRObject();
5791
5792 monkeyPatchInstanceMethod(xhr, 'open', function (args, originalOpen) {
5793 var method = args.shift(),
5794 url = args.shift(),
5795 joinedUrl = util.joinUrl(baseUrl, url);
5796
5797 return originalOpen.apply(this, [method, joinedUrl].concat(args));
5798 });
5799
5800 return xhr;
5801 };
5802
5803 return xhrConstructor;
5804 };
5805
5806 // Provides a convenient way of being notified when all pending XHR calls are finished
5807 module.finishNotifyingXhr = function (XHRObject) {
5808 var totalXhrCount = 0,
5809 doneXhrCount = 0,
5810 waitingForPendingToClose = false,
5811 defer = ayepromise.defer();
5812
5813 var checkAllRequestsFinished = function () {
5814 var pendingXhrCount = totalXhrCount - doneXhrCount;
5815
5816 if (pendingXhrCount <= 0 && waitingForPendingToClose) {
5817 defer.resolve({totalCount: totalXhrCount});
5818 }
5819 };
5820
5821 var xhrConstructor = function () {
5822 var xhr = new XHRObject();
5823
5824 monkeyPatchInstanceMethod(xhr, 'send', function (_, originalSend) {
5825 totalXhrCount += 1;
5826 return originalSend.apply(this, arguments);
5827 });
5828
5829 xhr.addEventListener('load', function () {
5830 doneXhrCount += 1;
5831
5832 checkAllRequestsFinished();
5833 });
5834
5835 return xhr;
5836 };
5837
5838 xhrConstructor.waitForRequestsToFinish = function () {
5839 waitingForPendingToClose = true;
5840 checkAllRequestsFinished();
5841 return defer.promise;
5842 };
5843
5844 return xhrConstructor;
5845 };
5846
5847 return module;
5848}(util, ayepromise));
5849
5850var documentUtil = (function () {
5851 "use strict";
5852
5853 var module = {};
5854
5855 var asArray = function (arrayLike) {
5856 return Array.prototype.slice.call(arrayLike);
5857 };
5858
5859 module.addClassName = function (element, className) {
5860 element.className += ' ' + className;
5861 };
5862
5863 module.addClassNameRecursively = function (element, className) {
5864 module.addClassName(element, className);
5865
5866 if (element.parentNode !== element.ownerDocument) {
5867 module.addClassNameRecursively(element.parentNode, className);
5868 }
5869 };
5870
5871 var changeCssRule = function (rule, newRuleText) {
5872 var styleSheet = rule.parentStyleSheet,
5873 ruleIdx = asArray(styleSheet.cssRules).indexOf(rule);
5874
5875 // Exchange rule with the new text
5876 styleSheet.insertRule(newRuleText, ruleIdx+1);
5877 styleSheet.deleteRule(ruleIdx);
5878 };
5879
5880 var updateRuleSelector = function (rule, updatedSelector) {
5881 var styleDefinitions = rule.cssText.replace(/^[^\{]+/, ''),
5882 newRule = updatedSelector + ' ' + styleDefinitions;
5883
5884 changeCssRule(rule, newRule);
5885 };
5886
5887 var cssRulesToText = function (cssRules) {
5888 return asArray(cssRules).reduce(function (cssText, rule) {
5889 return cssText + rule.cssText;
5890 }, '');
5891 };
5892
5893 var rewriteStyleContent = function (styleElement) {
5894 styleElement.textContent = cssRulesToText(styleElement.sheet.cssRules);
5895 };
5896
5897 var addSheetPropertyToSvgStyleElement = function (svgStyleElement) {
5898 var doc = document.implementation.createHTMLDocument(''),
5899 cssStyleElement = document.createElement('style');
5900
5901 cssStyleElement.textContent = svgStyleElement.textContent;
5902 // the style will only be parsed once it is added to a document
5903 doc.body.appendChild(cssStyleElement);
5904
5905 svgStyleElement.sheet = cssStyleElement.sheet;
5906 };
5907
5908 var matchingSimpleSelectorsRegex = function (simpleSelectorList) {
5909 return '(' +
5910 '(?:^|[^.#:\\w])' + // start of string or not a simple selector character,
5911 '|' + // ... or ...
5912 '(?=\\W)' + // the next character parsed is not an alphabetic character (and thus a natural boundary)
5913 ')' +
5914 '(' +
5915 simpleSelectorList.join('|') + // one out of the given simple selectors
5916 ')' +
5917 '(?=\\W|$)'; // followed either by a non-alphabetic character or the end of the string
5918 };
5919
5920 var replaceSimpleSelectorsBy = function (element, simpleSelectorList, caseInsensitiveReplaceFunc) {
5921 var selectorRegex = matchingSimpleSelectorsRegex(simpleSelectorList);
5922
5923 asArray(element.querySelectorAll('style')).forEach(function (styleElement) {
5924 // SVGStyleElement doesn't have a property sheet in Safari, we need some workaround here
5925 // more details can be found here: https://github.com/cburgmer/rasterizeHTML.js/issues/158
5926 if (typeof styleElement.sheet === 'undefined') {
5927 addSheetPropertyToSvgStyleElement(styleElement);
5928 }
5929
5930 var matchingRules = asArray(styleElement.sheet.cssRules).filter(function (rule) {
5931 return rule.selectorText && new RegExp(selectorRegex, 'i').test(rule.selectorText);
5932 });
5933
5934 if (matchingRules.length) {
5935 matchingRules.forEach(function (rule) {
5936 var newSelector = rule.selectorText.replace(new RegExp(selectorRegex, 'gi'),
5937 function (_, prefixMatch, selectorMatch) {
5938 return prefixMatch + caseInsensitiveReplaceFunc(selectorMatch);
5939 });
5940
5941 if (newSelector !== rule.selectorText) {
5942 updateRuleSelector(rule, newSelector);
5943 }
5944 });
5945
5946 rewriteStyleContent(styleElement);
5947 }
5948 });
5949 };
5950
5951 module.rewriteCssSelectorWith = function (element, oldSelector, newSelector) {
5952 replaceSimpleSelectorsBy(element, [oldSelector], function () {
5953 return newSelector;
5954 });
5955 };
5956
5957 module.lowercaseCssTypeSelectors = function (element, matchingTagNames) {
5958 replaceSimpleSelectorsBy(element, matchingTagNames, function (match) {
5959 return match.toLowerCase();
5960 });
5961 };
5962
5963 module.findHtmlOnlyNodeNames = function (element) {
5964 var treeWalker = element.ownerDocument.createTreeWalker(element, NodeFilter.SHOW_ELEMENT),
5965 htmlNodeNames = {},
5966 nonHtmlNodeNames = {},
5967 currentTagName;
5968
5969 do {
5970 currentTagName = treeWalker.currentNode.tagName.toLowerCase();
5971 if (treeWalker.currentNode.namespaceURI === 'http://www.w3.org/1999/xhtml') {
5972 htmlNodeNames[currentTagName] = true;
5973 } else {
5974 nonHtmlNodeNames[currentTagName] = true;
5975 }
5976 } while(treeWalker.nextNode());
5977
5978 return Object.keys(htmlNodeNames).filter(function (tagName) {
5979 return !nonHtmlNodeNames[tagName];
5980 });
5981 };
5982
5983 return module;
5984}());
5985
5986var documentHelper = (function (documentUtil) {
5987 "use strict";
5988
5989 var module = {};
5990
5991 var asArray = function (arrayLike) {
5992 return Array.prototype.slice.call(arrayLike);
5993 };
5994
5995 var cascadingAction = {
5996 active: true,
5997 hover: true,
5998 focus: false,
5999 target: false
6000 };
6001
6002 module.fakeUserAction = function (element, selector, action) {
6003 var elem = element.querySelector(selector),
6004 pseudoClass = ':' + action,
6005 fakeActionClass = 'rasterizehtml' + action;
6006 if (! elem) {
6007 return;
6008 }
6009
6010 if (cascadingAction[action]) {
6011 documentUtil.addClassNameRecursively(elem, fakeActionClass);
6012 } else {
6013 documentUtil.addClassName(elem, fakeActionClass);
6014 }
6015 documentUtil.rewriteCssSelectorWith(element, pseudoClass, '.' + fakeActionClass);
6016 };
6017
6018 module.persistInputValues = function (doc) {
6019 var inputs = doc.querySelectorAll('input'),
6020 textareas = doc.querySelectorAll('textarea'),
6021 isCheckable = function (input) {
6022 return input.type === 'checkbox' || input.type === 'radio';
6023 };
6024
6025 asArray(inputs).filter(isCheckable)
6026 .forEach(function (input) {
6027 if (input.checked) {
6028 input.setAttribute('checked', '');
6029 } else {
6030 input.removeAttribute('checked');
6031 }
6032 });
6033
6034 asArray(inputs).filter(function (input) { return !isCheckable(input); })
6035 .forEach(function (input) {
6036 input.setAttribute('value', input.value);
6037 });
6038
6039 asArray(textareas)
6040 .forEach(function (textarea) {
6041 textarea.textContent = textarea.value;
6042 });
6043 };
6044
6045 module.rewriteTagNameSelectorsToLowerCase = function (element) {
6046 documentUtil.lowercaseCssTypeSelectors(element, documentUtil.findHtmlOnlyNodeNames(element));
6047 };
6048
6049 return module;
6050}(documentUtil));
6051
6052var mediaQueryHelper = (function (cssMediaQuery) {
6053 "use strict";
6054
6055 var module = {};
6056
6057 var svgImgBlueByEmMediaQuery = function () {
6058 var svg = '<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="10" height="10">' +
6059 '<style>@media (max-width: 1em) { svg { background: #00f; } }</style>' +
6060 '</svg>';
6061
6062 var url = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg),
6063 img = document.createElement('img');
6064
6065 img.src = url;
6066
6067 return img;
6068 };
6069
6070 var firstPixelHasColor = function (img, r, g, b) {
6071 var canvas = document.createElement("canvas");
6072 canvas.width = img.width;
6073 canvas.height = img.height;
6074
6075 var context = canvas.getContext("2d"),
6076 data;
6077
6078 context.drawImage(img, 0, 0);
6079 data = context.getImageData(0, 0, 1, 1).data;
6080 return data[0] === r && data[1] === g && data[2] === b;
6081 };
6082
6083 var hasEmMediaQueryIssue = function () {
6084 var img = svgImgBlueByEmMediaQuery(),
6085 defer = ayepromise.defer();
6086
6087 document.querySelector('body').appendChild(img);
6088
6089 img.onload = function () {
6090 document.querySelector('body').removeChild(img);
6091 try {
6092 defer.resolve(!firstPixelHasColor(img, 0, 0, 255));
6093 } catch (e) {
6094 // Fails in PhantomJS, let's assume the issue exists
6095 defer.resolve(true);
6096 }
6097 };
6098 img.onerror = function () {
6099 defer.reject();
6100 };
6101
6102 return defer.promise;
6103 };
6104
6105 var hasEmIssue;
6106
6107 module.needsEmWorkaround = function () {
6108 if (hasEmIssue === undefined) {
6109 hasEmIssue = hasEmMediaQueryIssue();
6110 }
6111 return hasEmIssue;
6112 };
6113
6114 var asArray = function (arrayLike) {
6115 return Array.prototype.slice.call(arrayLike);
6116 };
6117
6118 var cssRulesToText = function (cssRules) {
6119 return asArray(cssRules).map(function (rule) {
6120 return rule.cssText;
6121 }).join('\n');
6122 };
6123
6124 var mediaQueryRule = function (mediaQuery, cssRules) {
6125 return '@media ' + mediaQuery + '{' +
6126 cssRulesToText(cssRules) +
6127 '}';
6128 };
6129
6130 var exchangeRuleWithNewContent = function (styleSheet, ruleIdx, newRuleText) {
6131 try {
6132 styleSheet.insertRule(newRuleText, ruleIdx+1);
6133 } catch (e) {
6134 // In case the browser does not like our new rule we just keep the existing one and quietly leave
6135 return;
6136 }
6137 styleSheet.deleteRule(ruleIdx);
6138 };
6139
6140 var changeCssRule = function (rule, newRuleText) {
6141 var styleSheet = rule.parentStyleSheet,
6142 ruleIdx = asArray(styleSheet.cssRules).indexOf(rule);
6143
6144 exchangeRuleWithNewContent(styleSheet, ruleIdx, newRuleText);
6145 };
6146
6147 var rewriteStyleContent = function (styleElement) {
6148 styleElement.textContent = cssRulesToText(styleElement.sheet.cssRules);
6149 };
6150
6151 var serializeExpression = function (exp) {
6152 var feature = exp.modifier ? exp.modifier + '-' + exp.feature : exp.feature;
6153 if (exp.value) {
6154 return '(' + feature + ': ' + exp.value + ')';
6155 } else {
6156 return '(' + feature + ')';
6157 }
6158 };
6159
6160 var serializeQueryPart = function (q) {
6161 var segments = [];
6162
6163 if (q.inverse) {
6164 segments.push("not");
6165 }
6166
6167 segments.push(q.type);
6168
6169 if (q.expressions.length > 0) {
6170 segments.push('and ' + q.expressions.map(serializeExpression).join(' and '));
6171 }
6172
6173 return segments.join(' ');
6174 };
6175
6176 // poor man's testability
6177 module.serializeQuery = function (q) {
6178 var queryParts = q.map(serializeQueryPart);
6179 return queryParts.join(', ');
6180 };
6181
6182 var transformEmIntoPx = function (em) {
6183 return em * 16;
6184 };
6185
6186 var replaceEmValueWithPx = function (value) {
6187 // Match a number with em unit. Doesn't match all, but should be enough for now
6188 var match = /^((?:\d+\.)?\d+)em/.exec(value);
6189 if (match) {
6190 return transformEmIntoPx(parseFloat(match[1])) + 'px';
6191 }
6192 return value;
6193 };
6194
6195 var substituteEmWithPx = function (mediaQuery) {
6196 var parsedQuery = cssMediaQuery.parse(mediaQuery),
6197 hasChanges = false;
6198
6199 parsedQuery.forEach(function (q) {
6200 q.expressions.forEach(function (exp) {
6201 var rewrittenValue = replaceEmValueWithPx(exp.value);
6202
6203 hasChanges |= rewrittenValue !== exp.value;
6204 exp.value = rewrittenValue;
6205 });
6206 });
6207
6208 if (hasChanges) {
6209 return module.serializeQuery(parsedQuery);
6210 }
6211 };
6212
6213 var replaceEmsWithPx = function (mediaQueryRules) {
6214 var anyRuleHasChanges = false;
6215
6216 mediaQueryRules.forEach(function (rule) {
6217 var rewrittenMediaQuery = substituteEmWithPx(rule.media.mediaText);
6218
6219 if (rewrittenMediaQuery) {
6220 changeCssRule(rule, mediaQueryRule(rewrittenMediaQuery, rule.cssRules));
6221 }
6222
6223 anyRuleHasChanges |= !!rewrittenMediaQuery;
6224 });
6225
6226 return anyRuleHasChanges;
6227 };
6228
6229 module.workAroundWebKitEmSizeIssue = function (element) {
6230 var styles = element.querySelectorAll('style');
6231
6232 asArray(styles).forEach(function (style) {
6233 var mediaQueryRules = asArray(style.sheet.cssRules).filter(function (rule) {
6234 return rule.type === window.CSSRule.MEDIA_RULE;
6235 });
6236
6237 var hasChanges = replaceEmsWithPx(mediaQueryRules);
6238 if (hasChanges) {
6239 rewriteStyleContent(style);
6240 }
6241 });
6242 };
6243
6244 return module;
6245}(cssMediaQuery));
6246
6247var browser = (function (util, proxies, ayepromise, sanedomparsererror, theWindow) {
6248 "use strict";
6249
6250 var module = {};
6251
6252 var createHiddenElement = function (doc, tagName, width, height) {
6253 var element = doc.createElement(tagName);
6254 // 'display: none' doesn't cut it, as browsers seem to be lazy loading CSS
6255 element.style.visibility = "hidden";
6256 element.style.width = width + "px";
6257 element.style.height = height + "px";
6258 element.style.position = "absolute";
6259 element.style.top = (-10000 - height) + "px";
6260 element.style.left = (-10000 - width) + "px";
6261 // We need to add the element to the document so that its content gets loaded
6262 doc.getElementsByTagName("body")[0].appendChild(element);
6263 return element;
6264 };
6265
6266 module.executeJavascript = function (element, options) {
6267 var iframe = createHiddenElement(theWindow.document, "iframe", options.width, options.height),
6268 html = element.outerHTML,
6269 iframeErrorsMessages = [],
6270 defer = ayepromise.defer(),
6271 timeout = options.executeJsTimeout || 0;
6272
6273 var doResolve = function () {
6274 var doc = iframe.contentDocument;
6275 theWindow.document.getElementsByTagName("body")[0].removeChild(iframe);
6276 defer.resolve({
6277 document: doc,
6278 errors: iframeErrorsMessages
6279 });
6280 };
6281
6282 var waitForJavaScriptToRun = function () {
6283 var d = ayepromise.defer();
6284 if (timeout > 0) {
6285 setTimeout(d.resolve, timeout);
6286 } else {
6287 d.resolve();
6288 }
6289 return d.promise;
6290 };
6291
6292 var xhr = iframe.contentWindow.XMLHttpRequest,
6293 finishNotifyXhrProxy = proxies.finishNotifyingXhr(xhr),
6294 baseUrlXhrProxy = proxies.baseUrlRespectingXhr(finishNotifyXhrProxy, options.baseUrl);
6295
6296 iframe.onload = function () {
6297 waitForJavaScriptToRun()
6298 .then(finishNotifyXhrProxy.waitForRequestsToFinish)
6299 .then(doResolve);
6300 };
6301
6302 iframe.contentDocument.open();
6303 iframe.contentWindow.XMLHttpRequest = baseUrlXhrProxy;
6304 iframe.contentWindow.onerror = function (msg) {
6305 iframeErrorsMessages.push({
6306 resourceType: "scriptExecution",
6307 msg: msg
6308 });
6309 };
6310
6311 iframe.contentDocument.write('<!DOCTYPE html>');
6312 iframe.contentDocument.write(html);
6313 iframe.contentDocument.close();
6314
6315 return defer.promise;
6316 };
6317
6318 var createHiddenSandboxedIFrame = function (doc, width, height) {
6319 var iframe = doc.createElement('iframe');
6320 iframe.style.width = width + "px";
6321 iframe.style.height = height + "px";
6322 // 'display: none' doesn't cut it, as browsers seem to be lazy loading content
6323 iframe.style.visibility = "hidden";
6324 iframe.style.position = "absolute";
6325 iframe.style.top = (-10000 - height) + "px";
6326 iframe.style.left = (-10000 - width) + "px";
6327 // Don't execute JS, all we need from sandboxing is access to the iframe's document
6328 iframe.sandbox = 'allow-same-origin';
6329 // Don't include a scrollbar on Linux
6330 iframe.scrolling = 'no';
6331 return iframe;
6332 };
6333
6334 var createIframeWithSizeAtZoomLevel1 = function (width, height, zoom) {
6335 var scaledViewportWidth = Math.floor(width / zoom),
6336 scaledViewportHeight = Math.floor(height / zoom);
6337
6338 return createHiddenSandboxedIFrame(theWindow.document, scaledViewportWidth, scaledViewportHeight);
6339 };
6340
6341 var calculateZoomedContentSizeAndRoundUp = function (actualViewport, requestedWidth, requestedHeight, zoom) {
6342 return {
6343 width: Math.max(actualViewport.width * zoom, requestedWidth),
6344 height: Math.max(actualViewport.height * zoom, requestedHeight)
6345 };
6346 };
6347
6348 var selectElementOrDescendant = function (element, selector) {
6349 var descendant = element.querySelector(selector);
6350 if (descendant) {
6351 return descendant;
6352 } else if (element.ownerDocument.querySelector(selector) === element) {
6353 return element;
6354 }
6355
6356 throw {
6357 message: "Clipping selector not found"
6358 };
6359 };
6360
6361 var calculateContentSize = function (rootElement, selector, requestedWidth, requestedHeight, zoom) {
6362 // clientWidth/clientHeight needed for PhantomJS
6363 var actualViewportWidth = Math.max(rootElement.scrollWidth, rootElement.clientWidth),
6364 actualViewportHeight = Math.max(rootElement.scrollHeight, rootElement.clientHeight),
6365 top, left, originalWidth, originalHeight, rootFontSize,
6366 element, rect, contentSize;
6367
6368 if (selector) {
6369 element = selectElementOrDescendant(rootElement, selector);
6370
6371 rect = element.getBoundingClientRect();
6372
6373 top = rect.top;
6374 left = rect.left;
6375 originalWidth = rect.width;
6376 originalHeight = rect.height;
6377 } else {
6378 top = 0;
6379 left = 0;
6380 originalWidth = actualViewportWidth;
6381 originalHeight = actualViewportHeight;
6382 }
6383
6384 contentSize = calculateZoomedContentSizeAndRoundUp({
6385 width: originalWidth,
6386 height: originalHeight
6387 },
6388 requestedWidth,
6389 requestedHeight,
6390 zoom);
6391
6392 rootFontSize = theWindow.getComputedStyle(rootElement.ownerDocument.documentElement).fontSize;
6393
6394 return {
6395 left: left,
6396 top: top,
6397 width: contentSize.width,
6398 height: contentSize.height,
6399 viewportWidth: actualViewportWidth,
6400 viewportHeight: actualViewportHeight,
6401
6402 rootFontSize: rootFontSize
6403 };
6404 };
6405
6406 var findCorrelatingElement = function (element, documentClone) {
6407 var tagName = element.tagName;
6408 // Stupid but simple method: find first match. Should work for a single HTML element, and any other element given as root
6409 return documentClone.querySelector(tagName);
6410 };
6411
6412 var elementToFullHtmlDocument = function (element) {
6413 var tagName = element.tagName.toLowerCase();
6414 if (tagName === 'html' || tagName === 'body') {
6415 return element.outerHTML;
6416 }
6417
6418 // Simple hack: hide the body from sizing, otherwise browser would apply a 8px margin
6419 return '<body style="margin: 0;">' + element.outerHTML + '</body>';
6420 };
6421
6422 module.calculateDocumentContentSize = function (element, options) {
6423 var defer = ayepromise.defer(),
6424 zoom = options.zoom || 1,
6425 iframe;
6426
6427
6428 iframe = createIframeWithSizeAtZoomLevel1(options.width, options.height, zoom);
6429 // We need to add the element to the document so that its content gets loaded
6430 theWindow.document.getElementsByTagName("body")[0].appendChild(iframe);
6431
6432 iframe.onload = function () {
6433 var doc = iframe.contentDocument,
6434 size;
6435
6436 try {
6437 size = calculateContentSize(findCorrelatingElement(element, doc), options.clip, options.width, options.height, zoom);
6438
6439 defer.resolve(size);
6440 } catch (e) {
6441 defer.reject(e);
6442 } finally {
6443 theWindow.document.getElementsByTagName("body")[0].removeChild(iframe);
6444 }
6445 };
6446
6447 // srcdoc doesn't work in PhantomJS yet
6448 iframe.contentDocument.open();
6449 iframe.contentDocument.write('<!DOCTYPE html>');
6450 iframe.contentDocument.write(elementToFullHtmlDocument(element));
6451 iframe.contentDocument.close();
6452
6453 return defer.promise;
6454 };
6455
6456 module.parseHtmlFragment = function (htmlFragment) {
6457 var doc = theWindow.document.implementation.createHTMLDocument('');
6458 doc.documentElement.innerHTML = htmlFragment;
6459
6460 var element = doc.querySelector('body').firstChild;
6461
6462 if (!element) {
6463 throw "Invalid source";
6464 }
6465
6466 return element;
6467 };
6468
6469 var addHTMLTagAttributes = function (doc, html) {
6470 var attributeMatch = /<html((?:\s+[^>]*)?)>/im.exec(html),
6471 helperDoc = theWindow.document.implementation.createHTMLDocument(''),
6472 htmlTagSubstitute,
6473 i, elementSubstitute, attribute;
6474
6475 if (!attributeMatch) {
6476 return;
6477 }
6478
6479 htmlTagSubstitute = '<div' + attributeMatch[1] + '></div>';
6480 helperDoc.documentElement.innerHTML = htmlTagSubstitute;
6481 elementSubstitute = helperDoc.querySelector('div');
6482
6483 for (i = 0; i < elementSubstitute.attributes.length; i++) {
6484 attribute = elementSubstitute.attributes[i];
6485 doc.documentElement.setAttribute(attribute.name, attribute.value);
6486 }
6487 };
6488
6489 module.parseHTML = function (html) {
6490 // We should be using the DOMParser, but it is not supported in older browsers
6491 var doc = theWindow.document.implementation.createHTMLDocument('');
6492 doc.documentElement.innerHTML = html;
6493
6494 addHTMLTagAttributes(doc, html);
6495 return doc;
6496 };
6497
6498 var failOnInvalidSource = function (doc) {
6499 try {
6500 return sanedomparsererror.failOnParseError(doc);
6501 } catch (e) {
6502 throw {
6503 message: "Invalid source",
6504 originalError: e
6505 };
6506 }
6507 };
6508
6509 module.validateXHTML = function (xhtml) {
6510 var p = new DOMParser(),
6511 doc = p.parseFromString(xhtml, "application/xml");
6512
6513 failOnInvalidSource(doc);
6514 };
6515
6516 var lastCacheDate = null;
6517
6518 var getUncachableURL = function (url, cache) {
6519 if (cache === 'none' || cache === 'repeated') {
6520 if (lastCacheDate === null || cache !== 'repeated') {
6521 lastCacheDate = Date.now();
6522 }
6523 return url + "?_=" + lastCacheDate;
6524 } else {
6525 return url;
6526 }
6527 };
6528
6529 var doDocumentLoad = function (url, options) {
6530 var xhr = new window.XMLHttpRequest(),
6531 joinedUrl = util.joinUrl(options.baseUrl, url),
6532 augmentedUrl = getUncachableURL(joinedUrl, options.cache),
6533 defer = ayepromise.defer(),
6534 doReject = function (e) {
6535 defer.reject({
6536 message: "Unable to load page",
6537 originalError: e
6538 });
6539 };
6540
6541 xhr.addEventListener("load", function () {
6542 if (xhr.status === 200 || xhr.status === 0) {
6543 defer.resolve(xhr.responseXML);
6544 } else {
6545 doReject(xhr.statusText);
6546 }
6547 }, false);
6548
6549 xhr.addEventListener("error", function (e) {
6550 doReject(e);
6551 }, false);
6552
6553 try {
6554 xhr.open('GET', augmentedUrl, true);
6555 xhr.responseType = "document";
6556 xhr.send(null);
6557 } catch (e) {
6558 doReject(e);
6559 }
6560
6561 return defer.promise;
6562 };
6563
6564 module.loadDocument = function (url, options) {
6565 return doDocumentLoad(url, options)
6566 .then(function (doc) {
6567 return failOnInvalidSource(doc);
6568 });
6569 };
6570
6571 return module;
6572}(util, proxies, ayepromise, sanedomparsererror, window));
6573
6574var svg2image = (function (ayepromise, window) {
6575 "use strict";
6576
6577 var module = {};
6578
6579 var urlForSvg = function (svg, useBlobs) {
6580 if (useBlobs) {
6581 return URL.createObjectURL(new Blob([svg], {"type": "image/svg+xml"}));
6582 } else {
6583 return "data:image/svg+xml;charset=utf-8," + encodeURIComponent(svg);
6584 }
6585 };
6586
6587 var cleanUpUrl = function (url) {
6588 if (url instanceof Blob) {
6589 URL.revokeObjectURL(url);
6590 }
6591 };
6592
6593 var simpleForeignObjectSvg = '<svg xmlns="http://www.w3.org/2000/svg" width="1" height="1"><foreignObject></foreignObject></svg>';
6594
6595 var supportsReadingObjectFromCanvas = function (url) {
6596 var canvas = document.createElement("canvas"),
6597 image = new Image(),
6598 defer = ayepromise.defer();
6599
6600 image.onload = function () {
6601 var context = canvas.getContext("2d");
6602 try {
6603 context.drawImage(image, 0, 0);
6604 // This will fail in Chrome & Safari
6605 canvas.toDataURL("image/png");
6606 defer.resolve(true);
6607 } catch (e) {
6608 defer.resolve(false);
6609 }
6610 };
6611 image.onerror = defer.reject;
6612 image.src = url;
6613
6614 return defer.promise;
6615 };
6616
6617 var readingBackFromCanvasBenefitsFromOldSchoolDataUris = function () {
6618 // Check for work around for https://code.google.com/p/chromium/issues/detail?id=294129
6619 var blobUrl = urlForSvg(simpleForeignObjectSvg, true);
6620 return supportsReadingObjectFromCanvas(blobUrl)
6621 .then(function (supportsReadingFromBlobs) {
6622 cleanUpUrl(blobUrl);
6623 if (supportsReadingFromBlobs) {
6624 return false;
6625 }
6626 return supportsReadingObjectFromCanvas(urlForSvg(simpleForeignObjectSvg, false))
6627 .then(function (s) {
6628 return s;
6629 });
6630 }, function () {
6631 return false;
6632 });
6633 };
6634
6635 var supportsBlobBuilding = function () {
6636 if (window.Blob) {
6637 // Available as constructor only in newer builds for all browsers
6638 try {
6639 new Blob(['<b></b>'], { "type" : "text/xml" });
6640 return true;
6641 } catch (err) {}
6642 }
6643 return false;
6644 };
6645
6646 var checkBlobSupport = function () {
6647 var defer = ayepromise.defer();
6648
6649 if (supportsBlobBuilding && window.URL) {
6650 readingBackFromCanvasBenefitsFromOldSchoolDataUris()
6651 .then(function (doesBenefit) {
6652 defer.resolve(! doesBenefit);
6653 }, function () {
6654 defer.reject();
6655 });
6656 } else {
6657 defer.resolve(false);
6658 }
6659
6660 return defer.promise;
6661 };
6662
6663 var checkForBlobsResult;
6664
6665 var checkForBlobs = function () {
6666 if (checkForBlobsResult === undefined) {
6667 checkForBlobsResult = checkBlobSupport();
6668 }
6669
6670 return checkForBlobsResult;
6671 };
6672
6673 var buildImageUrl = function (svg) {
6674 return checkForBlobs().then(function (useBlobs) {
6675 return urlForSvg(svg, useBlobs);
6676 });
6677 };
6678
6679 module.renderSvg = function (svg) {
6680 var url, image,
6681 defer = ayepromise.defer(),
6682 resetEventHandlers = function () {
6683 image.onload = null;
6684 image.onerror = null;
6685 },
6686 cleanUp = function () {
6687 if (url) {
6688 cleanUpUrl(url);
6689 }
6690 };
6691
6692 image = new Image();
6693 image.onload = function() {
6694 resetEventHandlers();
6695 cleanUp();
6696
6697 defer.resolve(image);
6698 };
6699 image.onerror = function () {
6700 cleanUp();
6701
6702 // Webkit calls the onerror handler if the SVG is faulty
6703 defer.reject();
6704 };
6705
6706 buildImageUrl(svg).then(function (imageUrl) {
6707 url = imageUrl;
6708 image.src = url;
6709 }, defer.reject);
6710
6711 return defer.promise;
6712 };
6713
6714 return module;
6715}(ayepromise, window));
6716
6717var document2svg = (function (util, browser, documentHelper, mediaQueryHelper, xmlserializer) {
6718 "use strict";
6719
6720 var module = {};
6721
6722 var svgAttributes = function (size, zoom) {
6723 var zoomFactor = zoom || 1;
6724
6725 var attributes = {
6726 width: size.width,
6727 height: size.height,
6728 'font-size': size.rootFontSize
6729 };
6730
6731 if (zoomFactor !== 1) {
6732 attributes.style = 'transform:scale(' + zoomFactor + '); transform-origin: 0 0;';
6733 }
6734
6735 return attributes;
6736 };
6737
6738 var foreignObjectAttributes = function (size) {
6739 var closestScaledWith, closestScaledHeight,
6740 offsetX, offsetY;
6741
6742 closestScaledWith = Math.round(size.viewportWidth);
6743 closestScaledHeight = Math.round(size.viewportHeight);
6744
6745 offsetX = -size.left;
6746 offsetY = -size.top;
6747
6748 var attributes = {
6749 'x': offsetX,
6750 'y': offsetY,
6751 'width': closestScaledWith,
6752 'height': closestScaledHeight
6753 };
6754
6755 return attributes;
6756 };
6757
6758 var workAroundCollapsingMarginsAcrossSVGElementInWebKitLike = function (attributes) {
6759 var style = attributes.style || '';
6760 attributes.style = style + 'float: left;';
6761 };
6762
6763 var workAroundSafariSometimesNotShowingExternalResources = function (attributes) {
6764 /* Let's hope that works some magic. The spec says SVGLoad only fires
6765 * now when all externals are available.
6766 * http://www.w3.org/TR/SVG/struct.html#ExternalResourcesRequired */
6767 attributes.externalResourcesRequired = true;
6768 };
6769
6770 var workAroundChromeShowingScrollbarsUnderLinuxIfHtmlIsOverflowScroll = function () {
6771 return '<style scoped="">html::-webkit-scrollbar { display: none; }</style>';
6772 };
6773
6774 var serializeAttributes = function (attributes) {
6775 var keys = Object.keys(attributes);
6776 if (!keys.length) {
6777 return '';
6778 }
6779
6780 return ' ' + keys.map(function (key) {
6781 return key + '="' + attributes[key] + '"';
6782 }).join(' ');
6783 };
6784
6785 var convertElementToSvg = function (element, size, zoomFactor) {
6786 var xhtml = xmlserializer.serializeToString(element);
6787
6788 browser.validateXHTML(xhtml);
6789
6790 var foreignObjectAttrs = foreignObjectAttributes(size);
6791 workAroundCollapsingMarginsAcrossSVGElementInWebKitLike(foreignObjectAttrs);
6792 workAroundSafariSometimesNotShowingExternalResources(foreignObjectAttrs);
6793
6794 return (
6795 '<svg xmlns="http://www.w3.org/2000/svg"' +
6796 serializeAttributes(svgAttributes(size, zoomFactor)) +
6797 '>' +
6798 workAroundChromeShowingScrollbarsUnderLinuxIfHtmlIsOverflowScroll() +
6799 '<foreignObject' + serializeAttributes(foreignObjectAttrs) + '>' +
6800 xhtml +
6801 '</foreignObject>' +
6802 '</svg>'
6803 );
6804 };
6805
6806 module.getSvgForDocument = function (element, size, zoomFactor) {
6807 documentHelper.rewriteTagNameSelectorsToLowerCase(element);
6808
6809 return mediaQueryHelper.needsEmWorkaround().then(function (needsWorkaround) {
6810 if (needsWorkaround) {
6811 mediaQueryHelper.workAroundWebKitEmSizeIssue(element);
6812 }
6813
6814 return convertElementToSvg(element, size, zoomFactor);
6815 });
6816 };
6817
6818 module.drawDocumentAsSvg = function (element, options) {
6819 ['hover', 'active', 'focus', 'target'].forEach(function (action) {
6820 if (options[action]) {
6821 documentHelper.fakeUserAction(element, options[action], action);
6822 }
6823 });
6824
6825 return browser.calculateDocumentContentSize(element, options)
6826 .then(function (size) {
6827 return module.getSvgForDocument(element, size, options.zoom);
6828 });
6829 };
6830
6831 return module;
6832}(util, browser, documentHelper, mediaQueryHelper, xmlserializer));
6833
6834var rasterize = (function (util, browser, documentHelper, document2svg, svg2image, inlineresources) {
6835 "use strict";
6836
6837 var module = {};
6838
6839 var generalDrawError = function (e) {
6840 return {
6841 message: "Error rendering page",
6842 originalError: e
6843 };
6844 };
6845
6846 var drawSvgAsImg = function (svg) {
6847 return svg2image.renderSvg(svg)
6848 .then(function (image) {
6849 return {
6850 image: image,
6851 svg: svg
6852 };
6853 }, function (e) {
6854 throw generalDrawError(e);
6855 });
6856 };
6857
6858 var drawImageOnCanvas = function (image, canvas) {
6859 try {
6860 canvas.getContext("2d").drawImage(image, 0, 0);
6861 } catch (e) {
6862 // Firefox throws a 'NS_ERROR_NOT_AVAILABLE' if the SVG is faulty
6863 throw generalDrawError(e);
6864 }
6865 };
6866
6867 var doDraw = function (element, canvas, options) {
6868 return document2svg.drawDocumentAsSvg(element, options)
6869 .then(drawSvgAsImg)
6870 .then(function (result) {
6871 if (canvas) {
6872 drawImageOnCanvas(result.image, canvas);
6873 }
6874
6875 return result;
6876 });
6877 };
6878
6879 var operateJavaScriptOnDocument = function (element, options) {
6880 return browser.executeJavascript(element, options)
6881 .then(function (result) {
6882 var document = result.document;
6883 documentHelper.persistInputValues(document);
6884
6885 return {
6886 document: document,
6887 errors: result.errors
6888 };
6889 });
6890 };
6891
6892 module.rasterize = function (element, canvas, options) {
6893 var inlineOptions;
6894
6895 inlineOptions = util.clone(options);
6896 inlineOptions.inlineScripts = options.executeJs === true;
6897
6898 return inlineresources.inlineReferences(element, inlineOptions)
6899 .then(function (errors) {
6900 if (options.executeJs) {
6901 return operateJavaScriptOnDocument(element, options)
6902 .then(function (result) {
6903 return {
6904 element: result.document.documentElement,
6905 errors: errors.concat(result.errors)
6906 };
6907 });
6908 } else {
6909 return {
6910 element: element,
6911 errors: errors
6912 };
6913 }
6914 }).then(function (result) {
6915 return doDraw(result.element, canvas, options)
6916 .then(function (drawResult) {
6917 return {
6918 image: drawResult.image,
6919 svg: drawResult.svg,
6920 errors: result.errors
6921 };
6922 });
6923 });
6924 };
6925
6926 return module;
6927}(util, browser, documentHelper, document2svg, svg2image, inlineresources));
6928
6929var rasterizeHTML = (function (util, browser, rasterize) {
6930 "use strict";
6931
6932 var module = {};
6933
6934 var getViewportSize = function (canvas, options) {
6935 var defaultWidth = 300,
6936 defaultHeight = 200,
6937 fallbackWidth = canvas ? canvas.width : defaultWidth,
6938 fallbackHeight = canvas ? canvas.height : defaultHeight,
6939 width = options.width !== undefined ? options.width : fallbackWidth,
6940 height = options.height !== undefined ? options.height : fallbackHeight;
6941
6942 return {
6943 width: width,
6944 height: height
6945 };
6946 };
6947
6948 var constructOptions = function (params) {
6949 var viewport = getViewportSize(params.canvas, params.options),
6950 options;
6951
6952 options = util.clone(params.options);
6953 options.width = viewport.width;
6954 options.height = viewport.height;
6955
6956 return options;
6957 };
6958
6959 /**
6960 * Draws a Document to the canvas.
6961 * rasterizeHTML.drawDocument( document [, canvas] [, options] ).then(function (result) { ... });
6962 */
6963 module.drawDocument = function () {
6964 var doc = arguments[0],
6965 optionalArguments = Array.prototype.slice.call(arguments, 1),
6966 params = util.parseOptionalParameters(optionalArguments);
6967
6968 var element = doc.documentElement ? doc.documentElement : doc;
6969
6970 return rasterize.rasterize(element, params.canvas, constructOptions(params));
6971 };
6972
6973 var drawHTML = function (html, canvas, options) {
6974 var doc = browser.parseHTML(html);
6975
6976 return module.drawDocument(doc, canvas, options);
6977 };
6978
6979 /**
6980 * Draws a HTML string to the canvas.
6981 * rasterizeHTML.drawHTML( html [, canvas] [, options] ).then(function (result) { ... });
6982 */
6983 module.drawHTML = function () {
6984 var html = arguments[0],
6985 optionalArguments = Array.prototype.slice.call(arguments, 1),
6986 params = util.parseOptionalParameters(optionalArguments);
6987
6988 return drawHTML(html, params.canvas, params.options);
6989 };
6990
6991 // work around https://bugzilla.mozilla.org/show_bug.cgi?id=925493
6992 var workAroundFirefoxNotLoadingStylesheetStyles = function (doc, url, options) {
6993 var d = document.implementation.createHTMLDocument('');
6994 d.replaceChild(doc.documentElement, d.documentElement);
6995
6996 var extendedOptions = options ? util.clone(options) : {};
6997
6998 if (!options.baseUrl) {
6999 extendedOptions.baseUrl = url;
7000 }
7001
7002 return {
7003 document: d,
7004 options: extendedOptions
7005 };
7006 };
7007
7008 var drawURL = function (url, canvas, options) {
7009 return browser.loadDocument(url, options)
7010 .then(function (doc) {
7011 var workaround = workAroundFirefoxNotLoadingStylesheetStyles(doc, url, options);
7012 return module.drawDocument(workaround.document, canvas, workaround.options);
7013 });
7014 };
7015
7016 /**
7017 * Draws a page to the canvas.
7018 * rasterizeHTML.drawURL( url [, canvas] [, options] ).then(function (result) { ... });
7019 */
7020 module.drawURL = function () {
7021 var url = arguments[0],
7022 optionalArguments = Array.prototype.slice.call(arguments, 1),
7023 params = util.parseOptionalParameters(optionalArguments);
7024
7025 return drawURL(url, params.canvas, params.options);
7026 };
7027
7028 return module;
7029}(util, browser, rasterize));
7030
7031return rasterizeHTML;
7032
7033}));
7034
7035},{"ayepromise":2,"css-mediaquery":8,"inlineresources":30,"sane-domparser-error":40,"url":3,"xmlserializer":41}],40:[function(require,module,exports){
7036'use strict';
7037
7038var innerXML = function (node) {
7039 var s = new XMLSerializer();
7040 return Array.prototype.map.call(node.childNodes, function (node) {
7041 return s.serializeToString(node);
7042 }).join('');
7043};
7044
7045var getParseError = function (doc) {
7046 // Firefox
7047 if (doc.documentElement.tagName === 'parsererror' &&
7048 doc.documentElement.namespaceURI === 'http://www.mozilla.org/newlayout/xml/parsererror.xml') {
7049 return doc.documentElement;
7050 }
7051
7052 // Chrome, Safari
7053 if ((doc.documentElement.tagName === 'xml' || doc.documentElement.tagName === 'html') &&
7054 doc.documentElement.childNodes &&
7055 doc.documentElement.childNodes.length > 0 &&
7056 doc.documentElement.childNodes[0].nodeName === 'parsererror') {
7057 return doc.documentElement.childNodes[0];
7058 }
7059
7060 // PhantomJS
7061 if (doc.documentElement.tagName === 'html' &&
7062 doc.documentElement.childNodes &&
7063 doc.documentElement.childNodes.length > 0 &&
7064 doc.documentElement.childNodes[0].nodeName === 'body' &&
7065 doc.documentElement.childNodes[0].childNodes &&
7066 doc.documentElement.childNodes[0].childNodes.length &&
7067 doc.documentElement.childNodes[0].childNodes[0].nodeName === 'parsererror') {
7068 return doc.documentElement.childNodes[0].childNodes[0];
7069 }
7070
7071 return undefined;
7072};
7073
7074var errorMessagePatterns = [
7075 // Chrome, Safari, PhantomJS
7076 new RegExp('^<h3[^>]*>This page contains the following errors:<\/h3><div[^>]*>(.+?)\n?<\/div>'),
7077 // Firefox
7078 new RegExp('^(.+)\n')
7079];
7080
7081var extractParseError = function (errorNode) {
7082 var content = innerXML(errorNode);
7083 var i, match;
7084
7085 for(i = 0; i < errorMessagePatterns.length; i++) {
7086 match = errorMessagePatterns[i].exec(content);
7087
7088 if (match) {
7089 return match[1];
7090 }
7091 }
7092 return undefined;
7093};
7094
7095var failOnParseError = function (doc) {
7096 var errorMessage;
7097
7098 if (doc === null) {
7099 throw new Error('Parse error');
7100 }
7101
7102 var parseError = getParseError(doc);
7103 if (parseError !== undefined) {
7104 errorMessage = extractParseError(parseError) || 'Parse error';
7105 throw new Error(errorMessage);
7106 }
7107};
7108
7109exports.failOnParseError = function (doc) {
7110 failOnParseError(doc);
7111
7112 return doc;
7113};
7114
7115},{}],41:[function(require,module,exports){
7116var removeInvalidCharacters = function (content) {
7117 // See http://www.w3.org/TR/xml/#NT-Char for valid XML 1.0 characters
7118 return content.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g, '');
7119};
7120
7121var serializeAttributeValue = function (value) {
7122 return value
7123 .replace(/&/g, '&amp;')
7124 .replace(/</g, '&lt;')
7125 .replace(/>/g, '&gt;')
7126 .replace(/"/g, '&quot;')
7127 .replace(/'/g, '&apos;');
7128};
7129
7130var serializeTextContent = function (content) {
7131 return content
7132 .replace(/&/g, '&amp;')
7133 .replace(/</g, '&lt;')
7134 .replace(/>/g, '&gt;');
7135};
7136
7137var serializeAttribute = function (attr) {
7138 var value = attr.value;
7139
7140 return ' ' + attr.name + '="' + serializeAttributeValue(value) + '"';
7141};
7142
7143var getTagName = function (node) {
7144 var tagName = node.tagName;
7145
7146 // Aid in serializing of original HTML documents
7147 if (node.namespaceURI === 'http://www.w3.org/1999/xhtml') {
7148 tagName = tagName.toLowerCase();
7149 }
7150 return tagName;
7151};
7152
7153var serializeNamespace = function (node, isRootNode) {
7154 var nodeHasXmlnsAttr = Array.prototype.map.call(node.attributes || node.attrs, function (attr) {
7155 return attr.name;
7156 })
7157 .indexOf('xmlns') >= 0;
7158 // Serialize the namespace as an xmlns attribute whenever the element
7159 // doesn't already have one and the inherited namespace does not match
7160 // the element's namespace.
7161 if (!nodeHasXmlnsAttr &&
7162 (isRootNode ||
7163 node.namespaceURI !== node.parentNode.namespaceURI)) {
7164 return ' xmlns="' + node.namespaceURI + '"';
7165 } else {
7166 return '';
7167 }
7168};
7169
7170var serializeChildren = function (node) {
7171 return Array.prototype.map.call(node.childNodes, function (childNode) {
7172 return nodeTreeToXHTML(childNode);
7173 }).join('');
7174};
7175
7176var serializeTag = function (node, isRootNode) {
7177 var output = '<' + getTagName(node);
7178 output += serializeNamespace(node, isRootNode);
7179
7180 Array.prototype.forEach.call(node.attributes || node.attrs, function (attr) {
7181 output += serializeAttribute(attr);
7182 });
7183
7184 if (node.childNodes.length > 0) {
7185 output += '>';
7186 output += serializeChildren(node);
7187 output += '</' + getTagName(node) + '>';
7188 } else {
7189 output += '/>';
7190 }
7191 return output;
7192};
7193
7194var serializeText = function (node) {
7195 var text = node.nodeValue || node.value || '';
7196 return serializeTextContent(text);
7197};
7198
7199var serializeComment = function (node) {
7200 return '<!--' +
7201 node.data
7202 .replace(/-/g, '&#45;') +
7203 '-->';
7204};
7205
7206var serializeCDATA = function (node) {
7207 return '<![CDATA[' + node.nodeValue + ']]>';
7208};
7209
7210var nodeTreeToXHTML = function (node, options) {
7211 var isRootNode = options && options.rootNode;
7212
7213 if (node.nodeName === '#document' ||
7214 node.nodeName === '#document-fragment') {
7215 return serializeChildren(node);
7216 } else {
7217 if (node.tagName) {
7218 return serializeTag(node, isRootNode);
7219 } else if (node.nodeName === '#text') {
7220 return serializeText(node);
7221 } else if (node.nodeName === '#comment') {
7222 return serializeComment(node);
7223 } else if (node.nodeName === '#cdata-section') {
7224 return serializeCDATA(node);
7225 }
7226 }
7227};
7228
7229exports.serializeToString = function (node) {
7230 return removeInvalidCharacters(nodeTreeToXHTML(node, {rootNode: true}));
7231};
7232
7233},{}]},{},[1]);