Updated
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
// This is a dummy file to trick genjsdeps into doing the right thing.
|
||||
// TODO(nicksantos): fix this
|
||||
+611
@@ -0,0 +1,611 @@
|
||||
// Copyright 2006-2008, The Google Caja project.
|
||||
// Modifications Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
// All Rights Reserved
|
||||
|
||||
/**
|
||||
* @license Portions of this code are from the google-caja project, received by
|
||||
* Google under the Apache license (http://code.google.com/p/google-caja/).
|
||||
* All other code is Copyright 2009 Google, Inc. All Rights Reserved.
|
||||
|
||||
// Copyright (C) 2006 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview A Html SAX parser.
|
||||
*
|
||||
* Examples of usage of the {@code goog.string.html.HtmlParser}:
|
||||
* <pre>
|
||||
* var handler = new MyCustomHtmlVisitorHandlerThatExtendsHtmlSaxHandler();
|
||||
* var parser = new goog.string.html.HtmlParser();
|
||||
* parser.parse(handler, '<html><a href="google.com">link found!</a></html>');
|
||||
* </pre>
|
||||
*
|
||||
* TODO(user, msamuel): validate sanitizer regex against the HTML5 grammar at
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/tree-construction.html
|
||||
*
|
||||
* @supported IE6, IE7, IE8, FF1.5, FF2, FF3, Chrome 3.0, Safari and Opera 10.
|
||||
*/
|
||||
|
||||
goog.provide('goog.string.html.HtmlParser');
|
||||
goog.provide('goog.string.html.HtmlParser.EFlags');
|
||||
goog.provide('goog.string.html.HtmlParser.Elements');
|
||||
goog.provide('goog.string.html.HtmlParser.Entities');
|
||||
goog.provide('goog.string.html.HtmlSaxHandler');
|
||||
|
||||
|
||||
/**
|
||||
* An Html parser: {@code parse} takes a string and calls methods on
|
||||
* {@code goog.string.html.HtmlSaxHandler} while it is visiting it.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
goog.string.html.HtmlParser = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* HTML entities that are encoded/decoded.
|
||||
* TODO(user): use {@code goog.string.htmlEncode} instead.
|
||||
* @enum {string}
|
||||
*/
|
||||
goog.string.html.HtmlParser.Entities = {
|
||||
lt: '<',
|
||||
gt: '>',
|
||||
amp: '&',
|
||||
nbsp: '\240',
|
||||
quot: '"',
|
||||
apos: '\''
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The html eflags, used internally on the parser.
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.string.html.HtmlParser.EFlags = {
|
||||
OPTIONAL_ENDTAG: 1,
|
||||
EMPTY: 2,
|
||||
CDATA: 4,
|
||||
RCDATA: 8,
|
||||
UNSAFE: 16,
|
||||
FOLDABLE: 32
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A map of element to a bitmap of flags it has, used internally on the parser.
|
||||
* @type {Object}
|
||||
*/
|
||||
goog.string.html.HtmlParser.Elements = {
|
||||
'a': 0,
|
||||
'abbr': 0,
|
||||
'acronym': 0,
|
||||
'address': 0,
|
||||
'applet': goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'area': goog.string.html.HtmlParser.EFlags.EMPTY,
|
||||
'b': 0,
|
||||
'base': goog.string.html.HtmlParser.EFlags.EMPTY |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'basefont': goog.string.html.HtmlParser.EFlags.EMPTY |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'bdo': 0,
|
||||
'big': 0,
|
||||
'blockquote': 0,
|
||||
'body': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.FOLDABLE,
|
||||
'br': goog.string.html.HtmlParser.EFlags.EMPTY,
|
||||
'button': 0,
|
||||
'caption': 0,
|
||||
'center': 0,
|
||||
'cite': 0,
|
||||
'code': 0,
|
||||
'col': goog.string.html.HtmlParser.EFlags.EMPTY,
|
||||
'colgroup': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'dd': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'del': 0,
|
||||
'dfn': 0,
|
||||
'dir': 0,
|
||||
'div': 0,
|
||||
'dl': 0,
|
||||
'dt': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'em': 0,
|
||||
'fieldset': 0,
|
||||
'font': 0,
|
||||
'form': 0,
|
||||
'frame': goog.string.html.HtmlParser.EFlags.EMPTY |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'frameset': goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'h1': 0,
|
||||
'h2': 0,
|
||||
'h3': 0,
|
||||
'h4': 0,
|
||||
'h5': 0,
|
||||
'h6': 0,
|
||||
'head': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.FOLDABLE,
|
||||
'hr': goog.string.html.HtmlParser.EFlags.EMPTY,
|
||||
'html': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.FOLDABLE,
|
||||
'i': 0,
|
||||
'iframe': goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.CDATA,
|
||||
'img': goog.string.html.HtmlParser.EFlags.EMPTY,
|
||||
'input': goog.string.html.HtmlParser.EFlags.EMPTY,
|
||||
'ins': 0,
|
||||
'isindex': goog.string.html.HtmlParser.EFlags.EMPTY |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'kbd': 0,
|
||||
'label': 0,
|
||||
'legend': 0,
|
||||
'li': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'link': goog.string.html.HtmlParser.EFlags.EMPTY |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'map': 0,
|
||||
'menu': 0,
|
||||
'meta': goog.string.html.HtmlParser.EFlags.EMPTY |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'noframes': goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.CDATA,
|
||||
'noscript': goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.CDATA,
|
||||
'object': goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'ol': 0,
|
||||
'optgroup': 0,
|
||||
'option': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'p': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'param': goog.string.html.HtmlParser.EFlags.EMPTY |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'pre': 0,
|
||||
'q': 0,
|
||||
's': 0,
|
||||
'samp': 0,
|
||||
'script': goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.CDATA,
|
||||
'select': 0,
|
||||
'small': 0,
|
||||
'span': 0,
|
||||
'strike': 0,
|
||||
'strong': 0,
|
||||
'style': goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.CDATA,
|
||||
'sub': 0,
|
||||
'sup': 0,
|
||||
'table': 0,
|
||||
'tbody': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'td': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'textarea': goog.string.html.HtmlParser.EFlags.RCDATA,
|
||||
'tfoot': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'th': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'thead': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'title': goog.string.html.HtmlParser.EFlags.RCDATA |
|
||||
goog.string.html.HtmlParser.EFlags.UNSAFE,
|
||||
'tr': goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG,
|
||||
'tt': 0,
|
||||
'u': 0,
|
||||
'ul': 0,
|
||||
'var': 0
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches &s.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.AMP_RE_ = /&/g;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches loose &s.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.LOOSE_AMP_RE_ =
|
||||
/&([^a-z#]|#(?:[^0-9x]|x(?:[^0-9a-f]|$)|$)|$)/gi;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches <.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.LT_RE_ = /</g;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches >.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.GT_RE_ = />/g;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches ".
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.QUOTE_RE_ = /\"/g;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches =.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.EQUALS_RE_ = /=/g;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches null characters.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.NULL_RE_ = /\0/g;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches entities.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.ENTITY_RE_ = /&(#\d+|#x[0-9A-Fa-f]+|\w+);/g;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches decimal numbers.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.DECIMAL_ESCAPE_RE_ = /^#(\d+)$/;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches hexadecimal numbers.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.HEX_ESCAPE_RE_ = /^#x([0-9A-Fa-f]+)$/;
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches the next token to be processed.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.INSIDE_TAG_TOKEN_ = new RegExp(
|
||||
// Don't capture space.
|
||||
'^\\s*(?:' +
|
||||
// Capture an attribute name in group 1, and value in group 3.
|
||||
// We capture the fact that there was an attribute in group 2, since
|
||||
// interpreters are inconsistent in whether a group that matches nothing
|
||||
// is null, undefined, or the empty string.
|
||||
('(?:' +
|
||||
'([a-z][a-z-]*)' + // attribute name
|
||||
('(' + // optionally followed
|
||||
'\\s*=\\s*' +
|
||||
('(' +
|
||||
// A double quoted string.
|
||||
'\"[^\"]*\"' +
|
||||
// A single quoted string.
|
||||
'|\'[^\']*\'' +
|
||||
// The positive lookahead is used to make sure that in
|
||||
// <foo bar= baz=boo>, the value for bar is blank, not "baz=boo".
|
||||
'|(?=[a-z][a-z-]*\\s*=)' +
|
||||
// An unquoted value that is not an attribute name.
|
||||
// We know it is not an attribute name because the previous
|
||||
// zero-width match would've eliminated that possibility.
|
||||
'|[^>\"\'\\s]*' +
|
||||
')'
|
||||
) +
|
||||
')'
|
||||
) + '?' +
|
||||
')'
|
||||
) +
|
||||
// End of tag captured in group 3.
|
||||
'|(/?>)' +
|
||||
// Don't capture cruft
|
||||
'|[^a-z\\s>]+)',
|
||||
'i');
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression that matches the next token to be processed when we are
|
||||
* outside a tag.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.OUTSIDE_TAG_TOKEN_ = new RegExp(
|
||||
'^(?:' +
|
||||
// Entity captured in group 1.
|
||||
'&(\\#[0-9]+|\\#[x][0-9a-f]+|\\w+);' +
|
||||
// Comment, doctypes, and processing instructions not captured.
|
||||
'|<[!]--[\\s\\S]*?-->|<!\\w[^>]*>|<\\?[^>*]*>' +
|
||||
// '/' captured in group 2 for close tags, and name captured in group 3.
|
||||
'|<(/)?([a-z][a-z0-9]*)' +
|
||||
// Text captured in group 4.
|
||||
'|([^<&>]+)' +
|
||||
// Cruft captured in group 5.
|
||||
'|([<&>]))',
|
||||
'i');
|
||||
|
||||
|
||||
/**
|
||||
* Given a SAX-like {@code goog.string.html.HtmlSaxHandler} parses a
|
||||
* {@code htmlText} and lets the {@code handler} know the structure while
|
||||
* visiting the nodes.
|
||||
*
|
||||
* @param {goog.string.html.HtmlSaxHandler} handler The HtmlSaxHandler that will
|
||||
* receive the events.
|
||||
* @param {string} htmlText The html text.
|
||||
*/
|
||||
goog.string.html.HtmlParser.prototype.parse = function(handler, htmlText) {
|
||||
var htmlLower = null;
|
||||
var inTag = false; // True iff we're currently processing a tag.
|
||||
var attribs = []; // Accumulates attribute names and values.
|
||||
var tagName; // The name of the tag currently being processed.
|
||||
var eflags; // The element flags for the current tag.
|
||||
var openTag; // True if the current tag is an open tag.
|
||||
|
||||
// Lets the handler know that we are starting to parse the document.
|
||||
handler.startDoc();
|
||||
|
||||
// Consumes tokens from the htmlText and stops once all tokens are processed.
|
||||
while (htmlText) {
|
||||
var regex = inTag ?
|
||||
goog.string.html.HtmlParser.INSIDE_TAG_TOKEN_ :
|
||||
goog.string.html.HtmlParser.OUTSIDE_TAG_TOKEN_;
|
||||
// Gets the next token
|
||||
var m = htmlText.match(regex);
|
||||
// And removes it from the string
|
||||
htmlText = htmlText.substring(m[0].length);
|
||||
|
||||
// TODO(goto): cleanup this code breaking it into separate methods.
|
||||
if (inTag) {
|
||||
if (m[1]) { // Attribute.
|
||||
// SetAttribute with uppercase names doesn't work on IE6.
|
||||
var attribName = goog.string.html.toLowerCase(m[1]);
|
||||
var decodedValue;
|
||||
if (m[2]) {
|
||||
var encodedValue = m[3];
|
||||
switch (encodedValue.charCodeAt(0)) { // Strip quotes.
|
||||
case 34: case 39:
|
||||
encodedValue = encodedValue.substring(
|
||||
1, encodedValue.length - 1);
|
||||
break;
|
||||
}
|
||||
decodedValue = this.unescapeEntities_(this.stripNULs_(encodedValue));
|
||||
} else {
|
||||
// Use name as value for valueless attribs, so
|
||||
// <input type=checkbox checked>
|
||||
// gets attributes ['type', 'checkbox', 'checked', 'checked']
|
||||
decodedValue = attribName;
|
||||
}
|
||||
attribs.push(attribName, decodedValue);
|
||||
} else if (m[4]) {
|
||||
if (eflags !== void 0) { // False if not in whitelist.
|
||||
if (openTag) {
|
||||
if (handler.startTag) {
|
||||
handler.startTag(/** @type {string} */ (tagName), attribs);
|
||||
}
|
||||
} else {
|
||||
if (handler.endTag) {
|
||||
handler.endTag(/** @type {string} */ (tagName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (openTag && (eflags &
|
||||
(goog.string.html.HtmlParser.EFlags.CDATA |
|
||||
goog.string.html.HtmlParser.EFlags.RCDATA))) {
|
||||
if (htmlLower === null) {
|
||||
htmlLower = goog.string.html.toLowerCase (htmlText);
|
||||
} else {
|
||||
htmlLower = htmlLower.substring(
|
||||
htmlLower.length - htmlText.length);
|
||||
}
|
||||
var dataEnd = htmlLower.indexOf('</' + tagName);
|
||||
if (dataEnd < 0) {
|
||||
dataEnd = htmlText.length;
|
||||
}
|
||||
if (eflags & goog.string.html.HtmlParser.EFlags.CDATA) {
|
||||
if (handler.cdata) {
|
||||
handler.cdata(htmlText.substring(0, dataEnd));
|
||||
}
|
||||
} else if (handler.rcdata) {
|
||||
handler.rcdata(
|
||||
this.normalizeRCData_(htmlText.substring(0, dataEnd)));
|
||||
}
|
||||
htmlText = htmlText.substring(dataEnd);
|
||||
}
|
||||
|
||||
tagName = eflags = openTag = void 0;
|
||||
attribs.length = 0;
|
||||
inTag = false;
|
||||
}
|
||||
} else {
|
||||
if (m[1]) { // Entity.
|
||||
handler.pcdata(m[0]);
|
||||
} else if (m[3]) { // Tag.
|
||||
openTag = !m[2];
|
||||
inTag = true;
|
||||
tagName = goog.string.html.toLowerCase (m[3]);
|
||||
eflags = goog.string.html.HtmlParser.Elements.hasOwnProperty(tagName) ?
|
||||
goog.string.html.HtmlParser.Elements[tagName] : void 0;
|
||||
} else if (m[4]) { // Text.
|
||||
handler.pcdata(m[4]);
|
||||
} else if (m[5]) { // Cruft.
|
||||
switch (m[5]) {
|
||||
case '<': handler.pcdata('<'); break;
|
||||
case '>': handler.pcdata('>'); break;
|
||||
default: handler.pcdata('&'); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lets the handler know that we are done parsing the document.
|
||||
handler.endDoc();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Decodes an HTML entity.
|
||||
*
|
||||
* @param {string} name The content between the '&' and the ';'.
|
||||
* @return {string} A single unicode code-point as a string.
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.prototype.lookupEntity_ = function(name) {
|
||||
// TODO(goto): use {goog.string.htmlDecode} instead ?
|
||||
// TODO(goto): π is different from Π
|
||||
name = goog.string.html.toLowerCase(name);
|
||||
if (goog.string.html.HtmlParser.Entities.hasOwnProperty(name)) {
|
||||
return goog.string.html.HtmlParser.Entities[name];
|
||||
}
|
||||
var m = name.match(goog.string.html.HtmlParser.DECIMAL_ESCAPE_RE_);
|
||||
if (m) {
|
||||
return String.fromCharCode(parseInt(m[1], 10));
|
||||
} else if (
|
||||
!!(m = name.match(goog.string.html.HtmlParser.HEX_ESCAPE_RE_))) {
|
||||
return String.fromCharCode(parseInt(m[1], 16));
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Removes null characters on the string.
|
||||
* @param {string} s The string to have the null characters removed.
|
||||
* @return {string} A string without null characters.
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.prototype.stripNULs_ = function(s) {
|
||||
return s.replace(goog.string.html.HtmlParser.NULL_RE_, '');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The plain text of a chunk of HTML CDATA which possibly containing.
|
||||
*
|
||||
* TODO(goto): use {@code goog.string.unescapeEntities} instead ?
|
||||
* @param {string} s A chunk of HTML CDATA. It must not start or end inside
|
||||
* an HTML entity.
|
||||
* @return {string} The unescaped entities.
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.prototype.unescapeEntities_ = function(s) {
|
||||
return s.replace(
|
||||
goog.string.html.HtmlParser.ENTITY_RE_,
|
||||
goog.bind(this.lookupEntity_, this));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Escape entities in RCDATA that can be escaped without changing the meaning.
|
||||
* @param {string} rcdata The RCDATA string we want to normalize.
|
||||
* @return {string} A normalized version of RCDATA.
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlParser.prototype.normalizeRCData_ = function(rcdata) {
|
||||
return rcdata.
|
||||
replace(goog.string.html.HtmlParser.LOOSE_AMP_RE_, '&$1').
|
||||
replace(goog.string.html.HtmlParser.LT_RE_, '<').
|
||||
replace(goog.string.html.HtmlParser.GT_RE_, '>');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* TODO(goto): why isn't this in the string package ? does this solves any
|
||||
* real problem ? move it to the goog.string package if it does.
|
||||
*
|
||||
* @param {string} str The string to lower case.
|
||||
* @return {string} The str in lower case format.
|
||||
*/
|
||||
goog.string.html.toLowerCase = function(str) {
|
||||
// The below may not be true on browsers in the Turkish locale.
|
||||
if ('script' === 'SCRIPT'.toLowerCase()) {
|
||||
return str.toLowerCase();
|
||||
} else {
|
||||
return str.replace(/[A-Z]/g, function(ch) {
|
||||
return String.fromCharCode(ch.charCodeAt(0) | 32);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An interface to the {@code goog.string.html.HtmlParser} visitor, that gets
|
||||
* called while the HTML is being parsed.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
goog.string.html.HtmlSaxHandler = function() {
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handler called when the parser found a new tag.
|
||||
* @param {string} name The name of the tag that is starting.
|
||||
* @param {Array.<string>} attributes The attributes of the tag.
|
||||
*/
|
||||
goog.string.html.HtmlSaxHandler.prototype.startTag = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handler called when the parser found a closing tag.
|
||||
* @param {string} name The name of the tag that is ending.
|
||||
*/
|
||||
goog.string.html.HtmlSaxHandler.prototype.endTag = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handler called when PCDATA is found.
|
||||
* @param {string} text The PCDATA text found.
|
||||
*/
|
||||
goog.string.html.HtmlSaxHandler.prototype.pcdata = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handler called when RCDATA is found.
|
||||
* @param {string} text The RCDATA text found.
|
||||
*/
|
||||
goog.string.html.HtmlSaxHandler.prototype.rcdata = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handler called when CDATA is found.
|
||||
* @param {string} text The CDATA text found.
|
||||
*/
|
||||
goog.string.html.HtmlSaxHandler.prototype.cdata = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handler called when the parser is starting to parse the document.
|
||||
*/
|
||||
goog.string.html.HtmlSaxHandler.prototype.startDoc = goog.abstractMethod;
|
||||
|
||||
|
||||
/**
|
||||
* Handler called when the parsing is done.
|
||||
*/
|
||||
goog.string.html.HtmlSaxHandler.prototype.endDoc = goog.abstractMethod;
|
||||
+605
@@ -0,0 +1,605 @@
|
||||
// Copyright 2006-2008, The Google Caja project.
|
||||
// Modifications Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
// All Rights Reserved
|
||||
|
||||
/**
|
||||
* @license Portions of this code are from the google-caja project, received by
|
||||
* Google under the Apache license (http://code.google.com/p/google-caja/).
|
||||
* All other code is Copyright 2009 Google, Inc. All Rights Reserved.
|
||||
|
||||
// Copyright (C) 2006 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview An HTML sanitizer that can satisfy a variety of security
|
||||
* policies. The HTML sanitizer is built around a SAX parser and HTML element
|
||||
* and attributes schemas.
|
||||
*
|
||||
* This package provides html sanitizing and parsing functions.
|
||||
* {@code goog.string.htmlSanitize} is basically just using a custom written
|
||||
* {@code goog.string.HtmlSaxHandler} that outputs safe html as the unsafe
|
||||
* html content is parsed by {@code goog.string.HtmlParser}.
|
||||
*
|
||||
* Examples of usage of the static {@code goog.string.htmlSanitize}:
|
||||
* <pre>
|
||||
* var safeHtml = goog.string.html.htmlSanitize('<script src="xss.js" />');
|
||||
* el.innerHTML = safeHtml;
|
||||
* </pre>
|
||||
*
|
||||
* We use {@code goog.string.StringBuffer} for fast string concatenation, since
|
||||
* htmlSanitize is relatively heavy considering that it is designed to parse
|
||||
* large html files.
|
||||
*
|
||||
* @supported IE6, IE7, IE8, FF1.5, FF2, FF3, Chrome 4.0, Safari and Opera 10.
|
||||
*/
|
||||
|
||||
goog.provide('goog.string.html.HtmlSanitizer');
|
||||
goog.provide('goog.string.html.HtmlSanitizer.AttributeType');
|
||||
goog.provide('goog.string.html.HtmlSanitizer.Attributes');
|
||||
goog.provide('goog.string.html.htmlSanitize');
|
||||
|
||||
goog.require('goog.string.StringBuffer');
|
||||
goog.require('goog.string.html.HtmlParser');
|
||||
goog.require('goog.string.html.HtmlParser.EFlags');
|
||||
goog.require('goog.string.html.HtmlParser.Elements');
|
||||
goog.require('goog.string.html.HtmlSaxHandler');
|
||||
|
||||
|
||||
/**
|
||||
* Strips unsafe tags and attributes from HTML.
|
||||
*
|
||||
* @param {string} htmlText The HTML text to sanitize.
|
||||
* @param {function(string) : string} opt_urlPolicy A transform to apply to URL
|
||||
* attribute values.
|
||||
* @param {function(string) : string} opt_nmTokenPolicy A transform to apply to
|
||||
* names, IDs, and classes.
|
||||
* @return {string} A sanitized HTML, safe to be embedded on the page.
|
||||
*/
|
||||
goog.string.html.htmlSanitize = function(
|
||||
htmlText, opt_urlPolicy, opt_nmTokenPolicy) {
|
||||
var stringBuffer = new goog.string.StringBuffer();
|
||||
var handler = new goog.string.html.HtmlSanitizer(
|
||||
stringBuffer, opt_urlPolicy, opt_nmTokenPolicy);
|
||||
var parser = new goog.string.html.HtmlParser();
|
||||
parser.parse(handler, htmlText);
|
||||
return stringBuffer.toString();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* An implementation of the {@code goog.string.HtmlSaxHandler} interface that
|
||||
* will take each of the html tags and sanitize it.
|
||||
*
|
||||
* @param {goog.string.StringBuffer} stringBuffer A string buffer, used to
|
||||
* output the html as we sanitize it.
|
||||
* @param {?function(string):string} opt_urlPolicy An optional function to be
|
||||
* applied in URLs.
|
||||
* @param {?function(string):string} opt_nmTokenPolicy An optional function to
|
||||
* be applied in names.
|
||||
* @constructor
|
||||
* @extends {goog.string.html.HtmlSaxHandler}
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer = function(
|
||||
stringBuffer, opt_urlPolicy, opt_nmTokenPolicy) {
|
||||
goog.string.html.HtmlSaxHandler.call(this);
|
||||
|
||||
/**
|
||||
* The string buffer that holds the sanitized version of the html. Used
|
||||
* during the parse time.
|
||||
* @type {goog.string.StringBuffer}
|
||||
* @private
|
||||
*/
|
||||
this.stringBuffer_ = stringBuffer;
|
||||
|
||||
/**
|
||||
* A stack that holds how the handler is being called.
|
||||
* @type {Array}
|
||||
* @private
|
||||
*/
|
||||
this.stack_ = [];
|
||||
|
||||
/**
|
||||
* Whether we are ignoring what is being processed or not.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.ignoring_ = false;
|
||||
|
||||
/**
|
||||
* A function to be applied to urls found on the parsing process.
|
||||
* @type {?function(string):string}
|
||||
* @private
|
||||
*/
|
||||
this.urlPolicy_ = opt_urlPolicy;
|
||||
|
||||
/**
|
||||
* A function to be applied to names fround on the parsing process.
|
||||
* @type {?function(string):string}
|
||||
* @private
|
||||
*/
|
||||
this.nmTokenPolicy_ = opt_nmTokenPolicy;
|
||||
};
|
||||
goog.inherits(
|
||||
goog.string.html.HtmlSanitizer,
|
||||
goog.string.html.HtmlSaxHandler);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The HTML types the parser supports.
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.AttributeType = {
|
||||
NONE: 0,
|
||||
URI: 1,
|
||||
URI_FRAGMENT: 11,
|
||||
SCRIPT: 2,
|
||||
STYLE: 3,
|
||||
ID: 4,
|
||||
IDREF: 5,
|
||||
IDREFS: 6,
|
||||
GLOBAL_NAME: 7,
|
||||
LOCAL_NAME: 8,
|
||||
CLASSES: 9,
|
||||
FRAME_TARGET: 10
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A map of attributes to types it has.
|
||||
* @enum {number}
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.Attributes = {
|
||||
'*::class': goog.string.html.HtmlSanitizer.AttributeType.CLASSES,
|
||||
'*::dir': 0,
|
||||
'*::id': goog.string.html.HtmlSanitizer.AttributeType.ID,
|
||||
'*::lang': 0,
|
||||
'*::onclick': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::ondblclick': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onkeydown': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onkeypress': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onkeyup': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onload': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onmousedown': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onmousemove': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onmouseout': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onmouseover': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onmouseup': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::style': goog.string.html.HtmlSanitizer.AttributeType.STYLE,
|
||||
'*::title': 0,
|
||||
'*::accesskey': 0,
|
||||
'*::tabindex': 0,
|
||||
'*::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'*::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'a::coords': 0,
|
||||
'a::href': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'a::hreflang': 0,
|
||||
'a::name': goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME,
|
||||
'a::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'a::rel': 0,
|
||||
'a::rev': 0,
|
||||
'a::shape': 0,
|
||||
'a::target': goog.string.html.HtmlSanitizer.AttributeType.FRAME_TARGET,
|
||||
'a::type': 0,
|
||||
'area::accesskey': 0,
|
||||
'area::alt': 0,
|
||||
'area::coords': 0,
|
||||
'area::href': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'area::nohref': 0,
|
||||
'area::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'area::shape': 0,
|
||||
'area::tabindex': 0,
|
||||
'area::target': goog.string.html.HtmlSanitizer.AttributeType.FRAME_TARGET,
|
||||
'bdo::dir': 0,
|
||||
'blockquote::cite': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'br::clear': 0,
|
||||
'button::accesskey': 0,
|
||||
'button::disabled': 0,
|
||||
'button::name': goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME,
|
||||
'button::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'button::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'button::tabindex': 0,
|
||||
'button::type': 0,
|
||||
'button::value': 0,
|
||||
'caption::align': 0,
|
||||
'col::align': 0,
|
||||
'col::char': 0,
|
||||
'col::charoff': 0,
|
||||
'col::span': 0,
|
||||
'col::valign': 0,
|
||||
'col::width': 0,
|
||||
'colgroup::align': 0,
|
||||
'colgroup::char': 0,
|
||||
'colgroup::charoff': 0,
|
||||
'colgroup::span': 0,
|
||||
'colgroup::valign': 0,
|
||||
'colgroup::width': 0,
|
||||
'del::cite': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'del::datetime': 0,
|
||||
'dir::compact': 0,
|
||||
'div::align': 0,
|
||||
'dl::compact': 0,
|
||||
'font::color': 0,
|
||||
'font::face': 0,
|
||||
'font::size': 0,
|
||||
'form::accept': 0,
|
||||
'form::action': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'form::autocomplete': 0,
|
||||
'form::enctype': 0,
|
||||
'form::method': 0,
|
||||
'form::name': goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME,
|
||||
'form::onreset': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'form::onsubmit': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'form::target': goog.string.html.HtmlSanitizer.AttributeType.FRAME_TARGET,
|
||||
'h1::align': 0,
|
||||
'h2::align': 0,
|
||||
'h3::align': 0,
|
||||
'h4::align': 0,
|
||||
'h5::align': 0,
|
||||
'h6::align': 0,
|
||||
'hr::align': 0,
|
||||
'hr::noshade': 0,
|
||||
'hr::size': 0,
|
||||
'hr::width': 0,
|
||||
'img::align': 0,
|
||||
'img::alt': 0,
|
||||
'img::border': 0,
|
||||
'img::height': 0,
|
||||
'img::hspace': 0,
|
||||
'img::ismap': 0,
|
||||
'img::longdesc': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'img::name': goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME,
|
||||
'img::src': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'img::usemap': goog.string.html.HtmlSanitizer.AttributeType.URI_FRAGMENT,
|
||||
'img::vspace': 0,
|
||||
'img::width': 0,
|
||||
'input::accept': 0,
|
||||
'input::accesskey': 0,
|
||||
'input::autocomplete': 0,
|
||||
'input::align': 0,
|
||||
'input::alt': 0,
|
||||
'input::checked': 0,
|
||||
'input::disabled': 0,
|
||||
'input::ismap': 0,
|
||||
'input::maxlength': 0,
|
||||
'input::name': goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME,
|
||||
'input::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'input::onchange': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'input::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'input::onselect': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'input::readonly': 0,
|
||||
'input::size': 0,
|
||||
'input::src': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'input::tabindex': 0,
|
||||
'input::type': 0,
|
||||
'input::usemap': goog.string.html.HtmlSanitizer.AttributeType.URI_FRAGMENT,
|
||||
'input::value': 0,
|
||||
'ins::cite': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'ins::datetime': 0,
|
||||
'label::accesskey': 0,
|
||||
'label::for': goog.string.html.HtmlSanitizer.AttributeType.IDREF,
|
||||
'label::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'label::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'legend::accesskey': 0,
|
||||
'legend::align': 0,
|
||||
'li::type': 0,
|
||||
'li::value': 0,
|
||||
'map::name': goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME,
|
||||
'menu::compact': 0,
|
||||
'ol::compact': 0,
|
||||
'ol::start': 0,
|
||||
'ol::type': 0,
|
||||
'optgroup::disabled': 0,
|
||||
'optgroup::label': 0,
|
||||
'option::disabled': 0,
|
||||
'option::label': 0,
|
||||
'option::selected': 0,
|
||||
'option::value': 0,
|
||||
'p::align': 0,
|
||||
'pre::width': 0,
|
||||
'q::cite': goog.string.html.HtmlSanitizer.AttributeType.URI,
|
||||
'select::disabled': 0,
|
||||
'select::multiple': 0,
|
||||
'select::name': goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME,
|
||||
'select::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'select::onchange': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'select::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'select::size': 0,
|
||||
'select::tabindex': 0,
|
||||
'table::align': 0,
|
||||
'table::bgcolor': 0,
|
||||
'table::border': 0,
|
||||
'table::cellpadding': 0,
|
||||
'table::cellspacing': 0,
|
||||
'table::frame': 0,
|
||||
'table::rules': 0,
|
||||
'table::summary': 0,
|
||||
'table::width': 0,
|
||||
'tbody::align': 0,
|
||||
'tbody::char': 0,
|
||||
'tbody::charoff': 0,
|
||||
'tbody::valign': 0,
|
||||
'td::abbr': 0,
|
||||
'td::align': 0,
|
||||
'td::axis': 0,
|
||||
'td::bgcolor': 0,
|
||||
'td::char': 0,
|
||||
'td::charoff': 0,
|
||||
'td::colspan': 0,
|
||||
'td::headers': goog.string.html.HtmlSanitizer.AttributeType.IDREFS,
|
||||
'td::height': 0,
|
||||
'td::nowrap': 0,
|
||||
'td::rowspan': 0,
|
||||
'td::scope': 0,
|
||||
'td::valign': 0,
|
||||
'td::width': 0,
|
||||
'textarea::accesskey': 0,
|
||||
'textarea::cols': 0,
|
||||
'textarea::disabled': 0,
|
||||
'textarea::name': goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME,
|
||||
'textarea::onblur': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'textarea::onchange': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'textarea::onfocus': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'textarea::onselect': goog.string.html.HtmlSanitizer.AttributeType.SCRIPT,
|
||||
'textarea::readonly': 0,
|
||||
'textarea::rows': 0,
|
||||
'textarea::tabindex': 0,
|
||||
'tfoot::align': 0,
|
||||
'tfoot::char': 0,
|
||||
'tfoot::charoff': 0,
|
||||
'tfoot::valign': 0,
|
||||
'th::abbr': 0,
|
||||
'th::align': 0,
|
||||
'th::axis': 0,
|
||||
'th::bgcolor': 0,
|
||||
'th::char': 0,
|
||||
'th::charoff': 0,
|
||||
'th::colspan': 0,
|
||||
'th::headers': goog.string.html.HtmlSanitizer.AttributeType.IDREFS,
|
||||
'th::height': 0,
|
||||
'th::nowrap': 0,
|
||||
'th::rowspan': 0,
|
||||
'th::scope': 0,
|
||||
'th::valign': 0,
|
||||
'th::width': 0,
|
||||
'thead::align': 0,
|
||||
'thead::char': 0,
|
||||
'thead::charoff': 0,
|
||||
'thead::valign': 0,
|
||||
'tr::align': 0,
|
||||
'tr::bgcolor': 0,
|
||||
'tr::char': 0,
|
||||
'tr::charoff': 0,
|
||||
'tr::valign': 0,
|
||||
'ul::compact': 0,
|
||||
'ul::type': 0
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.startTag =
|
||||
function(tagName, attribs) {
|
||||
if (this.ignoring_) {
|
||||
return;
|
||||
}
|
||||
if (!goog.string.html.HtmlParser.Elements.hasOwnProperty(tagName)) {
|
||||
return;
|
||||
}
|
||||
var eflags = goog.string.html.HtmlParser.Elements[tagName];
|
||||
if (eflags & goog.string.html.HtmlParser.EFlags.FOLDABLE) {
|
||||
return;
|
||||
} else if (eflags & goog.string.html.HtmlParser.EFlags.UNSAFE) {
|
||||
this.ignoring_ = !(eflags & goog.string.html.HtmlParser.EFlags.EMPTY);
|
||||
return;
|
||||
}
|
||||
attribs = this.sanitizeAttributes_(tagName, attribs);
|
||||
if (attribs) {
|
||||
if (!(eflags & goog.string.html.HtmlParser.EFlags.EMPTY)) {
|
||||
this.stack_.push(tagName);
|
||||
}
|
||||
|
||||
this.stringBuffer_.append('<', tagName);
|
||||
for (var i = 0, n = attribs.length; i < n; i += 2) {
|
||||
var attribName = attribs[i],
|
||||
value = attribs[i + 1];
|
||||
if (value !== null && value !== void 0) {
|
||||
this.stringBuffer_.append(' ', attribName, '="',
|
||||
this.escapeAttrib_(value), '"');
|
||||
}
|
||||
}
|
||||
this.stringBuffer_.append('>');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.endTag = function(tagName) {
|
||||
if (this.ignoring_) {
|
||||
this.ignoring_ = false;
|
||||
return;
|
||||
}
|
||||
if (!goog.string.html.HtmlParser.Elements.hasOwnProperty(tagName)) {
|
||||
return;
|
||||
}
|
||||
var eflags = goog.string.html.HtmlParser.Elements[tagName];
|
||||
if (!(eflags & (goog.string.html.HtmlParser.EFlags.UNSAFE |
|
||||
goog.string.html.HtmlParser.EFlags.EMPTY |
|
||||
goog.string.html.HtmlParser.EFlags.FOLDABLE))) {
|
||||
var index;
|
||||
if (eflags & goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG) {
|
||||
for (index = this.stack_.length; --index >= 0;) {
|
||||
var stackEl = this.stack_[index];
|
||||
if (stackEl === tagName) {
|
||||
break;
|
||||
}
|
||||
if (!(goog.string.html.HtmlParser.Elements[stackEl] &
|
||||
goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG)) {
|
||||
// Don't pop non optional end tags looking for a match.
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (index = this.stack_.length; --index >= 0;) {
|
||||
if (this.stack_[index] === tagName) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (index < 0) { return; } // Not opened.
|
||||
for (var i = this.stack_.length; --i > index;) {
|
||||
var stackEl = this.stack_[i];
|
||||
if (!(goog.string.html.HtmlParser.Elements[stackEl] &
|
||||
goog.string.html.HtmlParser.EFlags.OPTIONAL_ENDTAG)) {
|
||||
this.stringBuffer_.append('</', stackEl, '>');
|
||||
}
|
||||
}
|
||||
this.stack_.length = index;
|
||||
this.stringBuffer_.append('</', tagName, '>');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.pcdata = function(text) {
|
||||
if (!this.ignoring_) {
|
||||
this.stringBuffer_.append(text);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.rcdata = function(text) {
|
||||
if (!this.ignoring_) {
|
||||
this.stringBuffer_.append(text);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.cdata = function(text) {
|
||||
if (!this.ignoring_) {
|
||||
this.stringBuffer_.append(text);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.startDoc = function() {
|
||||
this.stack_ = [];
|
||||
this.ignoring_ = false;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritDoc
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.endDoc = function() {
|
||||
for (var i = this.stack_.length; --i >= 0;) {
|
||||
this.stringBuffer_.append('</', this.stack_[i], '>');
|
||||
}
|
||||
this.stack_.length = 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Escapes HTML special characters in attribute values as HTML entities.
|
||||
*
|
||||
* TODO(user): use {@code goog.string.htmlEscape} instead ?
|
||||
* @param {string} s The string to be escaped.
|
||||
* @return {string} An escaped version of {@code s}.
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.escapeAttrib_ = function(s) {
|
||||
// Escaping '=' defangs many UTF-7 and SGML short-tag attacks.
|
||||
return s.replace(goog.string.html.HtmlParser.AMP_RE_, '&').
|
||||
replace(goog.string.html.HtmlParser.LT_RE_, '<').
|
||||
replace(goog.string.html.HtmlParser.GT_RE_, '>').
|
||||
replace(goog.string.html.HtmlParser.QUOTE_RE_, '"').
|
||||
replace(goog.string.html.HtmlParser.EQUALS_RE_, '=');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sanitizes attributes found on html entities.
|
||||
* @param {string} tagName The name of the tag in which the {@code attribs} were
|
||||
* found.
|
||||
* @param {Array.<?string>} attribs An array of attributes.
|
||||
* @return {Array.<?string>} A sanitized version of the {@code attribs}.
|
||||
* @private
|
||||
*/
|
||||
goog.string.html.HtmlSanitizer.prototype.sanitizeAttributes_ =
|
||||
function(tagName, attribs) {
|
||||
for (var i = 0; i < attribs.length; i += 2) {
|
||||
var attribName = attribs[i];
|
||||
var value = attribs[i + 1];
|
||||
var atype = null, attribKey;
|
||||
if ((attribKey = tagName + '::' + attribName,
|
||||
goog.string.html.HtmlSanitizer.Attributes.hasOwnProperty(attribKey)) ||
|
||||
(attribKey = '*::' + attribName,
|
||||
goog.string.html.HtmlSanitizer.Attributes.hasOwnProperty(attribKey))) {
|
||||
atype = goog.string.html.HtmlSanitizer.Attributes[attribKey];
|
||||
}
|
||||
if (atype !== null) {
|
||||
switch (atype) {
|
||||
case 0: break;
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.SCRIPT:
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.STYLE:
|
||||
value = null;
|
||||
break;
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.ID:
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.IDREF:
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.IDREFS:
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.GLOBAL_NAME:
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.LOCAL_NAME:
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.CLASSES:
|
||||
value = this.nmTokenPolicy_ ?
|
||||
this.nmTokenPolicy_(/** @type {string} */ (value)) : value;
|
||||
break;
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.URI:
|
||||
value = this.urlPolicy_ && this.urlPolicy_(
|
||||
/** @type {string} */ (value));
|
||||
break;
|
||||
case goog.string.html.HtmlSanitizer.AttributeType.URI_FRAGMENT:
|
||||
if (value && '#' === value.charAt(0)) {
|
||||
value = this.nmTokenPolicy_ ? this.nmTokenPolicy_(value) : value;
|
||||
if (value) { value = '#' + value; }
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
value = null;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
value = null;
|
||||
}
|
||||
attribs[i + 1] = value;
|
||||
}
|
||||
return attribs;
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,162 @@
|
||||
goog.require('goog.dom');
|
||||
goog.require('goog.dom.query');
|
||||
goog.require('goog.testing.asserts'); // assertThrows
|
||||
|
||||
goog.setTestOnly('query_test');
|
||||
|
||||
function testBasicSelectors() {
|
||||
assertQuery(4, 'h3');
|
||||
assertQuery(1, 'h1:first-child');
|
||||
assertQuery(2, 'h3:first-child');
|
||||
assertQuery(1, '#t');
|
||||
assertQuery(1, '#bug');
|
||||
assertQuery(4, '#t h3');
|
||||
assertQuery(1, 'div#t');
|
||||
assertQuery(4, 'div#t h3');
|
||||
assertQuery(0, 'span#t');
|
||||
assertQuery(1, '#t div > h3');
|
||||
assertQuery(2, '.foo');
|
||||
assertQuery(1, '.foo.bar');
|
||||
assertQuery(2, '.baz');
|
||||
assertQuery(3, '#t > h3');
|
||||
}
|
||||
|
||||
function testSyntacticEquivalents() {
|
||||
// syntactic equivalents
|
||||
assertQuery(12, '#t > *');
|
||||
assertQuery(12, '#t >');
|
||||
assertQuery(3, '.foo > *');
|
||||
assertQuery(3, '.foo >');
|
||||
}
|
||||
|
||||
function testWithARootById() {
|
||||
// with a root, by ID
|
||||
assertQuery(3, '> *', 'container');
|
||||
assertQuery(3, '> h3', 't');
|
||||
}
|
||||
|
||||
function testCompoundQueries() {
|
||||
// compound queries
|
||||
assertQuery(2, '.foo, .bar');
|
||||
assertQuery(2, '.foo,.bar');
|
||||
}
|
||||
|
||||
function testMultipleClassAttributes() {
|
||||
// multiple class attribute
|
||||
assertQuery(1, '.foo.bar');
|
||||
assertQuery(2, '.foo');
|
||||
assertQuery(2, '.baz');
|
||||
}
|
||||
|
||||
function testCaseSensitivity() {
|
||||
// case sensitivity
|
||||
assertQuery(1, 'span.baz');
|
||||
assertQuery(1, 'sPaN.baz');
|
||||
assertQuery(1, 'SPAN.baz');
|
||||
assertQuery(1, '[class = \"foo bar\"]');
|
||||
assertQuery(2, '[foo~=\"bar\"]');
|
||||
assertQuery(2, '[ foo ~= \"bar\" ]');
|
||||
}
|
||||
|
||||
function testAttributes() {
|
||||
assertQuery(3, '[foo]');
|
||||
assertQuery(1, '[foo$=\"thud\"]');
|
||||
assertQuery(1, '[foo$=thud]');
|
||||
assertQuery(1, '[foo$=\"thudish\"]');
|
||||
assertQuery(1, '#t [foo$=thud]');
|
||||
assertQuery(1, '#t [ title $= thud ]');
|
||||
assertQuery(0, '#t span[ title $= thud ]');
|
||||
assertQuery(2, '[foo|=\"bar\"]');
|
||||
assertQuery(1, '[foo|=\"bar-baz\"]');
|
||||
assertQuery(0, '[foo|=\"baz\"]');
|
||||
}
|
||||
|
||||
function testDescendantSelectors() {
|
||||
assertQuery(3, '>', 'container');
|
||||
assertQuery(3, '> *', 'container');
|
||||
assertQuery(2, '> [qux]', 'container');
|
||||
assertEquals('child1', goog.dom.query('> [qux]', 'container')[0].id);
|
||||
assertEquals('child3', goog.dom.query('> [qux]', 'container')[1].id);
|
||||
assertQuery(3, '>', 'container');
|
||||
assertQuery(3, '> *', 'container');
|
||||
}
|
||||
|
||||
function testSiblingSelectors() {
|
||||
assertQuery(1, '+', 'container');
|
||||
assertQuery(3, '~', 'container');
|
||||
assertQuery(1, '.foo + span');
|
||||
assertQuery(4, '.foo ~ span');
|
||||
assertQuery(1, '#foo ~ *');
|
||||
assertQuery(1, '#foo ~');
|
||||
}
|
||||
|
||||
function testSubSelectors() {
|
||||
// sub-selector parsing
|
||||
assertQuery(1, '#t span.foo:not(span:first-child)');
|
||||
assertQuery(1, '#t span.foo:not(:first-child)');
|
||||
}
|
||||
|
||||
function testNthChild() {
|
||||
assertEquals(goog.dom.$('_foo'), goog.dom.query('.foo:nth-child(2)')[0]);
|
||||
assertQuery(2, '#t > h3:nth-child(odd)');
|
||||
assertQuery(3, '#t h3:nth-child(odd)');
|
||||
assertQuery(3, '#t h3:nth-child(2n+1)');
|
||||
assertQuery(1, '#t h3:nth-child(even)');
|
||||
assertQuery(1, '#t h3:nth-child(2n)');
|
||||
assertQuery(1, '#t h3:nth-child(2n+3)');
|
||||
assertQuery(2, '#t h3:nth-child(1)');
|
||||
assertQuery(1, '#t > h3:nth-child(1)');
|
||||
assertQuery(3, '#t :nth-child(3)');
|
||||
assertQuery(0, '#t > div:nth-child(1)');
|
||||
assertQuery(7, '#t span');
|
||||
assertQuery(3, '#t > *:nth-child(n+10)');
|
||||
assertQuery(1, '#t > *:nth-child(n+12)');
|
||||
assertQuery(10, '#t > *:nth-child(-n+10)');
|
||||
assertQuery(5, '#t > *:nth-child(-2n+10)');
|
||||
assertQuery(6, '#t > *:nth-child(2n+2)');
|
||||
assertQuery(5, '#t > *:nth-child(2n+4)');
|
||||
assertQuery(5, '#t > *:nth-child(2n+4)');
|
||||
assertQuery(12, '#t > *:nth-child(n-5)');
|
||||
assertQuery(6, '#t > *:nth-child(2n-5)');
|
||||
}
|
||||
|
||||
function testEmptyPseudoSelector() {
|
||||
assertQuery(4, '#t > span:empty');
|
||||
assertQuery(6, '#t span:empty');
|
||||
assertQuery(0, 'h3 span:empty');
|
||||
assertQuery(1, 'h3 :not(:empty)');
|
||||
}
|
||||
|
||||
function testIdsWithColons(){
|
||||
assertQuery(1, "#silly\\:id\\:\\:with\\:colons");
|
||||
}
|
||||
|
||||
function testOrder() {
|
||||
var els = goog.dom.query('.myupperclass .myclass input');
|
||||
assertEquals('myid1', els[0].id);
|
||||
assertEquals('myid2', els[1].id);
|
||||
}
|
||||
|
||||
function testCorrectDocumentInFrame() {
|
||||
var frameDocument = window.frames['ifr'].document;
|
||||
frameDocument.body.innerHTML =
|
||||
document.getElementById('iframe-test').innerHTML;
|
||||
|
||||
var els = goog.dom.query('#if1 .if2 div', document);
|
||||
var frameEls = goog.dom.query('#if1 .if2 div', frameDocument);
|
||||
|
||||
assertEquals(els.length, frameEls.length);
|
||||
assertEquals(1, frameEls.length);
|
||||
assertNotEquals(document.getElementById('if3'),
|
||||
frameDocument.getElementById('if3'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {number} expectedNumberOfNodes
|
||||
* @param {...*} var_args
|
||||
*/
|
||||
function assertQuery(expectedNumberOfNodes, var_args) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
assertEquals(expectedNumberOfNodes,
|
||||
goog.dom.query.apply(null, args).length);
|
||||
}
|
||||
+751
@@ -0,0 +1,751 @@
|
||||
/**
|
||||
* @license
|
||||
Copyright (c) 2008, Adobe Systems Incorporated
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Adobe Systems Incorporated nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
||||
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
/**
|
||||
* @license
|
||||
JPEG encoder ported to JavaScript and optimized by Andreas Ritter, www.bytestrom.eu, 11/2009
|
||||
|
||||
Basic GUI blocking jpeg encoder
|
||||
|
||||
v 0.9
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview This code was ported from
|
||||
* http://www.bytestrom.eu/blog/2009/1120a_jpeg_encoder_for_javascript and
|
||||
* modified slightly for Closure.
|
||||
*/
|
||||
|
||||
goog.provide('goog.crypt.JpegEncoder');
|
||||
|
||||
goog.require('goog.crypt.base64');
|
||||
|
||||
/**
|
||||
* Initializes the JpegEncoder.
|
||||
*
|
||||
* @constructor
|
||||
* @param {number=} opt_quality The compression quality. Default 50.
|
||||
*/
|
||||
goog.crypt.JpegEncoder = function(opt_quality) {
|
||||
var self = this;
|
||||
var fround = Math.round;
|
||||
var ffloor = Math.floor;
|
||||
var YTable = new Array(64);
|
||||
var UVTable = new Array(64);
|
||||
var fdtbl_Y = new Array(64);
|
||||
var fdtbl_UV = new Array(64);
|
||||
var YDC_HT;
|
||||
var UVDC_HT;
|
||||
var YAC_HT;
|
||||
var UVAC_HT;
|
||||
|
||||
var bitcode = new Array(65535);
|
||||
var category = new Array(65535);
|
||||
var outputfDCTQuant = new Array(64);
|
||||
var DU = new Array(64);
|
||||
var byteout = [];
|
||||
var bytenew = 0;
|
||||
var bytepos = 7;
|
||||
|
||||
var YDU = new Array(64);
|
||||
var UDU = new Array(64);
|
||||
var VDU = new Array(64);
|
||||
var clt = new Array(256);
|
||||
var RGB_YUV_TABLE = new Array(2048);
|
||||
var currentQuality;
|
||||
|
||||
var ZigZag = [
|
||||
0, 1, 5, 6,14,15,27,28,
|
||||
2, 4, 7,13,16,26,29,42,
|
||||
3, 8,12,17,25,30,41,43,
|
||||
9,11,18,24,31,40,44,53,
|
||||
10,19,23,32,39,45,52,54,
|
||||
20,22,33,38,46,51,55,60,
|
||||
21,34,37,47,50,56,59,61,
|
||||
35,36,48,49,57,58,62,63
|
||||
];
|
||||
|
||||
var std_dc_luminance_nrcodes = [0,0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0];
|
||||
var std_dc_luminance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
|
||||
var std_ac_luminance_nrcodes = [0,0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d];
|
||||
var std_ac_luminance_values = [
|
||||
0x01,0x02,0x03,0x00,0x04,0x11,0x05,0x12,
|
||||
0x21,0x31,0x41,0x06,0x13,0x51,0x61,0x07,
|
||||
0x22,0x71,0x14,0x32,0x81,0x91,0xa1,0x08,
|
||||
0x23,0x42,0xb1,0xc1,0x15,0x52,0xd1,0xf0,
|
||||
0x24,0x33,0x62,0x72,0x82,0x09,0x0a,0x16,
|
||||
0x17,0x18,0x19,0x1a,0x25,0x26,0x27,0x28,
|
||||
0x29,0x2a,0x34,0x35,0x36,0x37,0x38,0x39,
|
||||
0x3a,0x43,0x44,0x45,0x46,0x47,0x48,0x49,
|
||||
0x4a,0x53,0x54,0x55,0x56,0x57,0x58,0x59,
|
||||
0x5a,0x63,0x64,0x65,0x66,0x67,0x68,0x69,
|
||||
0x6a,0x73,0x74,0x75,0x76,0x77,0x78,0x79,
|
||||
0x7a,0x83,0x84,0x85,0x86,0x87,0x88,0x89,
|
||||
0x8a,0x92,0x93,0x94,0x95,0x96,0x97,0x98,
|
||||
0x99,0x9a,0xa2,0xa3,0xa4,0xa5,0xa6,0xa7,
|
||||
0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,0xb5,0xb6,
|
||||
0xb7,0xb8,0xb9,0xba,0xc2,0xc3,0xc4,0xc5,
|
||||
0xc6,0xc7,0xc8,0xc9,0xca,0xd2,0xd3,0xd4,
|
||||
0xd5,0xd6,0xd7,0xd8,0xd9,0xda,0xe1,0xe2,
|
||||
0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,0xea,
|
||||
0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
|
||||
0xf9,0xfa
|
||||
];
|
||||
|
||||
var std_dc_chrominance_nrcodes = [0,0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0];
|
||||
var std_dc_chrominance_values = [0,1,2,3,4,5,6,7,8,9,10,11];
|
||||
var std_ac_chrominance_nrcodes = [0,0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77];
|
||||
var std_ac_chrominance_values = [
|
||||
0x00,0x01,0x02,0x03,0x11,0x04,0x05,0x21,
|
||||
0x31,0x06,0x12,0x41,0x51,0x07,0x61,0x71,
|
||||
0x13,0x22,0x32,0x81,0x08,0x14,0x42,0x91,
|
||||
0xa1,0xb1,0xc1,0x09,0x23,0x33,0x52,0xf0,
|
||||
0x15,0x62,0x72,0xd1,0x0a,0x16,0x24,0x34,
|
||||
0xe1,0x25,0xf1,0x17,0x18,0x19,0x1a,0x26,
|
||||
0x27,0x28,0x29,0x2a,0x35,0x36,0x37,0x38,
|
||||
0x39,0x3a,0x43,0x44,0x45,0x46,0x47,0x48,
|
||||
0x49,0x4a,0x53,0x54,0x55,0x56,0x57,0x58,
|
||||
0x59,0x5a,0x63,0x64,0x65,0x66,0x67,0x68,
|
||||
0x69,0x6a,0x73,0x74,0x75,0x76,0x77,0x78,
|
||||
0x79,0x7a,0x82,0x83,0x84,0x85,0x86,0x87,
|
||||
0x88,0x89,0x8a,0x92,0x93,0x94,0x95,0x96,
|
||||
0x97,0x98,0x99,0x9a,0xa2,0xa3,0xa4,0xa5,
|
||||
0xa6,0xa7,0xa8,0xa9,0xaa,0xb2,0xb3,0xb4,
|
||||
0xb5,0xb6,0xb7,0xb8,0xb9,0xba,0xc2,0xc3,
|
||||
0xc4,0xc5,0xc6,0xc7,0xc8,0xc9,0xca,0xd2,
|
||||
0xd3,0xd4,0xd5,0xd6,0xd7,0xd8,0xd9,0xda,
|
||||
0xe2,0xe3,0xe4,0xe5,0xe6,0xe7,0xe8,0xe9,
|
||||
0xea,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7,0xf8,
|
||||
0xf9,0xfa
|
||||
];
|
||||
|
||||
function initQuantTables(sf){
|
||||
var YQT = [
|
||||
16, 11, 10, 16, 24, 40, 51, 61,
|
||||
12, 12, 14, 19, 26, 58, 60, 55,
|
||||
14, 13, 16, 24, 40, 57, 69, 56,
|
||||
14, 17, 22, 29, 51, 87, 80, 62,
|
||||
18, 22, 37, 56, 68,109,103, 77,
|
||||
24, 35, 55, 64, 81,104,113, 92,
|
||||
49, 64, 78, 87,103,121,120,101,
|
||||
72, 92, 95, 98,112,100,103, 99
|
||||
];
|
||||
|
||||
for (var i = 0; i < 64; i++) {
|
||||
var t = ffloor((YQT[i]*sf+50)/100);
|
||||
if (t < 1) {
|
||||
t = 1;
|
||||
} else if (t > 255) {
|
||||
t = 255;
|
||||
}
|
||||
YTable[ZigZag[i]] = t;
|
||||
}
|
||||
var UVQT = [
|
||||
17, 18, 24, 47, 99, 99, 99, 99,
|
||||
18, 21, 26, 66, 99, 99, 99, 99,
|
||||
24, 26, 56, 99, 99, 99, 99, 99,
|
||||
47, 66, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99,
|
||||
99, 99, 99, 99, 99, 99, 99, 99
|
||||
];
|
||||
for (var j = 0; j < 64; j++) {
|
||||
var u = ffloor((UVQT[j]*sf+50)/100);
|
||||
if (u < 1) {
|
||||
u = 1;
|
||||
} else if (u > 255) {
|
||||
u = 255;
|
||||
}
|
||||
UVTable[ZigZag[j]] = u;
|
||||
}
|
||||
var aasf = [
|
||||
1.0, 1.387039845, 1.306562965, 1.175875602,
|
||||
1.0, 0.785694958, 0.541196100, 0.275899379
|
||||
];
|
||||
var k = 0;
|
||||
for (var row = 0; row < 8; row++)
|
||||
{
|
||||
for (var col = 0; col < 8; col++)
|
||||
{
|
||||
fdtbl_Y[k] = (1.0 / (YTable [ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
|
||||
fdtbl_UV[k] = (1.0 / (UVTable[ZigZag[k]] * aasf[row] * aasf[col] * 8.0));
|
||||
k++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function computeHuffmanTbl(nrcodes, std_table){
|
||||
var codevalue = 0;
|
||||
var pos_in_table = 0;
|
||||
var HT = new Array();
|
||||
for (var k = 1; k <= 16; k++) {
|
||||
for (var j = 1; j <= nrcodes[k]; j++) {
|
||||
HT[std_table[pos_in_table]] = [];
|
||||
HT[std_table[pos_in_table]][0] = codevalue;
|
||||
HT[std_table[pos_in_table]][1] = k;
|
||||
pos_in_table++;
|
||||
codevalue++;
|
||||
}
|
||||
codevalue*=2;
|
||||
}
|
||||
return HT;
|
||||
}
|
||||
|
||||
function initHuffmanTbl()
|
||||
{
|
||||
YDC_HT = computeHuffmanTbl(std_dc_luminance_nrcodes,std_dc_luminance_values);
|
||||
UVDC_HT = computeHuffmanTbl(std_dc_chrominance_nrcodes,std_dc_chrominance_values);
|
||||
YAC_HT = computeHuffmanTbl(std_ac_luminance_nrcodes,std_ac_luminance_values);
|
||||
UVAC_HT = computeHuffmanTbl(std_ac_chrominance_nrcodes,std_ac_chrominance_values);
|
||||
}
|
||||
|
||||
function initCategoryNumber()
|
||||
{
|
||||
var nrlower = 1;
|
||||
var nrupper = 2;
|
||||
for (var cat = 1; cat <= 15; cat++) {
|
||||
//Positive numbers
|
||||
for (var nr = nrlower; nr<nrupper; nr++) {
|
||||
category[32767+nr] = cat;
|
||||
bitcode[32767+nr] = [];
|
||||
bitcode[32767+nr][1] = cat;
|
||||
bitcode[32767+nr][0] = nr;
|
||||
}
|
||||
//Negative numbers
|
||||
for (var nrneg =-(nrupper-1); nrneg<=-nrlower; nrneg++) {
|
||||
category[32767+nrneg] = cat;
|
||||
bitcode[32767+nrneg] = [];
|
||||
bitcode[32767+nrneg][1] = cat;
|
||||
bitcode[32767+nrneg][0] = nrupper-1+nrneg;
|
||||
}
|
||||
nrlower <<= 1;
|
||||
nrupper <<= 1;
|
||||
}
|
||||
}
|
||||
|
||||
function initRGBYUVTable() {
|
||||
for(var i = 0; i < 256;i++) {
|
||||
RGB_YUV_TABLE[i] = 19595 * i;
|
||||
RGB_YUV_TABLE[(i+ 256)>>0] = 38470 * i;
|
||||
RGB_YUV_TABLE[(i+ 512)>>0] = 7471 * i + 0x8000;
|
||||
RGB_YUV_TABLE[(i+ 768)>>0] = -11059 * i;
|
||||
RGB_YUV_TABLE[(i+1024)>>0] = -21709 * i;
|
||||
RGB_YUV_TABLE[(i+1280)>>0] = 32768 * i + 0x807FFF;
|
||||
RGB_YUV_TABLE[(i+1536)>>0] = -27439 * i;
|
||||
RGB_YUV_TABLE[(i+1792)>>0] = - 5329 * i;
|
||||
}
|
||||
}
|
||||
|
||||
// IO functions
|
||||
function writeBits(bs)
|
||||
{
|
||||
var value = bs[0];
|
||||
var posval = bs[1]-1;
|
||||
while ( posval >= 0 ) {
|
||||
if (value & (1 << posval) ) {
|
||||
bytenew |= (1 << bytepos);
|
||||
}
|
||||
posval--;
|
||||
bytepos--;
|
||||
if (bytepos < 0) {
|
||||
if (bytenew == 0xFF) {
|
||||
writeByte(0xFF);
|
||||
writeByte(0);
|
||||
}
|
||||
else {
|
||||
writeByte(bytenew);
|
||||
}
|
||||
bytepos=7;
|
||||
bytenew=0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function writeByte(value)
|
||||
{
|
||||
byteout.push(clt[value]); // write char directly instead of converting later
|
||||
}
|
||||
|
||||
function writeWord(value)
|
||||
{
|
||||
writeByte((value>>8)&0xFF);
|
||||
writeByte((value )&0xFF);
|
||||
}
|
||||
|
||||
// DCT & quantization core
|
||||
function fDCTQuant(data, fdtbl)
|
||||
{
|
||||
var d0, d1, d2, d3, d4, d5, d6, d7;
|
||||
/* Pass 1: process rows. */
|
||||
var dataOff=0;
|
||||
var i;
|
||||
var I8 = 8;
|
||||
var I64 = 64;
|
||||
for (i=0; i<I8; ++i)
|
||||
{
|
||||
d0 = data[dataOff];
|
||||
d1 = data[dataOff+1];
|
||||
d2 = data[dataOff+2];
|
||||
d3 = data[dataOff+3];
|
||||
d4 = data[dataOff+4];
|
||||
d5 = data[dataOff+5];
|
||||
d6 = data[dataOff+6];
|
||||
d7 = data[dataOff+7];
|
||||
|
||||
var tmp0 = d0 + d7;
|
||||
var tmp7 = d0 - d7;
|
||||
var tmp1 = d1 + d6;
|
||||
var tmp6 = d1 - d6;
|
||||
var tmp2 = d2 + d5;
|
||||
var tmp5 = d2 - d5;
|
||||
var tmp3 = d3 + d4;
|
||||
var tmp4 = d3 - d4;
|
||||
|
||||
/* Even part */
|
||||
var tmp10 = tmp0 + tmp3; /* phase 2 */
|
||||
var tmp13 = tmp0 - tmp3;
|
||||
var tmp11 = tmp1 + tmp2;
|
||||
var tmp12 = tmp1 - tmp2;
|
||||
|
||||
data[dataOff] = tmp10 + tmp11; /* phase 3 */
|
||||
data[dataOff+4] = tmp10 - tmp11;
|
||||
|
||||
var z1 = (tmp12 + tmp13) * 0.707106781; /* c4 */
|
||||
data[dataOff+2] = tmp13 + z1; /* phase 5 */
|
||||
data[dataOff+6] = tmp13 - z1;
|
||||
|
||||
/* Odd part */
|
||||
tmp10 = tmp4 + tmp5; /* phase 2 */
|
||||
tmp11 = tmp5 + tmp6;
|
||||
tmp12 = tmp6 + tmp7;
|
||||
|
||||
/* The rotator is modified from fig 4-8 to avoid extra negations. */
|
||||
var z5 = (tmp10 - tmp12) * 0.382683433; /* c6 */
|
||||
var z2 = 0.541196100 * tmp10 + z5; /* c2-c6 */
|
||||
var z4 = 1.306562965 * tmp12 + z5; /* c2+c6 */
|
||||
var z3 = tmp11 * 0.707106781; /* c4 */
|
||||
|
||||
var z11 = tmp7 + z3; /* phase 5 */
|
||||
var z13 = tmp7 - z3;
|
||||
|
||||
data[dataOff+5] = z13 + z2; /* phase 6 */
|
||||
data[dataOff+3] = z13 - z2;
|
||||
data[dataOff+1] = z11 + z4;
|
||||
data[dataOff+7] = z11 - z4;
|
||||
|
||||
dataOff += 8; /* advance pointer to next row */
|
||||
}
|
||||
|
||||
/* Pass 2: process columns. */
|
||||
dataOff = 0;
|
||||
for (i=0; i<I8; ++i)
|
||||
{
|
||||
d0 = data[dataOff];
|
||||
d1 = data[dataOff + 8];
|
||||
d2 = data[dataOff + 16];
|
||||
d3 = data[dataOff + 24];
|
||||
d4 = data[dataOff + 32];
|
||||
d5 = data[dataOff + 40];
|
||||
d6 = data[dataOff + 48];
|
||||
d7 = data[dataOff + 56];
|
||||
|
||||
var tmp0p2 = d0 + d7;
|
||||
var tmp7p2 = d0 - d7;
|
||||
var tmp1p2 = d1 + d6;
|
||||
var tmp6p2 = d1 - d6;
|
||||
var tmp2p2 = d2 + d5;
|
||||
var tmp5p2 = d2 - d5;
|
||||
var tmp3p2 = d3 + d4;
|
||||
var tmp4p2 = d3 - d4;
|
||||
|
||||
/* Even part */
|
||||
var tmp10p2 = tmp0p2 + tmp3p2; /* phase 2 */
|
||||
var tmp13p2 = tmp0p2 - tmp3p2;
|
||||
var tmp11p2 = tmp1p2 + tmp2p2;
|
||||
var tmp12p2 = tmp1p2 - tmp2p2;
|
||||
|
||||
data[dataOff] = tmp10p2 + tmp11p2; /* phase 3 */
|
||||
data[dataOff+32] = tmp10p2 - tmp11p2;
|
||||
|
||||
var z1p2 = (tmp12p2 + tmp13p2) * 0.707106781; /* c4 */
|
||||
data[dataOff+16] = tmp13p2 + z1p2; /* phase 5 */
|
||||
data[dataOff+48] = tmp13p2 - z1p2;
|
||||
|
||||
/* Odd part */
|
||||
tmp10p2 = tmp4p2 + tmp5p2; /* phase 2 */
|
||||
tmp11p2 = tmp5p2 + tmp6p2;
|
||||
tmp12p2 = tmp6p2 + tmp7p2;
|
||||
|
||||
/* The rotator is modified from fig 4-8 to avoid extra negations. */
|
||||
var z5p2 = (tmp10p2 - tmp12p2) * 0.382683433; /* c6 */
|
||||
var z2p2 = 0.541196100 * tmp10p2 + z5p2; /* c2-c6 */
|
||||
var z4p2 = 1.306562965 * tmp12p2 + z5p2; /* c2+c6 */
|
||||
var z3p2 = tmp11p2 * 0.707106781; /* c4 */
|
||||
|
||||
var z11p2 = tmp7p2 + z3p2; /* phase 5 */
|
||||
var z13p2 = tmp7p2 - z3p2;
|
||||
|
||||
data[dataOff+40] = z13p2 + z2p2; /* phase 6 */
|
||||
data[dataOff+24] = z13p2 - z2p2;
|
||||
data[dataOff+ 8] = z11p2 + z4p2;
|
||||
data[dataOff+56] = z11p2 - z4p2;
|
||||
|
||||
dataOff++; /* advance pointer to next column */
|
||||
}
|
||||
|
||||
// Quantize/descale the coefficients
|
||||
var fDCTQuant;
|
||||
for (i=0; i<I64; ++i)
|
||||
{
|
||||
// Apply the quantization and scaling factor & Round to nearest integer
|
||||
fDCTQuant = data[i]*fdtbl[i];
|
||||
outputfDCTQuant[i] = (fDCTQuant > 0.0) ? ((fDCTQuant + 0.5)|0) : ((fDCTQuant - 0.5)|0);
|
||||
//outputfDCTQuant[i] = fround(fDCTQuant);
|
||||
|
||||
}
|
||||
return outputfDCTQuant;
|
||||
}
|
||||
|
||||
function writeAPP0()
|
||||
{
|
||||
writeWord(0xFFE0); // marker
|
||||
writeWord(16); // length
|
||||
writeByte(0x4A); // J
|
||||
writeByte(0x46); // F
|
||||
writeByte(0x49); // I
|
||||
writeByte(0x46); // F
|
||||
writeByte(0); // = "JFIF",'\0'
|
||||
writeByte(1); // versionhi
|
||||
writeByte(1); // versionlo
|
||||
writeByte(0); // xyunits
|
||||
writeWord(1); // xdensity
|
||||
writeWord(1); // ydensity
|
||||
writeByte(0); // thumbnwidth
|
||||
writeByte(0); // thumbnheight
|
||||
}
|
||||
|
||||
function writeSOF0(width, height)
|
||||
{
|
||||
writeWord(0xFFC0); // marker
|
||||
writeWord(17); // length, truecolor YUV JPG
|
||||
writeByte(8); // precision
|
||||
writeWord(height);
|
||||
writeWord(width);
|
||||
writeByte(3); // nrofcomponents
|
||||
writeByte(1); // IdY
|
||||
writeByte(0x11); // HVY
|
||||
writeByte(0); // QTY
|
||||
writeByte(2); // IdU
|
||||
writeByte(0x11); // HVU
|
||||
writeByte(1); // QTU
|
||||
writeByte(3); // IdV
|
||||
writeByte(0x11); // HVV
|
||||
writeByte(1); // QTV
|
||||
}
|
||||
|
||||
function writeDQT()
|
||||
{
|
||||
writeWord(0xFFDB); // marker
|
||||
writeWord(132); // length
|
||||
writeByte(0);
|
||||
for (var i=0; i<64; i++) {
|
||||
writeByte(YTable[i]);
|
||||
}
|
||||
writeByte(1);
|
||||
for (var j=0; j<64; j++) {
|
||||
writeByte(UVTable[j]);
|
||||
}
|
||||
}
|
||||
|
||||
function writeDHT()
|
||||
{
|
||||
writeWord(0xFFC4); // marker
|
||||
writeWord(0x01A2); // length
|
||||
|
||||
writeByte(0); // HTYDCinfo
|
||||
for (var i=0; i<16; i++) {
|
||||
writeByte(std_dc_luminance_nrcodes[i+1]);
|
||||
}
|
||||
for (var j=0; j<=11; j++) {
|
||||
writeByte(std_dc_luminance_values[j]);
|
||||
}
|
||||
|
||||
writeByte(0x10); // HTYACinfo
|
||||
for (var k=0; k<16; k++) {
|
||||
writeByte(std_ac_luminance_nrcodes[k+1]);
|
||||
}
|
||||
for (var l=0; l<=161; l++) {
|
||||
writeByte(std_ac_luminance_values[l]);
|
||||
}
|
||||
|
||||
writeByte(1); // HTUDCinfo
|
||||
for (var m=0; m<16; m++) {
|
||||
writeByte(std_dc_chrominance_nrcodes[m+1]);
|
||||
}
|
||||
for (var n=0; n<=11; n++) {
|
||||
writeByte(std_dc_chrominance_values[n]);
|
||||
}
|
||||
|
||||
writeByte(0x11); // HTUACinfo
|
||||
for (var o=0; o<16; o++) {
|
||||
writeByte(std_ac_chrominance_nrcodes[o+1]);
|
||||
}
|
||||
for (var p=0; p<=161; p++) {
|
||||
writeByte(std_ac_chrominance_values[p]);
|
||||
}
|
||||
}
|
||||
|
||||
function writeSOS()
|
||||
{
|
||||
writeWord(0xFFDA); // marker
|
||||
writeWord(12); // length
|
||||
writeByte(3); // nrofcomponents
|
||||
writeByte(1); // IdY
|
||||
writeByte(0); // HTY
|
||||
writeByte(2); // IdU
|
||||
writeByte(0x11); // HTU
|
||||
writeByte(3); // IdV
|
||||
writeByte(0x11); // HTV
|
||||
writeByte(0); // Ss
|
||||
writeByte(0x3f); // Se
|
||||
writeByte(0); // Bf
|
||||
}
|
||||
|
||||
function processDU(CDU, fdtbl, DC, HTDC, HTAC){
|
||||
var EOB = HTAC[0x00];
|
||||
var M16zeroes = HTAC[0xF0];
|
||||
var pos;
|
||||
var I16 = 16;
|
||||
var I63 = 63;
|
||||
var I64 = 64;
|
||||
var DU_DCT = fDCTQuant(CDU, fdtbl);
|
||||
//ZigZag reorder
|
||||
for (var j=0;j<I64;++j) {
|
||||
DU[ZigZag[j]]=DU_DCT[j];
|
||||
}
|
||||
var Diff = DU[0] - DC; DC = DU[0];
|
||||
//Encode DC
|
||||
if (Diff==0) {
|
||||
writeBits(HTDC[0]); // Diff might be 0
|
||||
} else {
|
||||
pos = 32767+Diff;
|
||||
writeBits(HTDC[category[pos]]);
|
||||
writeBits(bitcode[pos]);
|
||||
}
|
||||
//Encode ACs
|
||||
var end0pos = 63; // was const... which is crazy
|
||||
for (; (end0pos>0)&&(DU[end0pos]==0); end0pos--) {};
|
||||
//end0pos = first element in reverse order !=0
|
||||
if ( end0pos == 0) {
|
||||
writeBits(EOB);
|
||||
return DC;
|
||||
}
|
||||
var i = 1;
|
||||
var lng;
|
||||
while ( i <= end0pos ) {
|
||||
var startpos = i;
|
||||
for (; (DU[i]==0) && (i<=end0pos); ++i) {}
|
||||
var nrzeroes = i-startpos;
|
||||
if ( nrzeroes >= I16 ) {
|
||||
lng = nrzeroes>>4;
|
||||
for (var nrmarker=1; nrmarker <= lng; ++nrmarker)
|
||||
writeBits(M16zeroes);
|
||||
nrzeroes = nrzeroes&0xF;
|
||||
}
|
||||
pos = 32767+DU[i];
|
||||
writeBits(HTAC[(nrzeroes<<4)+category[pos]]);
|
||||
writeBits(bitcode[pos]);
|
||||
i++;
|
||||
}
|
||||
if ( end0pos != I63 ) {
|
||||
writeBits(EOB);
|
||||
}
|
||||
return DC;
|
||||
}
|
||||
|
||||
function initCharLookupTable(){
|
||||
var sfcc = String.fromCharCode;
|
||||
for(var i=0; i < 256; i++){ ///// ACHTUNG // 255
|
||||
clt[i] = sfcc(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes ImageData to JPEG.
|
||||
*
|
||||
* @param {ImageData} image
|
||||
* @param {number=} opt_quality The compression quality.
|
||||
* @return {string} base64-encoded JPEG data.
|
||||
*/
|
||||
this.encode = function(image,opt_quality) // image data object
|
||||
{
|
||||
if(opt_quality) setQuality(opt_quality);
|
||||
|
||||
// Initialize bit writer
|
||||
byteout = new Array();
|
||||
bytenew=0;
|
||||
bytepos=7;
|
||||
|
||||
// Add JPEG headers
|
||||
writeWord(0xFFD8); // SOI
|
||||
writeAPP0();
|
||||
writeDQT();
|
||||
writeSOF0(image.width,image.height);
|
||||
writeDHT();
|
||||
writeSOS();
|
||||
|
||||
|
||||
// Encode 8x8 macroblocks
|
||||
var _DCY=0;
|
||||
var _DCU=0;
|
||||
var _DCV=0;
|
||||
|
||||
bytenew=0;
|
||||
bytepos=7;
|
||||
|
||||
|
||||
this.encode.displayName = "_encode_";
|
||||
|
||||
var imageData = image.data;
|
||||
var width = image.width;
|
||||
var height = image.height;
|
||||
|
||||
var quadWidth = width*4;
|
||||
var tripleWidth = width*3;
|
||||
|
||||
var x, y = 0;
|
||||
var r, g, b;
|
||||
var start,p, col,row,pos;
|
||||
while(y < height){
|
||||
x = 0;
|
||||
while(x < quadWidth){
|
||||
start = quadWidth * y + x;
|
||||
p = start;
|
||||
col = -1;
|
||||
row = 0;
|
||||
|
||||
for(pos=0; pos < 64; pos++){
|
||||
row = pos >> 3;// /8
|
||||
col = ( pos & 7 ) * 4; // %8
|
||||
p = start + ( row * quadWidth ) + col;
|
||||
|
||||
if(y+row >= height){ // padding bottom
|
||||
p-= (quadWidth*(y+1+row-height));
|
||||
}
|
||||
|
||||
if(x+col >= quadWidth){ // padding right
|
||||
p-= ((x+col) - quadWidth +4)
|
||||
}
|
||||
|
||||
r = imageData[ p++ ];
|
||||
g = imageData[ p++ ];
|
||||
b = imageData[ p++ ];
|
||||
|
||||
|
||||
/* // calculate YUV values dynamically
|
||||
YDU[pos]=((( 0.29900)*r+( 0.58700)*g+( 0.11400)*b))-128; //-0x80
|
||||
UDU[pos]=(((-0.16874)*r+(-0.33126)*g+( 0.50000)*b));
|
||||
VDU[pos]=((( 0.50000)*r+(-0.41869)*g+(-0.08131)*b));
|
||||
*/
|
||||
|
||||
// use lookup table (slightly faster)
|
||||
YDU[pos] = ((RGB_YUV_TABLE[r] + RGB_YUV_TABLE[(g + 256)>>0] + RGB_YUV_TABLE[(b + 512)>>0]) >> 16)-128;
|
||||
UDU[pos] = ((RGB_YUV_TABLE[(r + 768)>>0] + RGB_YUV_TABLE[(g + 1024)>>0] + RGB_YUV_TABLE[(b + 1280)>>0]) >> 16)-128;
|
||||
VDU[pos] = ((RGB_YUV_TABLE[(r + 1280)>>0] + RGB_YUV_TABLE[(g + 1536)>>0] + RGB_YUV_TABLE[(b + 1792)>>0]) >> 16)-128;
|
||||
|
||||
}
|
||||
|
||||
_DCY = processDU(YDU, fdtbl_Y, _DCY, YDC_HT, YAC_HT);
|
||||
_DCU = processDU(UDU, fdtbl_UV, _DCU, UVDC_HT, UVAC_HT);
|
||||
_DCV = processDU(VDU, fdtbl_UV, _DCV, UVDC_HT, UVAC_HT);
|
||||
x+=32;
|
||||
}
|
||||
y+=8;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
// Do the bit alignment of the EOI marker
|
||||
if ( bytepos >= 0 ) {
|
||||
var fillbits = [];
|
||||
fillbits[1] = bytepos+1;
|
||||
fillbits[0] = (1<<(bytepos+1))-1;
|
||||
writeBits(fillbits);
|
||||
}
|
||||
|
||||
writeWord(0xFFD9); //EOI
|
||||
|
||||
var jpegDataUri = 'data:image/jpeg;base64,'
|
||||
+ goog.crypt.base64.encodeString(byteout.join(''));
|
||||
|
||||
byteout = [];
|
||||
|
||||
return jpegDataUri
|
||||
}
|
||||
|
||||
function setQuality(quality){
|
||||
if (quality <= 0) {
|
||||
quality = 1;
|
||||
}
|
||||
if (quality > 100) {
|
||||
quality = 100;
|
||||
}
|
||||
|
||||
if(currentQuality == quality) return // don't recalc if unchanged
|
||||
|
||||
var sf = 0;
|
||||
if (quality < 50) {
|
||||
sf = Math.floor(5000 / quality);
|
||||
} else {
|
||||
sf = Math.floor(200 - quality*2);
|
||||
}
|
||||
|
||||
initQuantTables(sf);
|
||||
currentQuality = quality;
|
||||
}
|
||||
|
||||
function init(){
|
||||
if(!opt_quality) opt_quality = 50;
|
||||
// Create tables
|
||||
initCharLookupTable()
|
||||
initHuffmanTbl();
|
||||
initCategoryNumber();
|
||||
initRGBYUVTable();
|
||||
|
||||
setQuality(opt_quality);
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
};
|
||||
+712
@@ -0,0 +1,712 @@
|
||||
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
|
||||
|
||||
/**
|
||||
* @fileoverview A generator of lorem ipsum text based on the python
|
||||
* implementation at http://code.google.com/p/lorem-ipsum-generator/.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.text.LoremIpsum');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.math');
|
||||
goog.require('goog.string');
|
||||
goog.require('goog.structs.Map');
|
||||
goog.require('goog.structs.Set');
|
||||
|
||||
|
||||
/**
|
||||
* Generates random strings of "lorem ipsum" text, based on the word
|
||||
* distribution of a sample text, using the words in a dictionary.
|
||||
* @constructor
|
||||
*/
|
||||
goog.text.LoremIpsum = function() {
|
||||
this.generateChains_(this.sample_);
|
||||
this.generateStatistics_(this.sample_);
|
||||
|
||||
this.initializeDictionary_(this.dictionary_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Delimiters that end sentences.
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.DELIMITERS_SENTENCES_ = ['.', '?', '!'];
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression for spliting a text into sentences.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.SENTENCE_SPLIT_REGEX_ = /[\.\?\!]/;
|
||||
|
||||
|
||||
/**
|
||||
* Delimiters that end words.
|
||||
* @type {Array.<string>}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.DELIMITERS_WORDS_ = [',', '.', '?', '!'];
|
||||
|
||||
|
||||
/**
|
||||
* Regular expression for spliting text into words.
|
||||
* @type {RegExp}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.WORD_SPLIT_REGEX_ = /\s/;
|
||||
|
||||
|
||||
/**
|
||||
* Words that can be used in the generated output.
|
||||
* Maps a word-length to a list of words of that length.
|
||||
* @type {goog.structs.Map}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.words_;
|
||||
|
||||
|
||||
/**
|
||||
* Chains of three words that appear in the sample text
|
||||
* Maps a pair of word-lengths to a third word-length and an optional
|
||||
* piece of trailing punctuation (for example, a period, comma, etc.).
|
||||
* @type {goog.structs.Map}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.chains_;
|
||||
|
||||
|
||||
/**
|
||||
* Pairs of word-lengths that can appear at the beginning of sentences.
|
||||
* @type {Array}
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.starts_;
|
||||
|
||||
|
||||
/**
|
||||
* Averange sentence length in words.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.sentenceMean_;
|
||||
|
||||
|
||||
/**
|
||||
* Sigma (sqrt of variance) for the sentence length in words.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.sentenceSigma_;
|
||||
|
||||
|
||||
/**
|
||||
* Averange paragraph length in sentences.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.paragraphMean_;
|
||||
|
||||
|
||||
/**
|
||||
* Sigma (sqrt of variance) for the paragraph length in sentences.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.paragraphSigma_;
|
||||
|
||||
|
||||
/**
|
||||
* Generates the chains and starts values required for sentence generation.
|
||||
* @param {string} sample The same text.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.generateChains_ = function(sample) {
|
||||
var words = goog.text.LoremIpsum.splitWords_(sample);
|
||||
var wordInfo = goog.array.map(words, goog.text.LoremIpsum.getWordInfo_);
|
||||
|
||||
var previous = [0, 0];
|
||||
var previousKey = previous.join('-');
|
||||
var chains = new goog.structs.Map();
|
||||
var starts = [previousKey];
|
||||
var chainKeys = {};
|
||||
|
||||
goog.array.forEach(wordInfo, function(pair) {
|
||||
var chain = chains.get(previousKey);
|
||||
if (chain) {
|
||||
chain.push(pair);
|
||||
} else {
|
||||
chain = [pair];
|
||||
chains.set(previousKey, chain);
|
||||
}
|
||||
|
||||
if (goog.array.contains(
|
||||
goog.text.LoremIpsum.DELIMITERS_SENTENCES_, pair[1])) {
|
||||
starts.push(previousKey);
|
||||
}
|
||||
chainKeys[previousKey] = previous;
|
||||
previous = [previous[1], pair[0]];
|
||||
previousKey = previous.join('-');
|
||||
});
|
||||
|
||||
if (chains.getCount() > 0) {
|
||||
this.chains_ = chains;
|
||||
this.starts_ = starts;
|
||||
this.chainKeys_ = chainKeys;
|
||||
} else {
|
||||
throw Error('Could not generate chains from sample text.');
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the mean and standard deviation of sentence and paragraph lengths.
|
||||
* @param {string} sample The same text.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.generateStatistics_ = function(sample) {
|
||||
this.generateSentenceStatistics_(sample);
|
||||
this.generateParagraphStatistics_(sample);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the mean and standard deviation of the lengths of sentences
|
||||
* (in words) in a sample text.
|
||||
* @param {string} sample The same text.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.generateSentenceStatistics_ = function(sample) {
|
||||
var sentences = goog.array.filter(
|
||||
goog.text.LoremIpsum.splitSentences_(sample),
|
||||
goog.text.LoremIpsum.isNotEmptyOrWhitepace_);
|
||||
|
||||
var sentenceLengths = goog.array.map(
|
||||
goog.array.map(sentences, goog.text.LoremIpsum.splitWords_),
|
||||
goog.text.LoremIpsum.arrayLength_);
|
||||
this.sentenceMean_ = goog.math.average.apply(null, sentenceLengths);
|
||||
this.sentenceSigma_ = goog.math.standardDeviation.apply(
|
||||
null, sentenceLengths);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the mean and standard deviation of the lengths of paragraphs
|
||||
* (in sentences) in a sample text.
|
||||
* @param {string} sample The same text.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.generateParagraphStatistics_ = function(sample) {
|
||||
var paragraphs = goog.array.filter(
|
||||
goog.text.LoremIpsum.splitParagraphs_(sample),
|
||||
goog.text.LoremIpsum.isNotEmptyOrWhitepace_);
|
||||
|
||||
var paragraphLengths = goog.array.map(
|
||||
goog.array.map(paragraphs, goog.text.LoremIpsum.splitSentences_),
|
||||
goog.text.LoremIpsum.arrayLength_);
|
||||
|
||||
this.paragraphMean_ = goog.math.average.apply(null, paragraphLengths);
|
||||
this.paragraphSigma_ = goog.math.standardDeviation.apply(
|
||||
null, paragraphLengths);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Sets the generator to use a given selection of words for generating
|
||||
* sentences with.
|
||||
* @param {string} dictionary The dictionary to use.
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.initializeDictionary_ = function(dictionary) {
|
||||
var dictionaryWords = goog.text.LoremIpsum.splitWords_(dictionary);
|
||||
|
||||
var words = new goog.structs.Map();
|
||||
goog.array.forEach(dictionaryWords, function(word) {
|
||||
var set = words.get(word.length);
|
||||
if (!set) {
|
||||
set = new goog.structs.Set();
|
||||
words.set(word.length, set);
|
||||
}
|
||||
set.add(word);
|
||||
});
|
||||
|
||||
this.words_ = words;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Picks a random starting chain.
|
||||
* @return {Array.<string>} The starting key.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.chooseRandomStart_ = function() {
|
||||
var key = goog.text.LoremIpsum.randomChoice_(this.starts_);
|
||||
return this.chainKeys_[key];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generates a single sentence, of random length.
|
||||
* @param {boolean} opt_startWithLorem Whether to start the setnence with the
|
||||
* standard "Lorem ipsum..." first sentence.
|
||||
* @return {string} The generated sentence.
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.generateSentence = function(opt_startWithLorem) {
|
||||
if (this.chains_.getCount() == 0 || this.starts_.length == 0) {
|
||||
throw Error('No chains created');
|
||||
}
|
||||
|
||||
if (this.words_.getCount() == 0) {
|
||||
throw Error('No dictionary');
|
||||
}
|
||||
|
||||
// The length of the sentence is a normally distributed random variable.
|
||||
var sentenceLength = goog.text.LoremIpsum.randomNormal_(
|
||||
this.sentenceMean_, this.sentenceSigma_)
|
||||
sentenceLength = Math.max(Math.floor(sentenceLength), 1);
|
||||
|
||||
var wordDelimiter = ''; // Defined here in case while loop doesn't run
|
||||
|
||||
// Start the sentence with "Lorem ipsum...", if desired
|
||||
var sentence;
|
||||
if (opt_startWithLorem) {
|
||||
var lorem = 'lorem ipsum dolor sit amet, consecteteur adipiscing elit';
|
||||
sentence = goog.text.LoremIpsum.splitWords_(lorem);
|
||||
if (sentence.length > sentenceLength) {
|
||||
sentence.length = sentenceLength;
|
||||
}
|
||||
var lastWord = sentence[sentence.length - 1];
|
||||
var lastChar = lastWord.substring(lastWord.length - 1);
|
||||
if (goog.array.contains(goog.text.LoremIpsum.DELIMITERS_WORDS_, lastChar)) {
|
||||
wordDelimiter = lastChar;
|
||||
}
|
||||
} else {
|
||||
sentence = [];
|
||||
}
|
||||
|
||||
var previous = [];
|
||||
var previousKey = '';
|
||||
// Generate a sentence from the "chains"
|
||||
while (sentence.length < sentenceLength) {
|
||||
// If the current starting point is invalid, choose another randomly
|
||||
if (!this.chains_.containsKey(previousKey)) {
|
||||
previous = this.chooseRandomStart_();
|
||||
previousKey = previous.join('-');
|
||||
}
|
||||
|
||||
// Choose the next "chain" to go to. This determines the next word
|
||||
// length we'll use, and whether there is e.g. a comma at the end of
|
||||
// the word.
|
||||
var chain = /** @type {Array} */ (goog.text.LoremIpsum.randomChoice_(
|
||||
/** @type {Array} */ (this.chains_.get(previousKey))));
|
||||
var wordLength = chain[0];
|
||||
|
||||
// If the word delimiter contained in the chain is also a sentence
|
||||
// delimiter, then we don't include it because we don't want the
|
||||
// sentence to end prematurely (we want the length to match the
|
||||
// sentence_length value).
|
||||
//debugger;
|
||||
if (goog.array.contains(goog.text.LoremIpsum.DELIMITERS_SENTENCES_,
|
||||
chain[1])) {
|
||||
wordDelimiter = '';
|
||||
} else {
|
||||
wordDelimiter = chain[1];
|
||||
}
|
||||
|
||||
// Choose a word randomly that matches (or closely matches) the
|
||||
// length we're after.
|
||||
var closestLength = goog.text.LoremIpsum.chooseClosest(
|
||||
this.words_.getKeys(), wordLength);
|
||||
var word = goog.text.LoremIpsum.randomChoice_(
|
||||
this.words_.get(closestLength).getValues());
|
||||
|
||||
sentence.push(word + wordDelimiter);
|
||||
previous = [previous[1], wordLength];
|
||||
previousKey = previous.join('-');
|
||||
}
|
||||
|
||||
// Finish the sentence off with capitalisation, a period and
|
||||
// form it into a string
|
||||
sentence = sentence.join(' ');
|
||||
sentence = sentence.slice(0, 1).toUpperCase() + sentence.slice(1);
|
||||
if (sentence.substring(sentence.length - 1) == wordDelimiter) {
|
||||
sentence = sentence.slice(0, sentence.length - 1);
|
||||
}
|
||||
return sentence + '.';
|
||||
};
|
||||
|
||||
/**
|
||||
* Generates a single lorem ipsum paragraph, of random length.
|
||||
* @param {boolean} opt_startWithLorem Whether to start the sentence with the
|
||||
* standard "Lorem ipsum..." first sentence.
|
||||
* @return {string} The generated sentence.
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.generateParagraph = function(
|
||||
opt_startWithLorem) {
|
||||
// The length of the paragraph is a normally distributed random variable.
|
||||
var paragraphLength = goog.text.LoremIpsum.randomNormal_(
|
||||
this.paragraphMean_, this.paragraphSigma_);
|
||||
paragraphLength = Math.max(Math.floor(paragraphLength), 1);
|
||||
|
||||
// Construct a paragraph from a number of sentences.
|
||||
var paragraph = []
|
||||
var startWithLorem = opt_startWithLorem;
|
||||
while (paragraph.length < paragraphLength) {
|
||||
var sentence = this.generateSentence(startWithLorem);
|
||||
paragraph.push(sentence);
|
||||
startWithLorem = false;
|
||||
}
|
||||
|
||||
// Form the paragraph into a string.
|
||||
paragraph = paragraph.join(' ')
|
||||
return paragraph
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Splits a piece of text into paragraphs.
|
||||
* @param {string} text The text to split.
|
||||
* @return {Array.<string>} An array of paragraphs.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.splitParagraphs_ = function(text) {
|
||||
return text.split('\n')
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Splits a piece of text into sentences.
|
||||
* @param {string} text The text to split.
|
||||
* @return {Array.<string>} An array of sentences.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.splitSentences_ = function(text) {
|
||||
return goog.array.filter(
|
||||
text.split(goog.text.LoremIpsum.SENTENCE_SPLIT_REGEX_),
|
||||
goog.text.LoremIpsum.isNotEmptyOrWhitepace_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Splits a piece of text into words..
|
||||
* @param {string} text The text to split.
|
||||
* @return {Array.<string>} An array of words.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.splitWords_ = function(text) {
|
||||
return goog.array.filter(
|
||||
text.split(goog.text.LoremIpsum.WORD_SPLIT_REGEX_),
|
||||
goog.text.LoremIpsum.isNotEmptyOrWhitepace_);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the text is not empty or just whitespace.
|
||||
* @param {string} text The text to check.
|
||||
* @return {boolean} Whether the text is nether empty nor whitespace.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.isNotEmptyOrWhitepace_ = function(text) {
|
||||
return goog.string.trim(text).length > 0;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Returns the length of an array. Written as a function so it can be used
|
||||
* as a function parameter.
|
||||
* @param {Array} array The array to check.
|
||||
* @return {number} The length of the array.
|
||||
*/
|
||||
goog.text.LoremIpsum.arrayLength_ = function(array) {
|
||||
return array.length;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Find the number in the list of values that is closest to the target.
|
||||
* @param {Array.<number>} values The values.
|
||||
* @param {number} target The target value.
|
||||
* @return {number} The closest value.
|
||||
*/
|
||||
goog.text.LoremIpsum.chooseClosest = function(values, target) {
|
||||
var closest = values[0];
|
||||
goog.array.forEach(values, function(value) {
|
||||
if (Math.abs(target - value) < Math.abs(target - closest)) {
|
||||
closest = value;
|
||||
}
|
||||
});
|
||||
|
||||
return closest;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets info about a word used as part of the lorem ipsum algorithm.
|
||||
* @param {string} word The word to check.
|
||||
* @return {Array} A two element array. The first element is the size of the
|
||||
* word. The second element is the delimter used in the word.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.getWordInfo_ = function(word) {
|
||||
var ret;
|
||||
goog.array.some(goog.text.LoremIpsum.DELIMITERS_WORDS_,
|
||||
function (delimiter) {
|
||||
if (goog.string.endsWith(word, delimiter)) {
|
||||
ret = [word.length - delimiter.length, delimiter];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
return ret || [word.length, ''];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Constant used for {@link #randomNormal_}.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.NV_MAGICCONST_ = 4 * Math.exp(-0.5) / Math.sqrt(2.0);
|
||||
|
||||
|
||||
/**
|
||||
* Generates a random number for a normal distribution with the specified
|
||||
* mean and sigma.
|
||||
* @param {number} mu The mean of the distribution.
|
||||
* @param {number} sigma The sigma of the distribution.
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.randomNormal_ = function(mu, sigma) {
|
||||
while (true) {
|
||||
var u1 = Math.random();
|
||||
var u2 = 1.0 - Math.random();
|
||||
var z = goog.text.LoremIpsum.NV_MAGICCONST_ * (u1 - 0.5) / u2;
|
||||
var zz = z * z / 4.0;
|
||||
if (zz <= -Math.log(u2)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mu + z * sigma;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Picks a random element of the array.
|
||||
* @param {Array} array The array to pick from.
|
||||
* @return {*} An element from the array.
|
||||
*/
|
||||
goog.text.LoremIpsum.randomChoice_ = function(array) {
|
||||
return array[goog.math.randomInt(array.length)];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Dictionary of words for lorem ipsum.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.DICT_ =
|
||||
'a ac accumsan ad adipiscing aenean aliquam aliquet amet ante ' +
|
||||
'aptent arcu at auctor augue bibendum blandit class commodo ' +
|
||||
'condimentum congue consectetuer consequat conubia convallis cras ' +
|
||||
'cubilia cum curabitur curae cursus dapibus diam dictum dictumst ' +
|
||||
'dignissim dis dolor donec dui duis egestas eget eleifend elementum ' +
|
||||
'elit eni enim erat eros est et etiam eu euismod facilisi facilisis ' +
|
||||
'fames faucibus felis fermentum feugiat fringilla fusce gravida ' +
|
||||
'habitant habitasse hac hendrerit hymenaeos iaculis id imperdiet ' +
|
||||
'in inceptos integer interdum ipsum justo lacinia lacus laoreet ' +
|
||||
'lectus leo libero ligula litora lobortis lorem luctus maecenas ' +
|
||||
'magna magnis malesuada massa mattis mauris metus mi molestie ' +
|
||||
'mollis montes morbi mus nam nascetur natoque nec neque netus ' +
|
||||
'nibh nisi nisl non nonummy nostra nulla nullam nunc odio orci ' +
|
||||
'ornare parturient pede pellentesque penatibus per pharetra ' +
|
||||
'phasellus placerat platea porta porttitor posuere potenti praesent ' +
|
||||
'pretium primis proin pulvinar purus quam quis quisque rhoncus ' +
|
||||
'ridiculus risus rutrum sagittis sapien scelerisque sed sem semper ' +
|
||||
'senectus sit sociis sociosqu sodales sollicitudin suscipit ' +
|
||||
'suspendisse taciti tellus tempor tempus tincidunt torquent tortor ' +
|
||||
'tristique turpis ullamcorper ultrices ultricies urna ut varius ve ' +
|
||||
'vehicula vel velit venenatis vestibulum vitae vivamus viverra ' +
|
||||
'volutpat vulputate';
|
||||
|
||||
|
||||
/**
|
||||
* A sample to use for generating the distribution of word and sentence lengths
|
||||
* in lorem ipsum.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
goog.text.LoremIpsum.SAMPLE_ =
|
||||
'Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean ' +
|
||||
'commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus ' +
|
||||
'et magnis dis parturient montes, nascetur ridiculus mus. Donec quam ' +
|
||||
'felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla ' +
|
||||
'consequat massa quis enim. Donec pede justo, fringilla vel, aliquet ' +
|
||||
'nec, vulputate eget, arcu. In enim justo, rhoncus ut, imperdiet a, ' +
|
||||
'venenatis vitae, justo. Nullam dictum felis eu pede mollis pretium. ' +
|
||||
'Integer tincidunt. Cras dapibus. Vivamus elementum semper nisi. Aenean ' +
|
||||
'vulputate eleifend tellus. Aenean leo ligula, porttitor eu, consequat ' +
|
||||
'vitae, eleifend ac, enim. Aliquam lorem ante, dapibus in, viverra ' +
|
||||
'quis, feugiat a, tellus. Phasellus viverra nulla ut metus varius ' +
|
||||
'laoreet. Quisque rutrum. Aenean imperdiet. Etiam ultricies nisi vel ' +
|
||||
'augue. Curabitur ullamcorper ultricies nisi. Nam eget dui.\n\n' +
|
||||
|
||||
'Etiam rhoncus. Maecenas tempus, tellus eget condimentum rhoncus, sem ' +
|
||||
'quam semper libero, sit amet adipiscing sem neque sed ipsum. Nam quam ' +
|
||||
'nunc, blandit vel, luctus pulvinar, hendrerit id, lorem. Maecenas nec ' +
|
||||
'odio et ante tincidunt tempus. Donec vitae sapien ut libero venenatis ' +
|
||||
'faucibus. Nullam quis ante. Etiam sit amet orci eget eros faucibus ' +
|
||||
'tincidunt. Duis leo. Sed fringilla mauris sit amet nibh. Donec sodales ' +
|
||||
'sagittis magna. Sed consequat, leo eget bibendum sodales, augue velit ' +
|
||||
'cursus nunc, quis gravida magna mi a libero. Fusce vulputate eleifend ' +
|
||||
'sapien. Vestibulum purus quam, scelerisque ut, mollis sed, nonummy id, ' +
|
||||
'metus. Nullam accumsan lorem in dui. Cras ultricies mi eu turpis ' +
|
||||
'hendrerit fringilla. Vestibulum ante ipsum primis in faucibus orci ' +
|
||||
'luctus et ultrices posuere cubilia Curae; In ac dui quis mi ' +
|
||||
'consectetuer lacinia.\n\n' +
|
||||
|
||||
'Nam pretium turpis et arcu. Duis arcu tortor, suscipit eget, imperdiet ' +
|
||||
'nec, imperdiet iaculis, ipsum. Sed aliquam ultrices mauris. Integer ' +
|
||||
'ante arcu, accumsan a, consectetuer eget, posuere ut, mauris. Praesent ' +
|
||||
'adipiscing. Phasellus ullamcorper ipsum rutrum nunc. Nunc nonummy ' +
|
||||
'metus. Vestibulum volutpat pretium libero. Cras id dui. Aenean ut eros ' +
|
||||
'et nisl sagittis vestibulum. Nullam nulla eros, ultricies sit amet, ' +
|
||||
'nonummy id, imperdiet feugiat, pede. Sed lectus. Donec mollis hendrerit ' +
|
||||
'risus. Phasellus nec sem in justo pellentesque facilisis. Etiam ' +
|
||||
'imperdiet imperdiet orci. Nunc nec neque. Phasellus leo dolor, tempus ' +
|
||||
'non, auctor et, hendrerit quis, nisi.\n\n' +
|
||||
|
||||
'Curabitur ligula sapien, tincidunt non, euismod vitae, posuere ' +
|
||||
'imperdiet, leo. Maecenas malesuada. Praesent congue erat at massa. Sed ' +
|
||||
'cursus turpis vitae tortor. Donec posuere vulputate arcu. Phasellus ' +
|
||||
'accumsan cursus velit. Vestibulum ante ipsum primis in faucibus orci ' +
|
||||
'luctus et ultrices posuere cubilia Curae; Sed aliquam, nisi quis ' +
|
||||
'porttitor congue, elit erat euismod orci, ac placerat dolor lectus quis ' +
|
||||
'orci. Phasellus consectetuer vestibulum elit. Aenean tellus metus, ' +
|
||||
'bibendum sed, posuere ac, mattis non, nunc. Vestibulum fringilla pede ' +
|
||||
'sit amet augue. In turpis. Pellentesque posuere. Praesent turpis.\n\n' +
|
||||
|
||||
'Aenean posuere, tortor sed cursus feugiat, nunc augue blandit nunc, eu ' +
|
||||
'sollicitudin urna dolor sagittis lacus. Donec elit libero, sodales ' +
|
||||
'nec, volutpat a, suscipit non, turpis. Nullam sagittis. Suspendisse ' +
|
||||
'pulvinar, augue ac venenatis condimentum, sem libero volutpat nibh, ' +
|
||||
'nec pellentesque velit pede quis nunc. Vestibulum ante ipsum primis in ' +
|
||||
'faucibus orci luctus et ultrices posuere cubilia Curae; Fusce id ' +
|
||||
'purus. Ut varius tincidunt libero. Phasellus dolor. Maecenas vestibulum ' +
|
||||
'mollis diam. Pellentesque ut neque. Pellentesque habitant morbi ' +
|
||||
'tristique senectus et netus et malesuada fames ac turpis egestas.\n\n' +
|
||||
|
||||
'In dui magna, posuere eget, vestibulum et, tempor auctor, justo. In ac ' +
|
||||
'felis quis tortor malesuada pretium. Pellentesque auctor neque nec ' +
|
||||
'urna. Proin sapien ipsum, porta a, auctor quis, euismod ut, mi. Aenean ' +
|
||||
'viverra rhoncus pede. Pellentesque habitant morbi tristique senectus et ' +
|
||||
'netus et malesuada fames ac turpis egestas. Ut non enim eleifend felis ' +
|
||||
'pretium feugiat. Vivamus quis mi. Phasellus a est. Phasellus magna.\n\n' +
|
||||
|
||||
'In hac habitasse platea dictumst. Curabitur at lacus ac velit ornare ' +
|
||||
'lobortis. Curabitur a felis in nunc fringilla tristique. Morbi mattis ' +
|
||||
'ullamcorper velit. Phasellus gravida semper nisi. Nullam vel sem. ' +
|
||||
'Pellentesque libero tortor, tincidunt et, tincidunt eget, semper nec, ' +
|
||||
'quam. Sed hendrerit. Morbi ac felis. Nunc egestas, augue at ' +
|
||||
'pellentesque laoreet, felis eros vehicula leo, at malesuada velit leo ' +
|
||||
'quis pede. Donec interdum, metus et hendrerit aliquet, dolor diam ' +
|
||||
'sagittis ligula, eget egestas libero turpis vel mi. Nunc nulla. Fusce ' +
|
||||
'risus nisl, viverra et, tempor et, pretium in, sapien. Donec venenatis ' +
|
||||
'vulputate lorem.\n\n' +
|
||||
|
||||
'Morbi nec metus. Phasellus blandit leo ut odio. Maecenas ullamcorper, ' +
|
||||
'dui et placerat feugiat, eros pede varius nisi, condimentum viverra ' +
|
||||
'felis nunc et lorem. Sed magna purus, fermentum eu, tincidunt eu, ' +
|
||||
'varius ut, felis. In auctor lobortis lacus. Quisque libero metus, ' +
|
||||
'condimentum nec, tempor a, commodo mollis, magna. Vestibulum ' +
|
||||
'ullamcorper mauris at ligula. Fusce fermentum. Nullam cursus lacinia ' +
|
||||
'erat. Praesent blandit laoreet nibh.\n\n' +
|
||||
|
||||
'Fusce convallis metus id felis luctus adipiscing. Pellentesque egestas, ' +
|
||||
'neque sit amet convallis pulvinar, justo nulla eleifend augue, ac ' +
|
||||
'auctor orci leo non est. Quisque id mi. Ut tincidunt tincidunt erat. ' +
|
||||
'Etiam feugiat lorem non metus. Vestibulum dapibus nunc ac augue. ' +
|
||||
'Curabitur vestibulum aliquam leo. Praesent egestas neque eu enim. In ' +
|
||||
'hac habitasse platea dictumst. Fusce a quam. Etiam ut purus mattis ' +
|
||||
'mauris sodales aliquam. Curabitur nisi. Quisque malesuada placerat ' +
|
||||
'nisl. Nam ipsum risus, rutrum vitae, vestibulum eu, molestie vel, ' +
|
||||
'lacus.\n\n' +
|
||||
|
||||
'Sed augue ipsum, egestas nec, vestibulum et, malesuada adipiscing, ' +
|
||||
'dui. Vestibulum facilisis, purus nec pulvinar iaculis, ligula mi ' +
|
||||
'congue nunc, vitae euismod ligula urna in dolor. Mauris sollicitudin ' +
|
||||
'fermentum libero. Praesent nonummy mi in odio. Nunc interdum lacus sit ' +
|
||||
'amet orci. Vestibulum rutrum, mi nec elementum vehicula, eros quam ' +
|
||||
'gravida nisl, id fringilla neque ante vel mi. Morbi mollis tellus ac ' +
|
||||
'sapien. Phasellus volutpat, metus eget egestas mollis, lacus lacus ' +
|
||||
'blandit dui, id egestas quam mauris ut lacus. Fusce vel dui. Sed in ' +
|
||||
'libero ut nibh placerat accumsan. Proin faucibus arcu quis ante. In ' +
|
||||
'consectetuer turpis ut velit. Nulla sit amet est. Praesent metus ' +
|
||||
'tellus, elementum eu, semper a, adipiscing nec, purus. Cras risus ' +
|
||||
'ipsum, faucibus ut, ullamcorper id, varius ac, leo. Suspendisse ' +
|
||||
'feugiat. Suspendisse enim turpis, dictum sed, iaculis a, condimentum ' +
|
||||
'nec, nisi. Praesent nec nisl a purus blandit viverra. Praesent ac ' +
|
||||
'massa at ligula laoreet iaculis. Nulla neque dolor, sagittis eget, ' +
|
||||
'iaculis quis, molestie non, velit.\n\n' +
|
||||
|
||||
'Mauris turpis nunc, blandit et, volutpat molestie, porta ut, ligula. ' +
|
||||
'Fusce pharetra convallis urna. Quisque ut nisi. Donec mi odio, faucibus ' +
|
||||
'at, scelerisque quis, convallis in, nisi. Suspendisse non nisl sit amet ' +
|
||||
'velit hendrerit rutrum. Ut leo. Ut a nisl id ante tempus hendrerit. ' +
|
||||
'Proin pretium, leo ac pellentesque mollis, felis nunc ultrices eros, ' +
|
||||
'sed gravida augue augue mollis justo. Suspendisse eu ligula. Nulla ' +
|
||||
'facilisi. Donec id justo. Praesent porttitor, nulla vitae posuere ' +
|
||||
'iaculis, arcu nisl dignissim dolor, a pretium mi sem ut ipsum. ' +
|
||||
'Curabitur suscipit suscipit tellus.\n\n' +
|
||||
|
||||
'Praesent vestibulum dapibus nibh. Etiam iaculis nunc ac metus. Ut id ' +
|
||||
'nisl quis enim dignissim sagittis. Etiam sollicitudin, ipsum eu ' +
|
||||
'pulvinar rutrum, tellus ipsum laoreet sapien, quis venenatis ante ' +
|
||||
'odio sit amet eros. Proin magna. Duis vel nibh at velit scelerisque ' +
|
||||
'suscipit. Curabitur turpis. Vestibulum suscipit nulla quis orci. Fusce ' +
|
||||
'ac felis sit amet ligula pharetra condimentum. Maecenas egestas arcu ' +
|
||||
'quis ligula mattis placerat. Duis lobortis massa imperdiet quam. ' +
|
||||
'Suspendisse potenti.\n\n' +
|
||||
|
||||
'Pellentesque commodo eros a enim. Vestibulum turpis sem, aliquet eget, ' +
|
||||
'lobortis pellentesque, rutrum eu, nisl. Sed libero. Aliquam erat ' +
|
||||
'volutpat. Etiam vitae tortor. Morbi vestibulum volutpat enim. Aliquam ' +
|
||||
'eu nunc. Nunc sed turpis. Sed mollis, eros et ultrices tempus, mauris ' +
|
||||
'ipsum aliquam libero, non adipiscing dolor urna a orci. Nulla porta ' +
|
||||
'dolor. Class aptent taciti sociosqu ad litora torquent per conubia ' +
|
||||
'nostra, per inceptos hymenaeos.\n\n' +
|
||||
|
||||
'Pellentesque dapibus hendrerit tortor. Praesent egestas tristique nibh. ' +
|
||||
'Sed a libero. Cras varius. Donec vitae orci sed dolor rutrum auctor. ' +
|
||||
'Fusce egestas elit eget lorem. Suspendisse nisl elit, rhoncus eget, ' +
|
||||
'elementum ac, condimentum eget, diam. Nam at tortor in tellus interdum ' +
|
||||
'sagittis. Aliquam lobortis. Donec orci lectus, aliquam ut, faucibus ' +
|
||||
'non, euismod id, nulla. Curabitur blandit mollis lacus. Nam adipiscing. ' +
|
||||
'Vestibulum eu odio.\n\n' +
|
||||
|
||||
'Vivamus laoreet. Nullam tincidunt adipiscing enim. Phasellus tempus. ' +
|
||||
'Proin viverra, ligula sit amet ultrices semper, ligula arcu tristique ' +
|
||||
'sapien, a accumsan nisi mauris ac eros. Fusce neque. Suspendisse ' +
|
||||
'faucibus, nunc et pellentesque egestas, lacus ante convallis tellus, ' +
|
||||
'vitae iaculis lacus elit id tortor. Vivamus aliquet elit ac nisl. Fusce ' +
|
||||
'fermentum odio nec arcu. Vivamus euismod mauris. In ut quam vitae ' +
|
||||
'odio lacinia tincidunt. Praesent ut ligula non mi varius sagittis. ' +
|
||||
'Cras sagittis. Praesent ac sem eget est egestas volutpat. Vivamus ' +
|
||||
'consectetuer hendrerit lacus. Cras non dolor. Vivamus in erat ut urna ' +
|
||||
'cursus vestibulum. Fusce commodo aliquam arcu. Nam commodo suscipit ' +
|
||||
'quam. Quisque id odio. Praesent venenatis metus at tortor pulvinar ' +
|
||||
'varius.\n\n';
|
||||
|
||||
/**
|
||||
* Sample that the generated text is based on .
|
||||
* @type {string}
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.sample_ = goog.text.LoremIpsum.SAMPLE_;
|
||||
|
||||
|
||||
/**
|
||||
* Dictionary of words.
|
||||
* @type {string}
|
||||
*/
|
||||
goog.text.LoremIpsum.prototype.dictionary_ = goog.text.LoremIpsum.DICT_;
|
||||
@@ -0,0 +1,652 @@
|
||||
// Copyright 2007 Bob Ippolito. All Rights Reserved.
|
||||
// Modifications Copyright 2009 The Closure Library Authors. All Rights
|
||||
// Reserved.
|
||||
|
||||
/**
|
||||
* @license Portions of this code are from MochiKit, received by
|
||||
* The Closure Authors under the MIT license. All other code is Copyright
|
||||
* 2005-2009 The Closure Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Classes for tracking asynchronous operations and handling the
|
||||
* results. The Deferred object here is patterned after the Deferred object in
|
||||
* the Twisted python networking framework.
|
||||
*
|
||||
* See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html
|
||||
*
|
||||
* Based on the Dojo code which in turn is based on the MochiKit code.
|
||||
*
|
||||
*/
|
||||
|
||||
goog.provide('goog.async.Deferred');
|
||||
goog.provide('goog.async.Deferred.AlreadyCalledError');
|
||||
goog.provide('goog.async.Deferred.CancelledError');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.debug.Error');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Represents the results of an asynchronous operation. A Deferred object
|
||||
* starts with no result, and then gets a result at some point in the future.
|
||||
* @param {Function=} opt_canceller A function that will be called if the
|
||||
* deferred is cancelled.
|
||||
* @param {Object=} opt_defaultScope The default scope to call callbacks with.
|
||||
* @constructor
|
||||
*/
|
||||
goog.async.Deferred = function(opt_canceller, opt_defaultScope) {
|
||||
/**
|
||||
* Entries in the chain are arrays containing a callback, errback, and
|
||||
* optional scope. Callbacks or errbacks may be null.
|
||||
* @type {!Array.<!Array>}
|
||||
* @private
|
||||
*/
|
||||
this.chain_ = [];
|
||||
|
||||
/**
|
||||
* If provided, this is the function to call when the deferred is cancelled.
|
||||
* @type {Function|undefined}
|
||||
* @private
|
||||
*/
|
||||
this.canceller_ = opt_canceller;
|
||||
|
||||
/**
|
||||
* The default scope to execute callbacks in.
|
||||
* @type {Object}
|
||||
* @private
|
||||
*/
|
||||
this.defaultScope_ = opt_defaultScope || null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the deferred has been fired.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.fired_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* Whether the last result in the callback chain was an error.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.hadError_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* The current Deferred result, updated by registered callbacks and errbacks.
|
||||
* @type {*}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.result_;
|
||||
|
||||
|
||||
/**
|
||||
* The number of times this deferred has been paused.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.paused_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* If the deferred was cancelled but it did not have a canceller then this gets
|
||||
* set to true.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.silentlyCancelled_ = false;
|
||||
|
||||
/**
|
||||
* If a callback returns a deferred then this deferred is considered a chained
|
||||
* deferred and once it is chained we cannot add more callbacks.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.chained_ = false;
|
||||
|
||||
|
||||
/**
|
||||
* If an error is thrown during Deferred execution with no errback to catch it,
|
||||
* the error is rethrown after a timeout. Reporting the error after a timeout
|
||||
* allows execution to continue in the calling context.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.unhandledExceptionTimeoutId_;
|
||||
|
||||
|
||||
/**
|
||||
* If this Deferred was created by branch(), this will be the "parent" Deferred.
|
||||
* @type {goog.async.Deferred}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.parent_;
|
||||
|
||||
|
||||
/**
|
||||
* The number of Deferred objects that have been branched off this one. This
|
||||
* will be decremented whenever a branch is fired or cancelled.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.branches_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Cancels a deferred that has not yet received a value. If this Deferred is
|
||||
* paused waiting for a chained Deferred to fire, the chained Deferred will also
|
||||
* be cancelled.
|
||||
*
|
||||
* If this Deferred was created by calling branch() on a parent Deferred with
|
||||
* opt_propagateCancel set to true, the parent may also be cancelled. If
|
||||
* opt_deepCancel is set, cancel() will be called on the parent (as well as any
|
||||
* other ancestors if the parent is also a branch). If one or more branches were
|
||||
* created with opt_propagateCancel set to true, the parent will be cancelled if
|
||||
* cancel() is called on all of those branches.
|
||||
*
|
||||
* @param {boolean=} opt_deepCancel If true, cancels this Deferred's parent even
|
||||
* if cancel() hasn't been called on some of the parent's branches. Has no
|
||||
* effect on a branch without opt_propagateCancel set to true.
|
||||
*/
|
||||
goog.async.Deferred.prototype.cancel = function(opt_deepCancel) {
|
||||
if (!this.hasFired()) {
|
||||
if (this.parent_) {
|
||||
// Get rid of the parent reference before potentially running the parent's
|
||||
// canceller callback to ensure that this cancellation doesn't get
|
||||
// double-counted in any way.
|
||||
var parent = this.parent_;
|
||||
delete this.parent_;
|
||||
if (opt_deepCancel) {
|
||||
parent.cancel(opt_deepCancel);
|
||||
} else {
|
||||
parent.branchCancel_();
|
||||
}
|
||||
}
|
||||
|
||||
if (this.canceller_) {
|
||||
// Call in user-specified scope.
|
||||
this.canceller_.call(this.defaultScope_, this);
|
||||
} else {
|
||||
this.silentlyCancelled_ = true;
|
||||
}
|
||||
if (!this.hasFired()) {
|
||||
this.errback(new goog.async.Deferred.CancelledError(this));
|
||||
}
|
||||
} else if (this.result_ instanceof goog.async.Deferred) {
|
||||
this.result_.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Handle a single branch being cancelled. Once all branches are cancelled, this
|
||||
* Deferred will be cancelled as well.
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.branchCancel_ = function() {
|
||||
this.branches_--;
|
||||
if (this.branches_ <= 0) {
|
||||
this.cancel();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Pauses the deferred.
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.pause_ = function() {
|
||||
this.paused_++;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Resumes a paused deferred.
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.unpause_ = function() {
|
||||
// TODO(arv): Rename
|
||||
this.paused_--;
|
||||
if (this.paused_ == 0 && this.hasFired()) {
|
||||
this.fire_();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called when a dependent deferred fires.
|
||||
* @param {boolean} isSuccess Whether the result is a success or an error.
|
||||
* @param {*} res The result of the dependent deferred.
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.continue_ = function(isSuccess, res) {
|
||||
this.resback_(isSuccess, res);
|
||||
this.unpause_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called when either a success or a failure happens.
|
||||
* @param {boolean} isSuccess Whether the result is a success or an error.
|
||||
* @param {*} res The result.
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.resback_ = function(isSuccess, res) {
|
||||
this.fired_ = true;
|
||||
this.result_ = res;
|
||||
this.hadError_ = !isSuccess;
|
||||
this.fire_();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Verifies that the deferred has not yet been fired.
|
||||
* @private
|
||||
* @throws {Error} If this has already been fired.
|
||||
*/
|
||||
goog.async.Deferred.prototype.check_ = function() {
|
||||
if (this.hasFired()) {
|
||||
if (!this.silentlyCancelled_) {
|
||||
throw new goog.async.Deferred.AlreadyCalledError(this);
|
||||
}
|
||||
this.silentlyCancelled_ = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Record a successful result for this operation, and send the result
|
||||
* to all registered callback functions.
|
||||
* @param {*=} opt_result The result of the operation.
|
||||
*/
|
||||
goog.async.Deferred.prototype.callback = function(opt_result) {
|
||||
this.check_();
|
||||
this.assertNotDeferred_(opt_result);
|
||||
this.resback_(true /* isSuccess */, opt_result);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Record that this operation failed with an error, and send the error
|
||||
* to all registered errback functions.
|
||||
* @param {*=} opt_result The error result of the operation.
|
||||
*/
|
||||
goog.async.Deferred.prototype.errback = function(opt_result) {
|
||||
this.check_();
|
||||
this.assertNotDeferred_(opt_result);
|
||||
this.resback_(false /* isSuccess */, opt_result);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Asserts that an object is not a Deferred.
|
||||
* @param {*} obj The object to test.
|
||||
* @throws {Error} Throws an exception if the object is a Deferred.
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.assertNotDeferred_ = function(obj) {
|
||||
goog.asserts.assert(
|
||||
!(obj instanceof goog.async.Deferred),
|
||||
'Deferred instances can only be chained if they are the result of a ' +
|
||||
'callback');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback function, to be called when a successful result
|
||||
* is available.
|
||||
* @param {!Function} cb The function to be called on a successful result.
|
||||
* @param {Object=} opt_scope An optional scope to call the callback in.
|
||||
* @return {!goog.async.Deferred} The deferred object for chaining.
|
||||
*/
|
||||
goog.async.Deferred.prototype.addCallback = function(cb, opt_scope) {
|
||||
return this.addCallbacks(cb, null, opt_scope);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Register a callback function, to be called if this operation fails.
|
||||
* @param {!Function} eb The function to be called on an unsuccessful result.
|
||||
* @param {Object=} opt_scope An optional scope to call the errback in.
|
||||
* @return {!goog.async.Deferred} The deferred object for chaining.
|
||||
*/
|
||||
goog.async.Deferred.prototype.addErrback = function(eb, opt_scope) {
|
||||
return this.addCallbacks(null, eb, opt_scope);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Registers a callback function and errback function.
|
||||
* @param {Function} cb The function to be called on a successful result.
|
||||
* @param {Function} eb The function to be called on an unsuccessful result.
|
||||
* @param {Object=} opt_scope An optional scope to call the callbacks in.
|
||||
* @return {!goog.async.Deferred} The deferred object for chaining.
|
||||
*/
|
||||
goog.async.Deferred.prototype.addCallbacks = function(cb, eb, opt_scope) {
|
||||
goog.asserts.assert(!this.chained_, 'Chained Deferreds can not be re-used');
|
||||
this.chain_.push([cb, eb, opt_scope]);
|
||||
if (this.hasFired()) {
|
||||
this.fire_();
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Adds another deferred to the end of this deferred's processing chain.
|
||||
*
|
||||
* Use this when you want otherDeferred to be called at the end of
|
||||
* thisDeferred's previous callbacks.
|
||||
*
|
||||
* @param {!goog.async.Deferred} otherDeferred The Deferred to chain.
|
||||
* @return {!goog.async.Deferred} The deferred object for chaining.
|
||||
*/
|
||||
goog.async.Deferred.prototype.chainDeferred = function(otherDeferred) {
|
||||
this.addCallbacks(
|
||||
otherDeferred.callback, otherDeferred.errback, otherDeferred);
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Makes this Deferred wait for otherDeferred to be called, and its preceding
|
||||
* callbacks to be executed, before continuing with the callback sequence.
|
||||
*
|
||||
* This is equivalent to adding a callback that returns otherDeferred, but
|
||||
* doesn't prevent additional callbacks from being added to otherDeferred.
|
||||
*
|
||||
* @param {!goog.async.Deferred} otherDeferred The Deferred to wait for.
|
||||
* @return {!goog.async.Deferred} The deferred object for chaining.
|
||||
*/
|
||||
goog.async.Deferred.prototype.awaitDeferred = function(otherDeferred) {
|
||||
return this.addCallback(goog.bind(otherDeferred.branch, otherDeferred));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create a branch off this Deferred's callback chain, and return it as a new
|
||||
* Deferred. This means that the return value will have the value at the current
|
||||
* point in the callback chain, regardless of any further callbacks added to
|
||||
* this Deferred.
|
||||
*
|
||||
* Additional callbacks added to the original Deferred will not affect the value
|
||||
* of any branches. All branches at the same stage in the callback chain will
|
||||
* receive the same starting value.
|
||||
*
|
||||
* @param {boolean=} opt_propagateCancel If cancel() is called on every child
|
||||
* branch created with opt_propagateCancel, the parent will be cancelled as
|
||||
* well.
|
||||
* @return {!goog.async.Deferred} The deferred value at this point in the
|
||||
* callback chain.
|
||||
*/
|
||||
goog.async.Deferred.prototype.branch = function(opt_propagateCancel) {
|
||||
var d = new goog.async.Deferred();
|
||||
this.chainDeferred(d);
|
||||
if (opt_propagateCancel) {
|
||||
d.parent_ = this;
|
||||
this.branches_++;
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Registers a function as both callback and errback.
|
||||
* @param {!Function} f The function to be called on any result.
|
||||
* @param {Object=} opt_scope An optional scope to call the callbacks in.
|
||||
* @return {!goog.async.Deferred} The deferred object for chaining.
|
||||
*/
|
||||
goog.async.Deferred.prototype.addBoth = function(f, opt_scope) {
|
||||
return this.addCallbacks(f, f, opt_scope);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether callback or errback has been called on this
|
||||
* deferred.
|
||||
*/
|
||||
goog.async.Deferred.prototype.hasFired = function() {
|
||||
return this.fired_;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @param {*} res The current callback result.
|
||||
* @return {boolean} Whether the current result is an error that should cause
|
||||
* registered errbacks to fire. May be overridden by subclasses to handle
|
||||
* special error types.
|
||||
* @protected
|
||||
*/
|
||||
goog.async.Deferred.prototype.isError = function(res) {
|
||||
return res instanceof Error;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @return {boolean} Whether an errback has been registered.
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.hasErrback_ = function() {
|
||||
return goog.array.some(this.chain_, function(chainRow) {
|
||||
// The errback is the second element in the array.
|
||||
return goog.isFunction(chainRow[1]);
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Exhausts the callback sequence once a result is available.
|
||||
* @private
|
||||
*/
|
||||
goog.async.Deferred.prototype.fire_ = function() {
|
||||
if (this.unhandledExceptionTimeoutId_ && this.hasFired() &&
|
||||
this.hasErrback_()) {
|
||||
// It is possible to add errbacks after the Deferred has fired. If a new
|
||||
// errback is added immediately after the Deferred encountered an unhandled
|
||||
// error, but before that error is rethrown, cancel the rethrow.
|
||||
goog.global.clearTimeout(this.unhandledExceptionTimeoutId_);
|
||||
delete this.unhandledExceptionTimeoutId_;
|
||||
}
|
||||
|
||||
if (this.parent_) {
|
||||
this.parent_.branches_--;
|
||||
delete this.parent_;
|
||||
}
|
||||
|
||||
var res = this.result_;
|
||||
var unhandledException = false;
|
||||
var isChained = false;
|
||||
|
||||
while (this.chain_.length && this.paused_ == 0) {
|
||||
var chainEntry = this.chain_.shift();
|
||||
|
||||
var callback = chainEntry[0];
|
||||
var errback = chainEntry[1];
|
||||
var scope = chainEntry[2];
|
||||
|
||||
var f = this.hadError_ ? errback : callback;
|
||||
if (f) {
|
||||
/** @preserveTry */
|
||||
try {
|
||||
var ret = f.call(scope || this.defaultScope_, res);
|
||||
|
||||
// If no result, then use previous result.
|
||||
if (goog.isDef(ret)) {
|
||||
// Bubble up the error as long as the return value hasn't changed.
|
||||
this.hadError_ = this.hadError_ && (ret == res || this.isError(ret));
|
||||
this.result_ = res = ret;
|
||||
}
|
||||
|
||||
if (res instanceof goog.async.Deferred) {
|
||||
isChained = true;
|
||||
this.pause_();
|
||||
}
|
||||
|
||||
} catch (ex) {
|
||||
res = ex;
|
||||
this.hadError_ = true;
|
||||
|
||||
if (!this.hasErrback_()) {
|
||||
// If an error is thrown with no additional errbacks in the queue,
|
||||
// prepare to rethrow the error.
|
||||
unhandledException = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.result_ = res;
|
||||
|
||||
if (isChained && this.paused_) {
|
||||
res.addCallbacks(
|
||||
goog.bind(this.continue_, this, true /* isSuccess */),
|
||||
goog.bind(this.continue_, this, false /* isSuccess */));
|
||||
res.chained_ = true;
|
||||
}
|
||||
|
||||
if (unhandledException) {
|
||||
// Rethrow the unhandled error after a timeout. Execution will continue, but
|
||||
// the error will be seen by global handlers and the user. The throw will
|
||||
// be canceled if another errback is appended before the timeout executes.
|
||||
// The error's original stack trace is preserved where available.
|
||||
this.unhandledExceptionTimeoutId_ = goog.global.setTimeout(function() {
|
||||
throw res;
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deferred that always succeeds.
|
||||
* @param {*} res The result.
|
||||
* @return {!goog.async.Deferred} The deferred object.
|
||||
*/
|
||||
goog.async.Deferred.succeed = function(res) {
|
||||
var d = new goog.async.Deferred();
|
||||
d.callback(res);
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deferred that always fails.
|
||||
* @param {*} res The error result.
|
||||
* @return {!goog.async.Deferred} The deferred object.
|
||||
*/
|
||||
goog.async.Deferred.fail = function(res) {
|
||||
var d = new goog.async.Deferred();
|
||||
d.errback(res);
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a deferred that has already been cancelled.
|
||||
* @return {!goog.async.Deferred} The deferred object.
|
||||
*/
|
||||
goog.async.Deferred.cancelled = function() {
|
||||
var d = new goog.async.Deferred();
|
||||
d.cancel();
|
||||
return d;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Applies a callback to both deferred and non-deferred values, providing a
|
||||
* mechanism to normalize synchronous and asynchronous behavior.
|
||||
*
|
||||
* If the value is non-deferred, the callback will be executed immediately and
|
||||
* an already committed deferred returned.
|
||||
*
|
||||
* If the object is a deferred, it is branched (so the callback doesn't affect
|
||||
* the previous chain) and the callback is added to the new deferred. The
|
||||
* branched deferred is then returned.
|
||||
*
|
||||
* In the following (contrived) example, if <code>isImmediate</code> is true
|
||||
* then 3 is alerted immediately, otherwise 6 is alerted after a 2-second delay.
|
||||
*
|
||||
* <pre>
|
||||
* var value;
|
||||
* if (isImmediate) {
|
||||
* value = 3;
|
||||
* } else {
|
||||
* value = new goog.async.Deferred();
|
||||
* setTimeout(function() { value.callback(6); }, 2000);
|
||||
* }
|
||||
*
|
||||
* var d = goog.async.Deferred.when(value, alert);
|
||||
* </pre>
|
||||
*
|
||||
* @param {*} value Deferred or non-deferred value to pass to the callback.
|
||||
* @param {!Function} callback The callback to execute.
|
||||
* @param {Object=} opt_scope An optional scope to call the callback in.
|
||||
* @return {!goog.async.Deferred}
|
||||
*/
|
||||
goog.async.Deferred.when = function(value, callback, opt_scope) {
|
||||
if (value instanceof goog.async.Deferred) {
|
||||
return value.branch(true).addCallback(callback, opt_scope);
|
||||
} else {
|
||||
return goog.async.Deferred.succeed(value).addCallback(callback, opt_scope);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An error sub class that is used when a deferred has already been called.
|
||||
* @param {!goog.async.Deferred} deferred The deferred object.
|
||||
* @constructor
|
||||
* @extends {goog.debug.Error}
|
||||
*/
|
||||
goog.async.Deferred.AlreadyCalledError = function(deferred) {
|
||||
goog.debug.Error.call(this);
|
||||
|
||||
/**
|
||||
* The deferred that raised this error.
|
||||
* @type {goog.async.Deferred}
|
||||
*/
|
||||
this.deferred = deferred;
|
||||
};
|
||||
goog.inherits(goog.async.Deferred.AlreadyCalledError, goog.debug.Error);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.async.Deferred.AlreadyCalledError.prototype.message =
|
||||
'Deferred has already fired';
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.async.Deferred.AlreadyCalledError.prototype.name = 'AlreadyCalledError';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* An error sub class that is used when a deferred is cancelled.
|
||||
* @param {!goog.async.Deferred} deferred The deferred object.
|
||||
* @constructor
|
||||
* @extends {goog.debug.Error}
|
||||
*/
|
||||
goog.async.Deferred.CancelledError = function(deferred) {
|
||||
goog.debug.Error.call(this);
|
||||
|
||||
/**
|
||||
* The deferred that raised this error.
|
||||
* @type {goog.async.Deferred}
|
||||
*/
|
||||
this.deferred = deferred;
|
||||
};
|
||||
goog.inherits(goog.async.Deferred.CancelledError, goog.debug.Error);
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.async.Deferred.CancelledError.prototype.message = 'Deferred was cancelled';
|
||||
|
||||
|
||||
/** @override */
|
||||
goog.async.Deferred.CancelledError.prototype.name = 'CancelledError';
|
||||
+200
@@ -0,0 +1,200 @@
|
||||
// Copyright 2005 Bob Ippolito. All Rights Reserved.
|
||||
// Modifications Copyright 2009 The Closure Library Authors.
|
||||
// All Rights Reserved.
|
||||
|
||||
/**
|
||||
* Portions of this code are from MochiKit, received by The Closure
|
||||
* Library Authors under the MIT license. All other code is Copyright
|
||||
* 2005-2009 The Closure Library Authors. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Class for tracking multiple asynchronous operations and
|
||||
* handling the results. The DeferredList object here is patterned after the
|
||||
* DeferredList object in the Twisted python networking framework.
|
||||
*
|
||||
* Based on the MochiKit code.
|
||||
*
|
||||
* See: http://twistedmatrix.com/projects/core/documentation/howto/defer.html
|
||||
*
|
||||
* @author brenneman@google.com (Shawn Brenneman)
|
||||
*/
|
||||
|
||||
goog.provide('goog.async.DeferredList');
|
||||
|
||||
goog.require('goog.array');
|
||||
goog.require('goog.async.Deferred');
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs an object that waits on the results of multiple asynchronous
|
||||
* operations and marshals the results. It is itself a <code>Deferred</code>,
|
||||
* and sends results to its registered callback chain. Each instance is single
|
||||
* use and may only fire once.
|
||||
*
|
||||
* Unless overridden by one of the options below, the <code>DeferredList</code>
|
||||
* will wait for a result from every input <code>Deferred</code>. The results
|
||||
* are stored in a list of two-element arrays as <code>[success, result]</code>,
|
||||
* where <code>success</code> is whether that result was from a callback or
|
||||
* errback. Once all results are available, the <code>DeferredList</code>'s
|
||||
* callback chain is invoked with the full result list.
|
||||
*
|
||||
* @param {!Array.<!goog.async.Deferred>} list An array of deferred objects to
|
||||
* wait for.
|
||||
* @param {boolean=} opt_fireOnOneCallback Whether to stop waiting as soon as
|
||||
* one input completes successfully. In this case, the
|
||||
* <code>DeferredList</code>'s callback chain will be called with a two
|
||||
* element array, <code>[index, result]</code>, where <code>index</code>
|
||||
* identifies which input <code>Deferred</code> produced the
|
||||
* <code>result</code>.
|
||||
* @param {boolean=} opt_fireOnOneErrback Whether to stop waiting as soon as one
|
||||
* input reports an error. The error result is passed to the
|
||||
* <code>DeferredList</code>'s error callback chain.
|
||||
* @param {boolean=} opt_consumeErrors When true, will stop propagation of the
|
||||
* error callback chain for input deferred objects. If the failing deferred
|
||||
* has a registered callback after this <code>DeferredList</code>, it will
|
||||
* be called with null instead of an <code>Error</code>.
|
||||
* @param {Function=} opt_canceller A function that will be called if the
|
||||
* deferred list is canceled.
|
||||
* @param {Object=} opt_defaultScope The default scope to call callbacks with.
|
||||
* @constructor
|
||||
* @extends {goog.async.Deferred}
|
||||
*/
|
||||
goog.async.DeferredList = function(list,
|
||||
opt_fireOnOneCallback,
|
||||
opt_fireOnOneErrback,
|
||||
opt_consumeErrors,
|
||||
opt_canceller,
|
||||
opt_defaultScope) {
|
||||
goog.async.Deferred.call(this, opt_canceller, opt_defaultScope);
|
||||
|
||||
/**
|
||||
* The list of Deferred objects to wait for.
|
||||
* @type {!Array.<!goog.async.Deferred>}
|
||||
* @private
|
||||
*/
|
||||
this.list_ = list;
|
||||
|
||||
/**
|
||||
* The stored return values of the Deferred objects.
|
||||
* @type {!Array}
|
||||
* @private
|
||||
*/
|
||||
this.deferredResults_ = [];
|
||||
|
||||
/**
|
||||
* Whether to fire on the first successful callback instead of waiting for
|
||||
* every Deferred to complete.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.fireOnOneCallback_ = !!opt_fireOnOneCallback;
|
||||
|
||||
/**
|
||||
* Whether to fire on the first error result received instead of waiting for
|
||||
* every Deferred to complete.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.fireOnOneErrback_ = !!opt_fireOnOneErrback;
|
||||
|
||||
/**
|
||||
* Whether to stop error propagation on the input Deferred objects. If the
|
||||
* DeferredList sees an error from one of the Deferred inputs, the error will
|
||||
* be captured, and the Deferred will be returned to success state with a null
|
||||
* return value.
|
||||
* @type {boolean}
|
||||
* @private
|
||||
*/
|
||||
this.consumeErrors_ = !!opt_consumeErrors;
|
||||
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
var d = list[i];
|
||||
d.addCallbacks(goog.bind(this.handleCallback_, this, i, true),
|
||||
goog.bind(this.handleCallback_, this, i, false));
|
||||
}
|
||||
|
||||
if (list.length == 0 && !this.fireOnOneCallback_) {
|
||||
this.callback(this.deferredResults_);
|
||||
}
|
||||
};
|
||||
goog.inherits(goog.async.DeferredList, goog.async.Deferred);
|
||||
|
||||
|
||||
/**
|
||||
* The number of input deferred objects that have fired.
|
||||
* @type {number}
|
||||
* @private
|
||||
*/
|
||||
goog.async.DeferredList.prototype.numFinished_ = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Registers the result from an input deferred callback or errback. The result
|
||||
* is returned and may be passed to additional handlers in the callback chain.
|
||||
*
|
||||
* @param {number} index The index of the firing deferred object in the input
|
||||
* list.
|
||||
* @param {boolean} success Whether the result is from a callback or errback.
|
||||
* @param {*} result The result of the callback or errback.
|
||||
* @return {*} The result, to be handled by the next handler in the deferred's
|
||||
* callback chain (if any). If consumeErrors is set, an error result is
|
||||
* replaced with null.
|
||||
* @private
|
||||
*/
|
||||
goog.async.DeferredList.prototype.handleCallback_ = function(index,
|
||||
success,
|
||||
result) {
|
||||
this.numFinished_++;
|
||||
this.deferredResults_[index] = [success, result];
|
||||
|
||||
if (!this.hasFired()) {
|
||||
if (this.fireOnOneCallback_ && success) {
|
||||
this.callback([index, result]);
|
||||
} else if (this.fireOnOneErrback_ && !success) {
|
||||
this.errback(result);
|
||||
} else if (this.numFinished_ == this.list_.length) {
|
||||
this.callback(this.deferredResults_);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.consumeErrors_ && !success) {
|
||||
result = null;
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
|
||||
/** @inheritDoc */
|
||||
goog.async.DeferredList.prototype.errback = function(res) {
|
||||
goog.async.DeferredList.superClass_.errback.call(this, res);
|
||||
// On error, cancel any pending requests.
|
||||
goog.array.forEach(this.list_, function(item) {
|
||||
item.cancel();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a <code>DeferredList</code> that gathers results from multiple
|
||||
* <code>Deferred</code> inputs. If all inputs succeed, the callback is fired
|
||||
* with the list of results as a flat array. If any input fails, the errback is
|
||||
* fired with the error.
|
||||
*
|
||||
* @param {!Array.<!goog.async.Deferred>} list The list of deferred objects to
|
||||
* wait for.
|
||||
* @return {!goog.async.DeferredList} A new deferred list.
|
||||
*/
|
||||
goog.async.DeferredList.gatherResults = function(list) {
|
||||
var d = new goog.async.DeferredList(list, false, true);
|
||||
|
||||
d.addCallback(function(results) {
|
||||
return goog.array.map(results, function(res) {
|
||||
return res[1];
|
||||
});
|
||||
});
|
||||
|
||||
return d;
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @license
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Base OSAPI binding.
|
||||
* This file was copied from
|
||||
* http://svn.apache.org/repos/asf/shindig/trunk/features/src/main/javascript/features/shindig.container/osapi.js
|
||||
* and it's slightly modified for Closure.
|
||||
*/
|
||||
|
||||
goog.provide('goog.osapi');
|
||||
|
||||
|
||||
// Expose osapi from container side.
|
||||
var osapi = osapi || {};
|
||||
goog.exportSymbol('osapi', osapi);
|
||||
|
||||
|
||||
/** @type {Function} */
|
||||
osapi.callback;
|
||||
|
||||
|
||||
/**
|
||||
* Dispatch a JSON-RPC batch request to services defined in the osapi namespace
|
||||
* @param {Array.<Object>} requests an array of rpc requests.
|
||||
*/
|
||||
goog.osapi.handleGadgetRpcMethod = function(requests) {
|
||||
var responses = new Array(requests.length);
|
||||
var callCount = 0;
|
||||
var callback = osapi.callback;
|
||||
var dummy = function(params, apiCallback) {
|
||||
apiCallback({});
|
||||
};
|
||||
for (var i = 0; i < requests.length; i++) {
|
||||
// Don't allow underscores in any part of the method name as a
|
||||
// convention for restricted methods
|
||||
var current = osapi;
|
||||
if (requests[i]['method'].indexOf('_') == -1) {
|
||||
var path = requests[i]['method'].split('.');
|
||||
for (var j = 0; j < path.length; j++) {
|
||||
if (current.hasOwnProperty(path[j])) {
|
||||
current = current[path[j]];
|
||||
} else {
|
||||
// No matching api
|
||||
current = dummy;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
current = dummy;
|
||||
}
|
||||
|
||||
// Execute the call and latch the rpc callback until all
|
||||
// complete
|
||||
current(requests[i]['params'], function(i) {
|
||||
return function(response) {
|
||||
// Put back in json-rpc format
|
||||
responses[i] = {'id': requests[i].id, 'data': response};
|
||||
callCount++;
|
||||
if (callCount == requests.length) {
|
||||
callback(responses);
|
||||
}
|
||||
};
|
||||
}(i));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Initializes container side osapi binding.
|
||||
*/
|
||||
goog.osapi.init = function() {
|
||||
// Container-side binding for the gadgetsrpctransport used by osapi.
|
||||
// Containers add services to the client-side osapi implementation by
|
||||
// defining them in the osapi namespace
|
||||
if (gadgets && gadgets.rpc) { // Only define if gadgets rpc exists.
|
||||
// Register the osapi RPC dispatcher.
|
||||
gadgets.rpc.register('osapi._handleGadgetRpcMethod',
|
||||
/** @type {!Function} */ (goog.osapi.handleGadgetRpcMethod));
|
||||
}
|
||||
};
|
||||
+280
@@ -0,0 +1,280 @@
|
||||
// Copyright 2010 The Closure Library Authors. All Rights Reserved.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS-IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/**
|
||||
* @fileoverview A button that can access the native clipboard. Calls
|
||||
* out to silverlight when it's possible to do so.
|
||||
*
|
||||
* If the current browser does not support native clipboard access,
|
||||
* throw an error.
|
||||
*
|
||||
* @author nicksantos@google.com (Nick Santos)
|
||||
*/
|
||||
|
||||
goog.provide('goog.silverlight.ClipboardButton');
|
||||
goog.provide('goog.silverlight.ClipboardButtonType');
|
||||
goog.provide('goog.silverlight.ClipboardEvent');
|
||||
goog.provide('goog.silverlight.CopyButton');
|
||||
goog.provide('goog.silverlight.PasteButton');
|
||||
goog.provide('goog.silverlight.PasteButtonEvent');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.events.Event');
|
||||
goog.require('goog.math.Size');
|
||||
goog.require('goog.silverlight');
|
||||
goog.require('goog.ui.Component');
|
||||
|
||||
|
||||
/**
|
||||
* A button that can access the native clipboard, via Silverlight.
|
||||
*
|
||||
* If this is not a browser that supports native clipboard access,
|
||||
* throw an error.
|
||||
*
|
||||
* Clients should not instantiate this directly. Instead, use CopyButton
|
||||
* or PasteButton.
|
||||
*
|
||||
* @param {goog.silverlight.ClipboardButtonType} type Copy or Paste.
|
||||
* @param {Function} callback The callback function for copy or paste.
|
||||
* @param {goog.Uri} slResource The URI of ClosureClipboardButton.xap (The
|
||||
* silverlight resource included in this package.)
|
||||
* @param {string} caption Text caption to display as the button's caption.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.ui.Component}
|
||||
*/
|
||||
goog.silverlight.ClipboardButton =
|
||||
function(type, callback, slResource, caption, opt_domHelper) {
|
||||
// In an ideal world, we'd use some sort of transparent overlay over
|
||||
// an HTML-based button. Silverlight doesn't appear to allow this.
|
||||
// Silverlight only allows copy and paste in response to a
|
||||
// silverlight-managed click on a silverlight-rendered button,
|
||||
// and this button cannot be transparent.
|
||||
goog.asserts.assert(
|
||||
goog.silverlight.ClipboardButton.hasClipboardAccess(),
|
||||
'no clipboard access');
|
||||
|
||||
goog.base(this, opt_domHelper);
|
||||
|
||||
/**
|
||||
* The ID of the handler to pass to silverlight.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.callbackId_ = goog.asserts.assertString(
|
||||
goog.silverlight.getHandlerName(callback));
|
||||
|
||||
/**
|
||||
* The button caption.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
this.caption_ = caption;
|
||||
|
||||
/**
|
||||
* The compiled silverlight bundle.
|
||||
* @type {goog.Uri}
|
||||
* @private
|
||||
*/
|
||||
this.slResource_ = slResource;
|
||||
|
||||
/**
|
||||
* @type {goog.silverlight.ClipboardButtonType}
|
||||
* @private
|
||||
*/
|
||||
this.type_ = type;
|
||||
|
||||
/**
|
||||
* @type {goog.math.Size}
|
||||
* @private
|
||||
*/
|
||||
this.size_ = goog.silverlight.ClipboardButton.DEFAULT_SIZE_;
|
||||
};
|
||||
goog.inherits(goog.silverlight.ClipboardButton, goog.ui.Component);
|
||||
|
||||
|
||||
/**
|
||||
* The default size of the button.
|
||||
* @type {goog.math.Size}
|
||||
* @private
|
||||
*/
|
||||
goog.silverlight.ClipboardButton.DEFAULT_SIZE_ = new goog.math.Size(100, 30);
|
||||
|
||||
|
||||
/**
|
||||
* Silverlight components need absolute sizing. Should be set before the
|
||||
* component is rendered.
|
||||
* @param {goog.math.Size} size The size of the component.
|
||||
*/
|
||||
goog.silverlight.ClipboardButton.prototype.setSize = function(size) {
|
||||
this.size_ = size;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates the button's DOM.
|
||||
* @override
|
||||
*/
|
||||
goog.silverlight.ClipboardButton.prototype.createDom = function() {
|
||||
var dom = this.getDomHelper();
|
||||
var element = dom.createDom('div', goog.getCssName('goog-inline-block'));
|
||||
this.setElementInternal(element);
|
||||
|
||||
var source = this.slResource_.toString();
|
||||
goog.silverlight.createObject(
|
||||
source, element, null,
|
||||
{version: '4.0',
|
||||
width: this.size_.width, height: this.size_.height}, null,
|
||||
['buttonType=' + this.type_,
|
||||
'callbackName=' + this.callbackId_,
|
||||
'Content=' + this.caption_].join(','));
|
||||
};
|
||||
|
||||
|
||||
/** @return {boolean} If clipboard access is supported. */
|
||||
goog.silverlight.ClipboardButton.hasClipboardAccess = function() {
|
||||
// TODO(nicksantos): Use IE execCommand if available.
|
||||
return goog.silverlight.isInstalled('4.0');
|
||||
};
|
||||
|
||||
|
||||
/** @inheritDoc */
|
||||
goog.silverlight.ClipboardButton.prototype.disposeInternal = function() {
|
||||
goog.silverlight.disposeHandlerName(this.callbackId_);
|
||||
goog.base(this, 'disposeInternal');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Whether the button should trigger a copy or a paste.
|
||||
* @enum
|
||||
* @private
|
||||
*/
|
||||
goog.silverlight.ClipboardButtonType = {
|
||||
COPY: 1,
|
||||
PASTE: 2
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A clipboard button event. By design, replicates IE's clipboard API.
|
||||
* @param {string} type The event type.
|
||||
* @param {string=} opt_data The data pasted, if this is a paste event.
|
||||
* @constructor
|
||||
* @extends {goog.events.Event}
|
||||
*/
|
||||
goog.silverlight.ClipboardEvent = function(type, opt_data) {
|
||||
goog.base(this, type);
|
||||
|
||||
/**
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
this.data_ = opt_data || null;
|
||||
};
|
||||
goog.inherits(goog.silverlight.ClipboardEvent, goog.events.Event);
|
||||
|
||||
|
||||
/** @return {?string} Data pasted from the clipboard. */
|
||||
goog.silverlight.ClipboardEvent.prototype.getData = function() {
|
||||
return this.data_;
|
||||
};
|
||||
|
||||
|
||||
/** @param {string} data Data to put on the clipboard. */
|
||||
goog.silverlight.ClipboardEvent.prototype.setData = function(data) {
|
||||
this.data_ = data;
|
||||
};
|
||||
|
||||
|
||||
/** @enum {string} */
|
||||
goog.silverlight.ClipboardEventType = {
|
||||
COPY: 'copy',
|
||||
PASTE: 'paste'
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A button that can retrieve contents from the native clipboard.
|
||||
*
|
||||
* @param {goog.Uri} slResource The URI of ClosureClipboardButton.xap (the
|
||||
* silverlight resource included in this package.
|
||||
* @param {string=} opt_caption Text caption to display as the button's caption.
|
||||
* Defaults to 'Paste'.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.silverlight.ClipboardButton}
|
||||
*/
|
||||
goog.silverlight.PasteButton =
|
||||
function(slResource, opt_caption, opt_domHelper) {
|
||||
/** @desc Label for the paste button */
|
||||
var MSG_DEFAULT_PASTE_BUTTON_CAPTION = goog.getMsg('Paste');
|
||||
var caption = opt_caption || MSG_DEFAULT_PASTE_BUTTON_CAPTION;
|
||||
|
||||
goog.base(this,
|
||||
goog.silverlight.ClipboardButtonType.PASTE,
|
||||
goog.bind(this.handlePaste_, this),
|
||||
slResource,
|
||||
caption,
|
||||
opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.silverlight.PasteButton, goog.silverlight.ClipboardButton);
|
||||
|
||||
|
||||
/** @param {string} content The native content that was pasted. */
|
||||
goog.silverlight.PasteButton.prototype.handlePaste_ = function(content) {
|
||||
this.dispatchEvent(
|
||||
new goog.silverlight.ClipboardEvent(
|
||||
goog.silverlight.ClipboardEventType.PASTE,
|
||||
content));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A button that can copy contents.
|
||||
*
|
||||
* @param {goog.Uri} slResource The URI of ClosureClipboardButton.xap (the
|
||||
* silverlight resource included in this package.
|
||||
* @param {string=} opt_caption Text caption to display as the button's caption.
|
||||
* Defaults to 'Copy'.
|
||||
* @param {goog.dom.DomHelper=} opt_domHelper Optional DOM helper, used for
|
||||
* document interaction.
|
||||
* @constructor
|
||||
* @extends {goog.silverlight.ClipboardButton}
|
||||
*/
|
||||
goog.silverlight.CopyButton =
|
||||
function(slResource, opt_caption, opt_domHelper) {
|
||||
/** @desc Label for the copy button */
|
||||
var MSG_DEFAULT_COPY_BUTTON_CAPTION = goog.getMsg('Copy');
|
||||
var caption = opt_caption || MSG_DEFAULT_COPY_BUTTON_CAPTION;
|
||||
|
||||
goog.base(this,
|
||||
goog.silverlight.ClipboardButtonType.COPY,
|
||||
goog.bind(this.handleCopy_, this),
|
||||
slResource,
|
||||
caption,
|
||||
opt_domHelper);
|
||||
};
|
||||
goog.inherits(goog.silverlight.CopyButton, goog.silverlight.ClipboardButton);
|
||||
|
||||
|
||||
/** @return {string} The content to be copied to the clipboard. */
|
||||
goog.silverlight.CopyButton.prototype.handleCopy_ = function() {
|
||||
var event = new goog.silverlight.ClipboardEvent(
|
||||
goog.silverlight.ClipboardEventType.COPY);
|
||||
this.dispatchEvent(event);
|
||||
return event.getData() || '';
|
||||
};
|
||||
@@ -0,0 +1,760 @@
|
||||
/**
|
||||
* @license
|
||||
* Portions of this code are from Silverlight.js under the Ms-PL license and
|
||||
* modified by The Closure Authors.
|
||||
*
|
||||
* Silverlight.js version 4.0.50401.0
|
||||
*
|
||||
* This file is provided by Microsoft as a helper file for websites that
|
||||
* incorporate Silverlight Objects. This file is provided under the Microsoft
|
||||
* Public License available at
|
||||
* http://code.msdn.microsoft.com/silverlightjs/Project/License.aspx.
|
||||
* You may not use or distribute this file or the code in this file except as
|
||||
* expressly permitted under that license.
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Modified version of Silverlight.js to work with
|
||||
* the compiler's advanced modes. The API should be the same, except
|
||||
* that they're namespaced under goog.silverlight instead of Silverlight.
|
||||
*
|
||||
* We've also modified this so that the silverlight bootstrapper
|
||||
* does not run by default. If you need to start up silverlight, you
|
||||
* should explicitly call goog.silverlight.startup().
|
||||
*
|
||||
* @author nicksantos@google.com (Nick Santos) (ported to Closure)
|
||||
*/
|
||||
|
||||
goog.provide('goog.silverlight');
|
||||
|
||||
|
||||
/**
|
||||
* Counter of globalized event handlers
|
||||
* @type {number}
|
||||
*/
|
||||
goog.silverlight._silverlightCount = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Prevents onSilverlightInstalled from being called multiple
|
||||
* times
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.silverlight.__onSilverlightInstalledCalled = false;
|
||||
|
||||
|
||||
/**
|
||||
* Prefix for fwlink URL's
|
||||
* @type {string}
|
||||
*/
|
||||
goog.silverlight.fwlinkRoot = 'http://go2.microsoft.com/fwlink/?LinkID=';
|
||||
|
||||
|
||||
/**
|
||||
* Ensures that only one Installation State event is fired.
|
||||
* @type {boolean}
|
||||
*/
|
||||
goog.silverlight.__installationEventFired = false;
|
||||
|
||||
|
||||
/**
|
||||
* Called by goog.silverlight.getSilverlight to notify the page that a user
|
||||
* has requested the Silverlight installer
|
||||
* @type {Function}
|
||||
*/
|
||||
goog.silverlight.onGetSilverlight = null;
|
||||
|
||||
|
||||
/**
|
||||
* Called by Silverlight.waitForInstallCompletion when the page detects
|
||||
* that Silverlight has been installed. The event handler is not called
|
||||
* in upgrade scenarios.
|
||||
*/
|
||||
goog.silverlight.onSilverlightInstalled = function() {
|
||||
window.location.reload(false);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Checks to see if the correct version is installed.
|
||||
* @param {?string=} version The silverlight version.
|
||||
* @return {boolean} Whether silverlight is installed.
|
||||
*/
|
||||
goog.silverlight.isInstalled = function(version) {
|
||||
if (version == undefined)
|
||||
version = null;
|
||||
|
||||
var isVersionSupported = false;
|
||||
var container = null;
|
||||
|
||||
try {
|
||||
var control = null;
|
||||
var tryNS = false;
|
||||
if (typeof ActiveXObject == 'undefined') {
|
||||
try {
|
||||
control = new ActiveXObject('AgControl.AgControl');
|
||||
if (version === null) {
|
||||
isVersionSupported = true;
|
||||
} else if (control.isVersionSupported(version)) {
|
||||
isVersionSupported = true;
|
||||
}
|
||||
control = null;
|
||||
} catch (e) {
|
||||
tryNS = true;
|
||||
}
|
||||
} else {
|
||||
tryNS = true;
|
||||
}
|
||||
if (tryNS) {
|
||||
var plugin = navigator.plugins['Silverlight Plug-In'];
|
||||
if (plugin) {
|
||||
if (version === null) {
|
||||
isVersionSupported = true;
|
||||
} else {
|
||||
var actualVer = plugin.description;
|
||||
if (actualVer === '1.0.30226.2')
|
||||
actualVer = '2.0.30226.2';
|
||||
var actualVerArray = actualVer.split('.');
|
||||
while (actualVerArray.length > 3) {
|
||||
actualVerArray.pop();
|
||||
}
|
||||
while (actualVerArray.length < 4) {
|
||||
actualVerArray.push(0);
|
||||
}
|
||||
var reqVerArray = version.split('.');
|
||||
while (reqVerArray.length > 4) {
|
||||
reqVerArray.pop();
|
||||
}
|
||||
|
||||
var requiredVersionPart;
|
||||
var actualVersionPart;
|
||||
var index = 0;
|
||||
|
||||
|
||||
do {
|
||||
requiredVersionPart = parseInt(reqVerArray[index], 10);
|
||||
actualVersionPart = parseInt(actualVerArray[index], 10);
|
||||
index++;
|
||||
}
|
||||
while (index < reqVerArray.length &&
|
||||
requiredVersionPart === actualVersionPart);
|
||||
|
||||
if (requiredVersionPart <= actualVersionPart &&
|
||||
!isNaN(requiredVersionPart)) {
|
||||
isVersionSupported = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
isVersionSupported = false;
|
||||
}
|
||||
|
||||
return isVersionSupported;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Occasionally checks for Silverlight installation status. If it
|
||||
* detects that Silverlight has been installed then it calls
|
||||
* Silverlight.onSilverlightInstalled();. This is only supported
|
||||
* if Silverlight was not previously installed on this computer.
|
||||
*/
|
||||
goog.silverlight.waitForInstallCompletion = function() {
|
||||
if (!goog.silverlight.isBrowserRestartRequired &&
|
||||
goog.silverlight.onSilverlightInstalled) {
|
||||
try {
|
||||
navigator.plugins.refresh();
|
||||
} catch (e) {
|
||||
}
|
||||
if (goog.silverlight.isInstalled(null) &&
|
||||
!goog.silverlight.__onSilverlightInstalledCalled) {
|
||||
goog.silverlight.onSilverlightInstalled();
|
||||
goog.silverlight.__onSilverlightInstalledCalled = true;
|
||||
} else {
|
||||
setTimeout(goog.silverlight.waitForInstallCompletion, 3000);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Performs startup tasks.
|
||||
*/
|
||||
goog.silverlight.startup = function() {
|
||||
navigator.plugins.refresh();
|
||||
goog.silverlight.isBrowserRestartRequired =
|
||||
goog.silverlight.isInstalled(null);
|
||||
if (!goog.silverlight.isBrowserRestartRequired) {
|
||||
goog.silverlight.waitForInstallCompletion();
|
||||
if (!goog.silverlight.__installationEventFired) {
|
||||
goog.silverlight.onInstallRequired();
|
||||
goog.silverlight.__installationEventFired = true;
|
||||
}
|
||||
} else if (window.navigator.mimeTypes) {
|
||||
var mimeSL2 = navigator.mimeTypes['application/x-silverlight-2'];
|
||||
var mimeSL2b2 = navigator.mimeTypes['application/x-silverlight-2-b2'];
|
||||
var mimeSL2b1 = navigator.mimeTypes['application/x-silverlight-2-b1'];
|
||||
var mimeHighestBeta = mimeSL2b1;
|
||||
if (mimeSL2b2)
|
||||
mimeHighestBeta = mimeSL2b2;
|
||||
|
||||
if (!mimeSL2 && (mimeSL2b1 || mimeSL2b2)) {
|
||||
if (!goog.silverlight.__installationEventFired) {
|
||||
goog.silverlight.onUpgradeRequired();
|
||||
goog.silverlight.__installationEventFired = true;
|
||||
}
|
||||
} else if (mimeSL2 && mimeHighestBeta) {
|
||||
if (mimeSL2.enabledPlugin &&
|
||||
mimeHighestBeta.enabledPlugin) {
|
||||
if (mimeSL2.enabledPlugin.description !=
|
||||
mimeHighestBeta.enabledPlugin.description) {
|
||||
if (!goog.silverlight.__installationEventFired) {
|
||||
goog.silverlight.onRestartRequired();
|
||||
goog.silverlight.__installationEventFired = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Inserts a Silverlight <object> tag or installation experience into
|
||||
* the HTML DOM based on the current installed state of
|
||||
* goog.silverlight.
|
||||
* @param {string} source The source file.
|
||||
* @param {Element} parentElement The place to render the tag.
|
||||
* @param {(string|null)=} opt_id A DOM id for the tag.
|
||||
* @param {Object=} opt_properties Properties for the tag. One of these should
|
||||
* be version, to get the correct version of silverlight.
|
||||
* @param {Array.<Function>=} opt_events An array of functions, keyed by
|
||||
* the name of the event that they should be listening on (e.g.,
|
||||
* "onload"). What a weird API. The keys should be quoted if you're
|
||||
* compiling with property renaming on.
|
||||
* @param {string=} opt_initParams The initParams property of SilverlightPlugin.
|
||||
* @param {Object=} opt_userContext Context to fire the events in. doesn't
|
||||
* really work correctly. Use goog.bind instead.
|
||||
* @return {?string} The html for the object, if no rendering parent
|
||||
* was specified.
|
||||
*/
|
||||
goog.silverlight.createObject =
|
||||
function(source, parentElement, opt_id, opt_properties,
|
||||
opt_events, opt_initParams, opt_userContext) {
|
||||
var slPluginHelper = {};
|
||||
var slProperties = opt_properties;
|
||||
var slEvents = opt_events;
|
||||
|
||||
slPluginHelper.version = slProperties.version;
|
||||
slProperties.source = source;
|
||||
slPluginHelper.alt = slProperties.alt;
|
||||
|
||||
// Rename properties to their tag property names. For backwards compatibility
|
||||
// with goog.silverlight.js version 1.0
|
||||
if (opt_initParams)
|
||||
slProperties.initParams = opt_initParams;
|
||||
if (slProperties.isWindowless && !slProperties.windowless)
|
||||
slProperties.windowless = slProperties.isWindowless;
|
||||
if (slProperties.framerate && !slProperties.maxFramerate)
|
||||
slProperties.maxFramerate = slProperties.framerate;
|
||||
if (opt_id && !slProperties.id)
|
||||
slProperties.id = opt_id;
|
||||
|
||||
// remove elements which are not to be added to the instantiation tag
|
||||
delete slProperties.ignoreBrowserVer;
|
||||
delete slProperties.inplaceInstallPrompt;
|
||||
delete slProperties.version;
|
||||
delete slProperties.isWindowless;
|
||||
delete slProperties.framerate;
|
||||
delete slProperties.data;
|
||||
delete slProperties.src;
|
||||
delete slProperties.alt;
|
||||
|
||||
|
||||
// detect that the correct version of Silverlight is installed, else
|
||||
// display install
|
||||
var slPluginHtml;
|
||||
if (goog.silverlight.isInstalled(slPluginHelper.version)) {
|
||||
// move unknown events to the slProperties array
|
||||
for (var name in slEvents) {
|
||||
if (slEvents[name]) {
|
||||
if (name == 'onLoad' && typeof slEvents[name] == 'function' &&
|
||||
slEvents[name].length != 1) {
|
||||
var onLoadHandler = slEvents[name];
|
||||
slEvents[name] = function(sender) {
|
||||
return onLoadHandler(
|
||||
document.getElementById(/** @type {string} */ (opt_id)),
|
||||
opt_userContext,
|
||||
sender);
|
||||
};
|
||||
}
|
||||
var handlerName = goog.silverlight.getHandlerName(slEvents[name]);
|
||||
if (handlerName != null) {
|
||||
slProperties[name] = handlerName;
|
||||
slEvents[name] = null;
|
||||
} else {
|
||||
throw 'typeof events.' + name + " must be 'function' or 'string'";
|
||||
}
|
||||
}
|
||||
}
|
||||
slPluginHtml = goog.silverlight.buildHtml(slProperties);
|
||||
} else {
|
||||
// The control could not be instantiated. Show the installation prompt
|
||||
slPluginHtml = goog.silverlight.buildPromptHtml(slPluginHelper);
|
||||
}
|
||||
|
||||
// insert or return the HTML
|
||||
if (parentElement) {
|
||||
parentElement.innerHTML = slPluginHtml;
|
||||
} else {
|
||||
return slPluginHtml;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Create HTML that instantiates the control
|
||||
* @param {Object} slProperties The properties of the object.
|
||||
* @return {string} The HTML for the object.
|
||||
*/
|
||||
goog.silverlight.buildHtml = function(slProperties) {
|
||||
var htmlBuilder = [];
|
||||
|
||||
htmlBuilder.push(
|
||||
'<object type=\"application/x-silverlight\" ' +
|
||||
'data="data:application/x-silverlight,"');
|
||||
if (slProperties.id != null) {
|
||||
htmlBuilder.push(
|
||||
' id="' +
|
||||
goog.silverlight.htmlAttributeEncode(slProperties.id) + '"');
|
||||
}
|
||||
if (slProperties.width != null) {
|
||||
htmlBuilder.push(' width="' + slProperties.width + '"');
|
||||
}
|
||||
if (slProperties.height != null) {
|
||||
htmlBuilder.push(' height="' + slProperties.height + '"');
|
||||
}
|
||||
htmlBuilder.push(' >');
|
||||
|
||||
delete slProperties.id;
|
||||
delete slProperties.width;
|
||||
delete slProperties.height;
|
||||
|
||||
for (var name in slProperties) {
|
||||
if (slProperties[name]) {
|
||||
htmlBuilder.push(
|
||||
'<param name="' + goog.silverlight.htmlAttributeEncode(name) +
|
||||
'" value="' +
|
||||
goog.silverlight.htmlAttributeEncode(slProperties[name]) +
|
||||
'" />');
|
||||
}
|
||||
}
|
||||
htmlBuilder.push('<\/object>');
|
||||
return htmlBuilder.join('');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* takes a single parameter of all createObject
|
||||
* parameters enclosed in {}
|
||||
* @param {{source: string,
|
||||
* parentElement: Element,
|
||||
* id: string,
|
||||
* properties: Object,
|
||||
* events: Array.<Function>,
|
||||
* initParams: string,
|
||||
* context: Object}} params Named parameters of createObject.
|
||||
* @return {?string} See #createObject.
|
||||
*/
|
||||
goog.silverlight.createObjectEx = function(params) {
|
||||
// stupid hack to make compiler stop complaining.
|
||||
var unused = {properties: 0, events: 0, context: 0};
|
||||
|
||||
var parameters = params;
|
||||
var html = goog.silverlight.createObject(
|
||||
parameters.source, parameters.parentElement, parameters.id,
|
||||
parameters.properties, parameters.events, parameters.initParams,
|
||||
parameters.context);
|
||||
if (parameters.parentElement == null) {
|
||||
return html;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Builds the HTML to prompt the user to download and install Silverlight
|
||||
*
|
||||
* @param {{version: string, alt: string}} slPluginHelper Alt is an alternate
|
||||
* link to silverlight. If no alternate is specified, a default will
|
||||
* be used with the given version number.
|
||||
* @return {string} The UI.
|
||||
*/
|
||||
goog.silverlight.buildPromptHtml = function(slPluginHelper) {
|
||||
var slPluginHtml = '';
|
||||
var urlRoot = goog.silverlight.fwlinkRoot;
|
||||
var version = slPluginHelper.version;
|
||||
if (slPluginHelper.alt) {
|
||||
slPluginHtml = slPluginHelper.alt;
|
||||
} else {
|
||||
if (!version) {
|
||||
version = '';
|
||||
}
|
||||
slPluginHtml =
|
||||
"<a href='javascript:goog.silverlight.getSilverlight(\"{1}\");' " +
|
||||
"style='text-decoration: none;'><img src='{2}' " +
|
||||
"alt='Get Microsoft Silverlight' style='border-style: none'/></a>";
|
||||
slPluginHtml = slPluginHtml.replace('{1}', version);
|
||||
slPluginHtml = slPluginHtml.replace('{2}', urlRoot + '108181');
|
||||
}
|
||||
|
||||
return slPluginHtml;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Navigates the browser to the appropriate Silverlight installer
|
||||
* @param {string} version The silverlight version.
|
||||
*/
|
||||
goog.silverlight.getSilverlight = function(version) {
|
||||
if (goog.silverlight.onGetSilverlight) {
|
||||
goog.silverlight.onGetSilverlight();
|
||||
}
|
||||
|
||||
var shortVer = '';
|
||||
var reqVerArray = String(version).split('.');
|
||||
if (reqVerArray.length > 1) {
|
||||
var majorNum = parseInt(reqVerArray[0], 10);
|
||||
if (isNaN(majorNum) || majorNum < 2) {
|
||||
shortVer = '1.0';
|
||||
} else {
|
||||
shortVer = reqVerArray[0] + '.' + reqVerArray[1];
|
||||
}
|
||||
}
|
||||
|
||||
var verArg = '';
|
||||
|
||||
if (shortVer.match(/^\d+\056\d+$/)) {
|
||||
verArg = '&v=' + shortVer;
|
||||
}
|
||||
|
||||
goog.silverlight.followFWLink('149156' + verArg);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Navigates to a url based on fwlinkid
|
||||
* @param {string} linkid The link suffix.
|
||||
*/
|
||||
goog.silverlight.followFWLink = function(linkid) {
|
||||
top.location = goog.silverlight.fwlinkRoot + String(linkid);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encodes special characters in input strings as charcodes
|
||||
* @param {?string} strInput String to encode, or null. If null
|
||||
* is passed, we give null right back.
|
||||
* @return {?string} The encoded string, or null.
|
||||
*/
|
||||
goog.silverlight.htmlAttributeEncode = function(strInput) {
|
||||
var c;
|
||||
var retVal = '';
|
||||
|
||||
if (strInput == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (var cnt = 0; cnt < strInput.length; cnt++) {
|
||||
c = strInput.charCodeAt(cnt);
|
||||
|
||||
if (((c > 96) && (c < 123)) ||
|
||||
((c > 64) && (c < 91)) ||
|
||||
((c > 43) && (c < 58) && (c != 47)) ||
|
||||
(c == 95)) {
|
||||
retVal = retVal + String.fromCharCode(c);
|
||||
} else {
|
||||
retVal = retVal + '&#' + c + ';';
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Default error handling function
|
||||
* @param {SilverlightDependencyObject} sender dep object.
|
||||
* @param {SilverlightErrorEventArgs} args The event.
|
||||
*/
|
||||
goog.silverlight.defaultErrorHandler = function(sender, args) {
|
||||
var iErrorCode;
|
||||
var errorType = args.errorType;
|
||||
|
||||
iErrorCode = args.errorCode;
|
||||
|
||||
var errMsg = '\nSilverlight error message \n';
|
||||
|
||||
errMsg += 'ErrorCode: ' + iErrorCode + '\n';
|
||||
|
||||
|
||||
errMsg += 'ErrorType: ' + errorType + ' \n';
|
||||
errMsg += 'Message: ' + args.errorMessage + ' \n';
|
||||
|
||||
if (errorType == 'ParserError') {
|
||||
errMsg += 'XamlFile: ' + args.xamlFile + ' \n';
|
||||
errMsg += 'Line: ' + args.lineNumber + ' \n';
|
||||
errMsg += 'Position: ' + args.charPosition + ' \n';
|
||||
} else if (errorType == 'RuntimeError') {
|
||||
if (args.lineNumber != 0) {
|
||||
errMsg += 'Line: ' + args.lineNumber + ' \n';
|
||||
errMsg += 'Position: ' + args.charPosition + ' \n';
|
||||
}
|
||||
errMsg += 'MethodName: ' + args.methodName + ' \n';
|
||||
}
|
||||
alert(errMsg);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Releases event handler resources when the page is unloaded
|
||||
*/
|
||||
goog.silverlight.__cleanup = function() {
|
||||
for (var i = goog.silverlight._silverlightCount - 1; i >= 0; i--) {
|
||||
goog.global['__closure_slEvent' + i] = null;
|
||||
}
|
||||
goog.silverlight._silverlightCount = 0;
|
||||
if (window.removeEventListener) {
|
||||
window.removeEventListener('unload', goog.silverlight.__cleanup, false);
|
||||
} else {
|
||||
window.detachEvent('onunload', goog.silverlight.__cleanup);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generates named event handlers for delegates.
|
||||
*
|
||||
* Attaches them to the global window with the name __closure_slEvent +
|
||||
* a unique id.
|
||||
*
|
||||
* @param {Function|string} handler A function to export.
|
||||
* @return {?string} The name of the handler.
|
||||
*/
|
||||
goog.silverlight.getHandlerName = function(handler) {
|
||||
var handlerName = '';
|
||||
if (typeof handler == 'string') {
|
||||
handlerName = handler;
|
||||
} else if (typeof handler == 'function') {
|
||||
if (goog.silverlight._silverlightCount == 0) {
|
||||
if (window.addEventListener) {
|
||||
window.addEventListener('unload', goog.silverlight.__cleanup, false);
|
||||
} else {
|
||||
window.attachEvent('onunload', goog.silverlight.__cleanup);
|
||||
}
|
||||
}
|
||||
var count = goog.silverlight._silverlightCount++;
|
||||
handlerName = '__closure_slEvent' + count;
|
||||
|
||||
goog.global[handlerName] = handler;
|
||||
} else {
|
||||
handlerName = null;
|
||||
}
|
||||
return handlerName;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Frees a handler created by getHandlerName.
|
||||
* @param {string} handlerName A handler name.
|
||||
*/
|
||||
goog.silverlight.disposeHandlerName = function(handlerName) {
|
||||
delete goog.global[handlerName];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called by version verification control to notify the page that
|
||||
* an appropriate build of Silverlight is available. The page
|
||||
* should respond by injecting the appropriate Silverlight control
|
||||
*/
|
||||
goog.silverlight.onRequiredVersionAvailable = function() {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called by version verification control to notify the page that
|
||||
* an appropriate build of Silverlight is installed but not loaded.
|
||||
* The page should respond by injecting a clear and visible
|
||||
* "Thanks for installing. Please restart your browser and return
|
||||
* to mysite.com" or equivalent into the browser DOM
|
||||
*/
|
||||
goog.silverlight.onRestartRequired = function() {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called by version verification control to notify the page that
|
||||
* Silverlight must be upgraded. The page should respond by
|
||||
* injecting a clear, visible, and actionable upgrade message into
|
||||
* the DOM. The message must inform the user that they need to
|
||||
* upgrade Silverlight to use the page. They are already somewhat
|
||||
* familiar with the Silverlight product when they encounter this.
|
||||
* Silverlight should be mentioned so the user expects to see that
|
||||
* string in the installer UI. However, the Silverlight-powered
|
||||
* application should be the focus of the solicitation. The user
|
||||
* wants the app. Silverlight is a means to the app.
|
||||
*
|
||||
* The upgrade solicitation will have a button that directs
|
||||
* the user to the Silverlight installer. Upon click the button
|
||||
* should both kick off a download of the installer URL and replace
|
||||
* the Upgrade text with "Thanks for downloading. When the upgarde
|
||||
* is complete please restart your browser and return to
|
||||
* mysite.com" or equivalent.
|
||||
*
|
||||
* Note: For a more interesting upgrade UX we can use Silverlight
|
||||
* 1.0-style XAML for this upgrade experience. Contact PiotrP for
|
||||
* details.
|
||||
*/
|
||||
goog.silverlight.onUpgradeRequired = function() {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Called by goog.silverlight.checkInstallStatus to notify the page
|
||||
* that Silverlight has not been installed by this user.
|
||||
* The page should respond by
|
||||
* injecting a clear, visible, and actionable upgrade message into
|
||||
* the DOM. The message must inform the user that they need to
|
||||
* download and install components needed to use the page.
|
||||
* Silverlight should be mentioned so the user expects to see that
|
||||
* string in the installer UI. However, the Silverlight-powered
|
||||
* application should be the focus of the solicitation. The user
|
||||
* wants the app. Silverlight is a means to the app.
|
||||
*
|
||||
* The installation solicitation will have a button that directs
|
||||
* the user to the Silverlight installer. Upon click the button
|
||||
* should both kick off a download of the installer URL and replace
|
||||
* the Upgrade text with "Thanks for downloading. When installation
|
||||
* is complete you may need to refresh the page to view this
|
||||
* content" or equivalent.
|
||||
*/
|
||||
goog.silverlight.onInstallRequired = function() {
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function should be called at the beginning of a web page's
|
||||
* Silverlight error handler. It will determine if the required
|
||||
* version of Silverlight is installed and available in the
|
||||
* current process.
|
||||
*
|
||||
* During its execution the function will trigger one of the
|
||||
* Silverlight installation state events, if appropriate.
|
||||
*
|
||||
* Sender and Args should be passed through from the calling
|
||||
* onError handler's parameters.
|
||||
*
|
||||
* The associated Sivlerlight <object> tag must have
|
||||
* minRuntimeVersion set and should have autoUpgrade set to false.
|
||||
*
|
||||
* @param {SilverlightDependencyObject} sender The sender.
|
||||
* @param {SilverlightErrorEventArgs} args The event arguments.
|
||||
* @return {boolean} Is the version available?
|
||||
*/
|
||||
goog.silverlight.isVersionAvailableOnError = function(sender, args) {
|
||||
var retVal = false;
|
||||
try {
|
||||
if (args.errorCode == 8001 && !goog.silverlight.__installationEventFired) {
|
||||
goog.silverlight.onUpgradeRequired();
|
||||
goog.silverlight.__installationEventFired = true;
|
||||
} else if (args.errorCode == 8002 &&
|
||||
!goog.silverlight.__installationEventFired) {
|
||||
goog.silverlight.onRestartRequired();
|
||||
goog.silverlight.__installationEventFired = true;
|
||||
}
|
||||
// this handles upgrades from 1.0. That control did not
|
||||
// understand the minRuntimeVerison parameter. It also
|
||||
// did not know how to parse XAP files, so would throw
|
||||
// Parse Error (5014). A Beta 2 control may throw 2106
|
||||
else if (args.errorCode == 5014 || args.errorCode == 2106) {
|
||||
if (goog.silverlight.__verifySilverlight2UpgradeSuccess(args.getHost())) {
|
||||
retVal = true;
|
||||
}
|
||||
} else {
|
||||
retVal = true;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This function should be called at the beginning of a web page's
|
||||
* Silverlight onLoad handler. It will determine if the required
|
||||
* version of Silverlight is installed and available in the
|
||||
* current process.
|
||||
*
|
||||
* During its execution the function will trigger one of the
|
||||
* Silverlight installation state events, if appropriate.
|
||||
*
|
||||
* Sender should be passed through from the calling
|
||||
* onError handler's parameters.
|
||||
*
|
||||
* The associated Sivlerlight <object> tag must have
|
||||
* minRuntimeVersion set and should have autoUpgrade set to false.
|
||||
*
|
||||
* @param {SilverlightDependencyObject} sender The dep object.
|
||||
* @return {boolean} Is the version available?
|
||||
*/
|
||||
goog.silverlight.isVersionAvailableOnLoad = function(sender) {
|
||||
var retVal = false;
|
||||
try {
|
||||
if (goog.silverlight.__verifySilverlight2UpgradeSuccess(sender.getHost())) {
|
||||
retVal = true;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* This internal function helps identify installation state by
|
||||
* taking advantage of behavioral differences between the
|
||||
* 1.0 and 2.0 releases of goog.silverlight.
|
||||
* @param {SilverlightPlugin} host The dep object.
|
||||
* @return {boolean} If verification succeeded.
|
||||
*/
|
||||
goog.silverlight.__verifySilverlight2UpgradeSuccess = function(host) {
|
||||
var retVal = false;
|
||||
var version = '4.0.50401';
|
||||
var installationEvent = null;
|
||||
|
||||
try {
|
||||
if (host.isVersionSupported(version + '.99')) {
|
||||
installationEvent = goog.silverlight.onRequiredVersionAvailable;
|
||||
retVal = true;
|
||||
} else if (host.isVersionSupported(version + '.0')) {
|
||||
installationEvent = goog.silverlight.onRestartRequired;
|
||||
} else {
|
||||
installationEvent = goog.silverlight.onUpgradeRequired;
|
||||
}
|
||||
|
||||
if (installationEvent && !goog.silverlight.__installationEventFired) {
|
||||
installationEvent();
|
||||
goog.silverlight.__installationEventFired = true;
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
return retVal;
|
||||
};
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/**
|
||||
* @license
|
||||
* Portions of this code are from Silverlight.supportedUserAgent.js
|
||||
* under the Ms-PL license.
|
||||
*
|
||||
* Silverlight.supportedUserAgent.js version 4.0.50401.0
|
||||
*
|
||||
* This file is provided by Microsoft as a helper file for websites that
|
||||
* incorporate Silverlight Objects. This file is provided under the Microsoft
|
||||
* Public License available at
|
||||
* http://code.msdn.microsoft.com/SLsupportedUA/Project/License.aspx.
|
||||
* You may not use or distribute this file or the code in this file except as
|
||||
* expressly permitted under that license.
|
||||
*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Modified version of Silverlight.supportedUserAgent.js
|
||||
* to work with the compiler's advanced modes. The API should be the same,
|
||||
* except that they're namespaced under goog.silverlight
|
||||
* instead of Silverlight.
|
||||
*
|
||||
* @author nicksantos@google.com (Nick Santos) (ported to Closure)
|
||||
*/
|
||||
|
||||
goog.provide('goog.silverlight.supportedUserAgent');
|
||||
|
||||
|
||||
/**
|
||||
* NOTE: This function is strongly tied to current implementations of web
|
||||
* browsers. The implementation of this function will change over time to
|
||||
* account for new Web browser developments. Visit
|
||||
* http://code.msdn.microsoft.com/SLsupportedUA often to ensure that you have
|
||||
* the latest version.
|
||||
*
|
||||
* Determines if the client browser is supported by Silverlight.
|
||||
*
|
||||
* @param {string} version
|
||||
* determines if a particular version of Silverlight supports
|
||||
* this browser. Acceptable values are "1.0" and "2.0".
|
||||
* @param {string=} userAgent
|
||||
* optional. User Agent string to be analized. If null then the
|
||||
* current browsers user agent string will be used.
|
||||
*
|
||||
* @return {boolean} Whether the user agent is supported.
|
||||
*/
|
||||
goog.silverlight.supportedUserAgent = function(version, userAgent) {
|
||||
try {
|
||||
var ua = null;
|
||||
|
||||
if (userAgent) {
|
||||
ua = userAgent;
|
||||
} else {
|
||||
ua = window.navigator.userAgent;
|
||||
}
|
||||
|
||||
var slua = {OS: 'Unsupported', Browser: 'Unsupported'};
|
||||
|
||||
//Silverlight does not support pre-Windows NT platforms
|
||||
if (ua.indexOf('Windows NT') >= 0 ||
|
||||
ua.indexOf('Mozilla/4.0 (compatible; MSIE 6.0)') >= 0) {
|
||||
slua.OS = 'Windows';
|
||||
} else if (ua.indexOf('PPC Mac OS X') >= 0) {
|
||||
slua.OS = 'MacPPC';
|
||||
} else if (ua.indexOf('Intel Mac OS X') >= 0) {
|
||||
slua.OS = 'MacIntel';
|
||||
} else if (ua.indexOf('Linux') >= 0) {
|
||||
slua.OS = 'Linux';
|
||||
}
|
||||
|
||||
if (slua.OS != 'Unsupported') {
|
||||
if (ua.indexOf('MSIE') >= 0) {
|
||||
if (navigator.userAgent.indexOf('Win64') == -1) {
|
||||
if (parseInt(ua.split('MSIE')[1], 10) >= 6) {
|
||||
slua.Browser = 'MSIE';
|
||||
}
|
||||
}
|
||||
} else if (ua.indexOf('Firefox') >= 0) {
|
||||
var versionArr = ua.split('Firefox/')[1].split('.');
|
||||
var major = parseInt(versionArr[0], 10);
|
||||
if (major >= 2) {
|
||||
slua.Browser = 'Firefox';
|
||||
} else {
|
||||
var minor = parseInt(versionArr[1], 10);
|
||||
if ((major == 1) && (minor >= 5)) {
|
||||
slua.Browser = 'Firefox';
|
||||
}
|
||||
}
|
||||
} else if (ua.indexOf('Chrome') >= 0) {
|
||||
slua.Browser = 'Chrome';
|
||||
} else if (ua.indexOf('Safari') >= 0) {
|
||||
slua.Browser = 'Safari';
|
||||
}
|
||||
}
|
||||
|
||||
var sl_version = parseInt(version, 10);
|
||||
|
||||
// detect all unsupported platform combinations (IE on Mac, Safari on Win)
|
||||
var supUA = (!(// Unsupported OS
|
||||
slua.OS == 'Unsupported' ||
|
||||
// Unsupported Browser
|
||||
slua.Browser == 'Unsupported' ||
|
||||
// Safari is not supported on Windows
|
||||
(slua.OS == 'Windows' && slua.Browser == 'Safari') ||
|
||||
// IE is not supported on Mac
|
||||
(slua.OS.indexOf('Mac') >= 0 && slua.Browser == 'MSIE') ||
|
||||
// Google Chrome is not supported on Mac
|
||||
(slua.OS.indexOf('Mac') >= 0 && slua.Browser == 'Chrome')));
|
||||
|
||||
// Detection of specific platform/versions
|
||||
|
||||
// Google Chrome only on Windows and only in SL4+
|
||||
if ((slua.OS.indexOf('Windows') >= 0 &&
|
||||
slua.Browser == 'Chrome' && sl_version < 4)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// detect unsupported MacPPC platforms (basically anything but 1.0)
|
||||
if ((slua.OS == 'MacPPC') && (sl_version > 1)) {
|
||||
return ((supUA && (slua.OS != 'MacPPC')));
|
||||
}
|
||||
|
||||
// detect unsupported Linux platforms per Moonlight
|
||||
if ((slua.OS == 'Linux') && (sl_version > 2)) {
|
||||
return ((supUA && (slua.OS != 'Linux')));
|
||||
}
|
||||
|
||||
// detect other unsupported 1.0 platforms (win2k)
|
||||
if (version == '1.0') {
|
||||
return (supUA && (ua.indexOf('Windows NT 5.0') < 0));
|
||||
} else {
|
||||
return (supUA);
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user