diff --git a/appengine/redirect.html b/appengine/redirect.html index b35021f47..63d892d71 100644 --- a/appengine/redirect.html +++ b/appengine/redirect.html @@ -90,6 +90,8 @@ if (loc.match('/demos/fixed/')) { loc = 'https://google.github.io/blockly-samples/examples/pitch-field-demo/'; } else if (loc.match('/demos/mirror/')) { loc = 'https://google.github.io/blockly-samples/examples/mirror-demo/'; +} else if (loc.match('/demos/plane/')) { + loc = 'https://google.github.io/blockly-samples/examples/plane-demo/'; } location = loc; diff --git a/demos/index.html b/demos/index.html index d97b89c51..28d6e2f1d 100644 --- a/demos/index.html +++ b/demos/index.html @@ -197,18 +197,6 @@ - - - - - - - -
Plane
-
Using Closure Templates to support 35 languages.
- - - diff --git a/demos/plane/README.txt b/demos/plane/README.txt deleted file mode 100644 index 944448fd6..000000000 --- a/demos/plane/README.txt +++ /dev/null @@ -1,26 +0,0 @@ -This Blockly demo uses Closure Templates to create a multilingual application. -Any changes to the template.soy file require a recompile. Here is the command -to generate a quick English version for debugging: - -java -jar soy/SoyToJsSrcCompiler.jar --outputPathFormat generated/en.js --srcs template.soy - -To generate a full set of language translations, first extract all the strings -from template.soy using this command: - -java -jar soy/SoyMsgExtractor.jar --outputFile xlf/extracted_msgs.xlf template.soy - -This generates xlf/extracted_msgs.xlf, which may then be used by any -XLIFF-compatible translation console to generate a set of files with the -translated strings. These should be placed in the xlf directory. - -Finally, generate all the language versions with this command: - -java -jar soy/SoyToJsSrcCompiler.jar --locales ar,be-tarask,br,ca,da,de,el,en,es,fa,fr,he,hrx,hu,ia,is,it,ja,ko,ms,nb,nl,pl,pms,pt-br,ro,ru,sc,sv,th,tr,uk,vi,zh-hans,zh-hant --messageFilePathFormat xlf/translated_msgs_{LOCALE}.xlf --outputPathFormat "generated/{LOCALE}.js" template.soy - -This is the process that Google uses for maintaining Blockly Games in 50+ -languages. The XLIFF format is simple enough that it is trivial to write a -Python script to reformat it into some other format (such as JSON) for -compatibility with other translation consoles. - -For more information, see message translation for Closure Templates: -https://developers.google.com/closure/templates/docs/translation diff --git a/demos/plane/blocks.js b/demos/plane/blocks.js deleted file mode 100644 index c3c094a87..000000000 --- a/demos/plane/blocks.js +++ /dev/null @@ -1,95 +0,0 @@ -/** - * @license - * Copyright 2013 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview Blocks for Blockly's Plane Seat Calculator application. - */ -'use strict'; - -Blockly.Blocks['plane_set_seats'] = { - // Block seat variable setter. - init: function() { - this.setHelpUrl(Blockly.Msg['VARIABLES_SET_HELPURL']); - this.setColour(330); - this.appendValueInput('VALUE') - .appendField(Plane.getMsg('Plane_setSeats')); - this.setTooltip(Blockly.Msg['VARIABLES_SET_TOOLTIP']); - this.setDeletable(false); - } -}; - -Blockly.JavaScript['plane_set_seats'] = function(block) { - // Generate JavaScript for seat variable setter. - var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', - Blockly.JavaScript.ORDER_ASSIGNMENT) || 'NaN'; - return argument0 + ';'; -}; - -Blockly.Blocks['plane_get_rows'] = { - // Block for row variable getter. - init: function() { - this.setHelpUrl(Blockly.Msg['VARIABLES_GET_HELPURL']); - this.setColour(330); - this.appendDummyInput() - .appendField(Plane.getMsg('Plane_getRows') - .replace('%1', Plane.rows1st), 'title'); - this.setOutput(true, 'Number'); - }, - customUpdate: function() { - this.setFieldValue( - Plane.getMsg('Plane_getRows') - .replace('%1', Plane.rows1st), 'title'); - } -}; - -Blockly.JavaScript['plane_get_rows'] = function(block) { - // Generate JavaScript for row variable getter. - return ['Plane.rows1st', Blockly.JavaScript.ORDER_MEMBER]; -}; - -Blockly.Blocks['plane_get_rows1st'] = { - // Block for first class row variable getter. - init: function() { - this.setHelpUrl(Blockly.Msg['VARIABLES_GET_HELPURL']); - this.setColour(330); - this.appendDummyInput() - .appendField(Plane.getMsg('Plane_getRows1') - .replace('%1', Plane.rows1st), 'title'); - this.setOutput(true, 'Number'); - }, - customUpdate: function() { - this.setFieldValue( - Plane.getMsg('Plane_getRows1') - .replace('%1', Plane.rows1st), 'title'); - } -}; - -Blockly.JavaScript['plane_get_rows1st'] = function(block) { - // Generate JavaScript for first class row variable getter. - return ['Plane.rows1st', Blockly.JavaScript.ORDER_MEMBER]; -}; - -Blockly.Blocks['plane_get_rows2nd'] = { - // Block for second class row variable getter. - init: function() { - this.setHelpUrl(Blockly.Msg['VARIABLES_GET_HELPURL']); - this.setColour(330); - this.appendDummyInput() - .appendField(Plane.getMsg('Plane_getRows2') - .replace('%1', Plane.rows2nd), 'title'); - this.setOutput(true, 'Number'); - }, - customUpdate: function() { - this.setFieldValue( - Plane.getMsg('Plane_getRows2') - .replace('%1', Plane.rows2nd), 'title'); - } -}; - -Blockly.JavaScript['plane_get_rows2nd'] = function(block) { - // Generate JavaScript for second class row variable getter. - return ['Plane.rows2nd', Blockly.JavaScript.ORDER_MEMBER]; -}; diff --git a/demos/plane/generated/ar.js b/demos/plane/generated/ar.js deleted file mode 100644 index ac760fb11..000000000 --- a/demos/plane/generated/ar.js +++ /dev/null @@ -1,50 +0,0 @@ -// This file was automatically generated from template.soy. -// Please don't edit this file by hand. - -/** - * @fileoverview Templates in namespace planepage. - */ - -if (typeof planepage == 'undefined') { var planepage = {}; } - - -planepage.messages = function(opt_data, opt_ignored, opt_ijData) { - return '
\u0627\u0644\u0635\u0641\u0648\u0641: %1\u0627\u0644\u0635\u0641\u0648\u0641 (%1)\u0635\u0641\u0648\u0641 \u0627\u0644\u0637\u0628\u0642\u0629 \u0627\u0644\u0623\u0648\u0644\u0649: %1\u0635\u0641\u0648\u0641 \u0627\u0644\u0637\u0628\u0642\u0629 \u0627\u0644\u0623\u0648\u0644\u0649 (%1)\u0635\u0641\u0648\u0641 \u0627\u0644\u0641\u0626\u0629 \u0627\u0644\u062B\u0627\u0646\u064A\u0629: %1\u0635\u0641\u0648\u0641 \u0627\u0644\u0641\u0626\u0629 \u0627\u0644\u062B\u0627\u0646\u064A\u0629: (%1)\u0627\u0644\u0645\u0642\u0627\u0639\u062F: %1\u061F\u0627\u0644\u0645\u0642\u0627\u0639\u062F =
'; -}; -if (goog.DEBUG) { - planepage.messages.soyTemplateName = 'planepage.messages'; -} - - -planepage.start = function(opt_data, opt_ignored, opt_ijData) { - var output = planepage.messages(null, null, opt_ijData) + '

Blockly‏ > Demos‏ > \u0622\u0644\u0629 \u062D\u0627\u0633\u0628\u0629 \u0644\u0645\u0642\u0639\u062F \u0627\u0644\u0637\u0627\u0626\u0631\u0629   '; - var iLimit47 = opt_ijData.maxLevel + 1; - for (var i47 = 1; i47 < iLimit47; i47++) { - output += ' ' + ((i47 == opt_ijData.level) ? '' + soy.$$escapeHtml(i47) + '' : (i47 < opt_ijData.level) ? '' : '' + soy.$$escapeHtml(i47) + ''); - } - output += '

- - - - - - diff --git a/demos/plane/plane.js b/demos/plane/plane.js deleted file mode 100644 index a62d061b2..000000000 --- a/demos/plane/plane.js +++ /dev/null @@ -1,429 +0,0 @@ -/** - * @license - * Copyright 2012 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview JavaScript for Blockly's Plane Seat Calculator demo. - */ -'use strict'; - -/** - * Create a namespace for the application. - */ -var Plane = {}; - -/** - * Lookup for names of supported languages. Keys should be in ISO 639 format. - */ -Plane.LANGUAGE_NAME = { - 'ar': 'العربية', - 'be-tarask': 'Taraškievica', - 'br': 'Brezhoneg', - 'ca': 'Català', - 'da': 'Dansk', - 'de': 'Deutsch', - 'el': 'Ελληνικά', - 'en': 'English', - 'es': 'Español', - 'fa': 'فارسی', - 'fr': 'Français', - 'he': 'עברית', - 'hrx': 'Hunsrik', - 'hu': 'Magyar', - 'ia': 'Interlingua', - 'is': 'Íslenska', - 'it': 'Italiano', - 'ja': '日本語', - 'ko': '한국어', - 'ms': 'Bahasa Melayu', - 'nb': 'Norsk Bokmål', - 'nl': 'Nederlands, Vlaams', - 'pl': 'Polski', - 'pms': 'Piemontèis', - 'pt-br': 'Português Brasileiro', - 'ro': 'Română', - 'ru': 'Русский', - 'sc': 'Sardu', - 'sv': 'Svenska', - 'th': 'ภาษาไทย', - 'tr': 'Türkçe', - 'uk': 'Українська', - 'vi': 'Tiếng Việt', - 'zh-hans': '简体中文', - 'zh-hant': '正體中文' -}; - -/** - * List of RTL languages. - */ -Plane.LANGUAGE_RTL = ['ar', 'fa', 'he']; - -/** - * Main Blockly workspace. - * @type {Blockly.WorkspaceSvg} - */ -Plane.workspace = null; - -/** - * Extracts a parameter from the URL. - * If the parameter is absent default_value is returned. - * @param {string} name The name of the parameter. - * @param {string} defaultValue Value to return if paramater not found. - * @return {string} The parameter value or the default value if not found. - */ -Plane.getStringParamFromUrl = function(name, defaultValue) { - var val = location.search.match(new RegExp('[?&]' + name + '=([^&]+)')); - return val ? decodeURIComponent(val[1].replace(/\+/g, '%20')) : defaultValue; -}; - -/** - * Extracts a numeric parameter from the URL. - * If the parameter is absent or less than min_value, min_value is - * returned. If it is greater than max_value, max_value is returned. - * @param {string} name The name of the parameter. - * @param {number} minValue The minimum legal value. - * @param {number} maxValue The maximum legal value. - * @return {number} A number in the range [min_value, max_value]. - */ -Plane.getNumberParamFromUrl = function(name, minValue, maxValue) { - var val = Number(Plane.getStringParamFromUrl(name, 'NaN')); - return isNaN(val) ? minValue : Math.min(Math.max(minValue, val), maxValue); -}; - -/** - * Get the language of this user from the URL. - * @return {string} User's language. - */ -Plane.getLang = function() { - var lang = Plane.getStringParamFromUrl('lang', ''); - if (Plane.LANGUAGE_NAME[lang] === undefined) { - // Default to English. - lang = 'en'; - } - return lang; -}; - -/** - * Is the current language (Plane.LANG) an RTL language? - * @return {boolean} True if RTL, false if LTR. - */ -Plane.isRtl = function() { - return Plane.LANGUAGE_RTL.indexOf(Plane.LANG) !== -1; -}; - -/** - * Load blocks saved in session/local storage. - * @param {string} defaultXml Text representation of default blocks. - */ -Plane.loadBlocks = function(defaultXml) { - try { - var loadOnce = window.sessionStorage.loadOnceBlocks; - } catch(e) { - // Firefox sometimes throws a SecurityError when accessing sessionStorage. - // Restarting Firefox fixes this, so it looks like a bug. - var loadOnce = null; - } - if (loadOnce) { - // Language switching stores the blocks during the reload. - delete window.sessionStorage.loadOnceBlocks; - var xml = Blockly.Xml.textToDom(loadOnce); - Blockly.Xml.domToWorkspace(xml, Plane.workspace); - } else if (defaultXml) { - // Load the editor with default starting blocks. - var xml = Blockly.Xml.textToDom(defaultXml); - Blockly.Xml.domToWorkspace(xml, Plane.workspace); - } - Plane.workspace.clearUndo(); -}; - -/** - * Save the blocks and reload with a different language. - */ -Plane.changeLanguage = function() { - // Store the blocks for the duration of the reload. - // This should be skipped for the index page, which has no blocks and does - // not load Blockly. - // MSIE 11 does not support sessionStorage on file:// URLs. - if (typeof Blockly !== 'undefined' && window.sessionStorage) { - var xml = Blockly.Xml.workspaceToDom(Plane.workspace); - var text = Blockly.Xml.domToText(xml); - window.sessionStorage.loadOnceBlocks = text; - } - - var languageMenu = document.getElementById('languageMenu'); - var newLang = encodeURIComponent( - languageMenu.options[languageMenu.selectedIndex].value); - var search = window.location.search; - if (search.length <= 1) { - search = '?lang=' + newLang; - } else if (search.match(/[?&]lang=[^&]*/)) { - search = search.replace(/([?&]lang=)[^&]*/, '$1' + newLang); - } else { - search = search.replace(/\?/, '?lang=' + newLang + '&'); - } - - window.location = window.location.protocol + '//' + - window.location.host + window.location.pathname + search; -}; - -/** - * Gets the message with the given key from the document. - * @param {string} key The key of the document element. - * @return {string} The textContent of the specified element, - * or an error message if the element was not found. - */ -Plane.getMsg = function(key) { - var element = document.getElementById(key); - if (element) { - var text = element.textContent; - // Convert newline sequences. - text = text.replace(/\\n/g, '\n'); - return text; - } else { - return '[Unknown message: ' + key + ']'; - } -}; - -/** - * User's language (e.g. "en"). - * @type {string} - */ -Plane.LANG = Plane.getLang(); - -Plane.MAX_LEVEL = 3; -Plane.LEVEL = Plane.getNumberParamFromUrl('level', 1, Plane.MAX_LEVEL); - -Plane.rows1st = 0; -Plane.rows2nd = 0; - -/** - * Redraw the rows and update blocks when the slider has moved. - * @param {number} value New slider position. - */ -Plane.sliderChange = function(value) { - var newRows = Math.round(value * 410 / 20); - Plane.redraw(newRows); - - function updateBlocks(blocks) { - for (var i = 0, block; block = blocks[i]; i++) { - block.customUpdate && block.customUpdate(); - } - } - updateBlocks(Plane.workspace.getAllBlocks(false), true); - updateBlocks(Plane.workspace.flyout_.workspace_.getAllBlocks(false)); -}; - -/** - * Change the text of a label. - * @param {string} id ID of element to change. - * @param {string} text New text. - */ -Plane.setText = function(id, text) { - var el = document.getElementById(id); - while (el.firstChild) { - el.removeChild(el.firstChild); - } - el.appendChild(document.createTextNode(text)); -}; - -/** - * Display a checkmark or cross next to the answer. - * @param {?boolean} ok True for checkmark, false for cross, null for nothing. - */ -Plane.setCorrect = function(ok) { - var yes = document.getElementById('seatYes'); - var no = document.getElementById('seatNo'); - yes.style.display = 'none'; - no.style.display = 'none'; - if (ok === true) { - yes.style.display = 'block'; - } else if (ok === false) { - no.style.display = 'block'; - } -}; - -/** - * Initialize Blockly and the SVG plane. - */ -Plane.init = function() { - Plane.initLanguage(); - - // Fixes viewport for small screens. - var viewport = document.querySelector('meta[name="viewport"]'); - if (viewport && screen.availWidth < 725) { - viewport.setAttribute('content', - 'width=725, initial-scale=.35, user-scalable=no'); - } - - Plane.workspace = Blockly.inject('blockly', - {media: '../../media/', - rtl: Plane.isRtl(), - toolbox: document.getElementById('toolbox')}); - - var defaultXml = - '' + - ' ' + - ' ' + - ''; - Plane.loadBlocks(defaultXml); - - Plane.workspace.addChangeListener(Plane.recalculate); - Plane.workspace.addChangeListener(Blockly.Events.disableOrphans); - - // Initialize the slider. - var svg = document.getElementById('plane'); - Plane.rowSlider = new Slider(60, 330, 425, svg, Plane.sliderChange); - Plane.rowSlider.setValue(0.225); - - // Draw five 1st class rows. - Plane.redraw(5); -}; - -/** - * Initialize the page language. - */ -Plane.initLanguage = function() { - // Set the page title with the content of the H1 title. - document.title += ' ' + document.getElementById('title').textContent; - - // Set the HTML's language and direction. - // document.dir fails in Mozilla, use document.body.parentNode.dir instead. - // https://bugzilla.mozilla.org/show_bug.cgi?id=151407 - var rtl = Plane.isRtl(); - document.head.parentElement.setAttribute('dir', rtl ? 'rtl' : 'ltr'); - document.head.parentElement.setAttribute('lang', Plane.LANG); - - // Sort languages alphabetically. - var languages = []; - for (var lang in Plane.LANGUAGE_NAME) { - languages.push([Plane.LANGUAGE_NAME[lang], lang]); - } - var comp = function(a, b) { - // Sort based on first argument ('English', 'Русский', '简体字', etc). - if (a[0] > b[0]) return 1; - if (a[0] < b[0]) return -1; - return 0; - }; - languages.sort(comp); - // Populate the language selection menu. - var languageMenu = document.getElementById('languageMenu'); - languageMenu.options.length = 0; - for (var i = 0; i < languages.length; i++) { - var tuple = languages[i]; - var lang = tuple[tuple.length - 1]; - var option = new Option(tuple[0], lang); - if (lang === Plane.LANG) { - option.selected = true; - } - languageMenu.options.add(option); - } - languageMenu.addEventListener('change', Plane.changeLanguage, true); -}; - -/** - * Use the blocks to calculate the number of seats. - * Display the calculated number. - */ -Plane.recalculate = function() { - // Find the 'set' block and use it as the formula root. - var rootBlock = null; - var blocks = Plane.workspace.getTopBlocks(false); - for (var i = 0, block; block = blocks[i]; i++) { - if (block.type === 'plane_set_seats') { - rootBlock = block; - } - } - var seats = NaN; - Blockly.JavaScript.init(Plane.workspace); - var code = Blockly.JavaScript.blockToCode(rootBlock); - try { - seats = eval(code); - } catch (e) { - // Allow seats to remain NaN. - } - Plane.setText('seatText', - Plane.getMsg('Plane_seats').replace( - '%1', isNaN(seats) ? '?' : seats)); - Plane.setCorrect(isNaN(seats) ? null : (Plane.answer() === seats)); -}; - -/** - * Calculate the correct answer. - * @return {number} Number of seats. - */ -Plane.answer = function() { - if (Plane.LEVEL === 1) { - return Plane.rows1st * 4; - } else if (Plane.LEVEL === 2) { - return 2 + (Plane.rows1st * 4); - } else if (Plane.LEVEL === 3) { - return 2 + (Plane.rows1st * 4) + (Plane.rows2nd * 5); - } - throw 'Unknown level.'; -}; - -/** - * Redraw the SVG to show a new number of rows. - * @param {number} newRows - */ -Plane.redraw = function(newRows) { - var rows1st = Plane.rows1st; - var rows2nd = Plane.rows2nd; - var svg = document.getElementById('plane'); - if (newRows !== rows1st) { - while (newRows < rows1st) { - var row = document.getElementById('row1st' + rows1st); - row.parentNode.removeChild(row); - rows1st--; - } - while (newRows > rows1st) { - rows1st++; - var row = document.createElementNS('http://www.w3.org/2000/svg', 'use'); - row.id = 'row1st' + rows1st; - // Row of 4 seats. - row.setAttribute('x', (rows1st - 1) * 20); - row.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', '#row1st'); - svg.appendChild(row); - } - - if (Plane.LEVEL === 3) { - newRows = Math.floor((21 - newRows) * 1.11); - while (newRows < rows2nd) { - var row = document.getElementById('row2nd' + rows2nd); - row.parentNode.removeChild(row); - rows2nd--; - } - while (newRows > rows2nd) { - rows2nd++; - var row = document.createElementNS('http://www.w3.org/2000/svg', 'use'); - row.id = 'row2nd' + rows2nd; - row.setAttribute('x', 400 - (rows2nd - 1) * 18); - row.setAttributeNS('http://www.w3.org/1999/xlink', - 'xlink:href', '#row2nd'); - svg.appendChild(row); - } - } - - if (Plane.LEVEL < 3) { - Plane.setText('row1stText', - Plane.getMsg('Plane_rows').replace('%1', rows1st)); - } else { - Plane.setText('row1stText', - Plane.getMsg('Plane_rows1').replace('%1', rows1st)); - Plane.setText('row2ndText', - Plane.getMsg('Plane_rows2').replace('%1', rows2nd)); - } - - Plane.rows1st = rows1st; - Plane.rows2nd = rows2nd; - Plane.recalculate(); - } -}; - -window.addEventListener('load', Plane.init); - -// Load the user's language pack. -document.write('\n'); diff --git a/demos/plane/slider.js b/demos/plane/slider.js deleted file mode 100644 index 8c0a1e8fc..000000000 --- a/demos/plane/slider.js +++ /dev/null @@ -1,273 +0,0 @@ -/** - * @license - * Copyright 2012 Google LLC - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @fileoverview A slider control in SVG. - */ -'use strict'; - - -/** - * Object representing a horizontal slider widget. - * @param {number} x The horizontal offset of the slider. - * @param {number} y The vertical offset of the slider. - * @param {number} width The total width of the slider. - * @param {!Element} svgParent The SVG element to append the slider to. - * @param {Function=} opt_changeFunc Optional callback function that will be - * called when the slider is moved. The current value is passed. - * @constructor - */ -var Slider = function(x, y, width, svgParent, opt_changeFunc) { - this.KNOB_Y_ = y - 12; - this.KNOB_MIN_X_ = x + 8; - this.KNOB_MAX_X_ = x + width - 8; - this.TARGET_OVERHANG_ = 20; - this.value_ = 0.5; - this.changeFunc_ = opt_changeFunc; - this.animationTasks_ = []; - - // Draw the slider. - /* - - - - - */ - var track = document.createElementNS(Slider.SVG_NS_, 'line'); - track.setAttribute('class', 'sliderTrack'); - track.setAttribute('x1', x); - track.setAttribute('y1', y); - track.setAttribute('x2', x + width); - track.setAttribute('y2', y); - svgParent.appendChild(track); - this.track_ = track; - var rect = document.createElementNS(Slider.SVG_NS_, 'rect'); - rect.setAttribute('style', 'opacity: 0'); - rect.setAttribute('x', x - this.TARGET_OVERHANG_); - rect.setAttribute('y', y - this.TARGET_OVERHANG_); - rect.setAttribute('width', width + 2 * this.TARGET_OVERHANG_); - rect.setAttribute('height', 2 * this.TARGET_OVERHANG_); - rect.setAttribute('rx', this.TARGET_OVERHANG_); - rect.setAttribute('ry', this.TARGET_OVERHANG_); - svgParent.appendChild(rect); - this.trackTarget_ = rect; - var knob = document.createElementNS(Slider.SVG_NS_, 'path'); - knob.setAttribute('class', 'sliderKnob'); - knob.setAttribute('d', 'm 0,0 l -8,8 v 12 h 16 v -12 z'); - svgParent.appendChild(knob); - this.knob_ = knob; - var circle = document.createElementNS(Slider.SVG_NS_, 'circle'); - circle.setAttribute('style', 'opacity: 0'); - circle.setAttribute('r', this.TARGET_OVERHANG_); - circle.setAttribute('cy', y); - svgParent.appendChild(circle); - this.knobTarget_ = circle; - this.setValue(0.5); - - // Find the root SVG object. - while (svgParent && svgParent.nodeName.toLowerCase() !== 'svg') { - svgParent = svgParent.parentNode; - } - this.SVG_ = svgParent; - - // Bind the events to this slider. - Slider.bindEvent_(this.knobTarget_, 'mousedown', this, this.knobMouseDown_); - Slider.bindEvent_(this.knobTarget_, 'touchstart', this, this.knobMouseDown_); - Slider.bindEvent_(this.trackTarget_, 'mousedown', this, this.rectMouseDown_); - Slider.bindEvent_(this.SVG_, 'mouseup', null, Slider.knobMouseUp_); - Slider.bindEvent_(this.SVG_, 'touchend', null, Slider.knobMouseUp_); - Slider.bindEvent_(this.SVG_, 'mousemove', null, Slider.knobMouseMove_); - Slider.bindEvent_(this.SVG_, 'touchmove', null, Slider.knobMouseMove_); - Slider.bindEvent_(document, 'mouseover', null, Slider.mouseOver_); -}; - - -Slider.SVG_NS_ = 'http://www.w3.org/2000/svg'; - -Slider.activeSlider_ = null; -Slider.startMouseX_ = 0; -Slider.startKnobX_ = 0; - -/** - * Start a drag when clicking down on the knob. - * @param {!Event} e Mouse-down event. - * @private - */ -Slider.prototype.knobMouseDown_ = function(e) { - if (e.type === 'touchstart') { - if (e.changedTouches.length !== 1) { - return; - } - Slider.touchToMouse_(e) - } - Slider.activeSlider_ = this; - Slider.startMouseX_ = this.mouseToSvg_(e).x; - Slider.startKnobX_ = 0; - var transform = this.knob_.getAttribute('transform'); - if (transform) { - var r = transform.match(/translate\(\s*([-\d.]+)/); - if (r) { - Slider.startKnobX_ = Number(r[1]); - } - } - // Stop browser from attempting to drag the knob or - // from scrolling/zooming the page. - e.preventDefault(); -}; - -/** - * Stop a drag when clicking up anywhere. - * @param {Event} e Mouse-up event. - * @private - */ -Slider.knobMouseUp_ = function(e) { - Slider.activeSlider_ = null; -}; - -/** - * Stop a drag when the mouse enters a node not part of the SVG. - * @param {Event} e Mouse-up event. - * @private - */ -Slider.mouseOver_ = function(e) { - if (!Slider.activeSlider_) { - return; - } - var node = e.target; - // Find the root SVG object. - do { - if (node === Slider.activeSlider_.SVG_) { - return; - } - } while (node = node.parentNode); - Slider.knobMouseUp_(e); -}; - -/** - * Drag the knob to follow the mouse. - * @param {!Event} e Mouse-move event. - * @private - */ -Slider.knobMouseMove_ = function(e) { - var thisSlider = Slider.activeSlider_; - if (!thisSlider) { - return; - } - if (e.type === 'touchmove') { - if (e.changedTouches.length !== 1) { - return; - } - Slider.touchToMouse_(e) - } - var x = thisSlider.mouseToSvg_(e).x - Slider.startMouseX_ + - Slider.startKnobX_; - thisSlider.setValue((x - thisSlider.KNOB_MIN_X_) / - (thisSlider.KNOB_MAX_X_ - thisSlider.KNOB_MIN_X_)); -}; - -/** - * Jump to a new value when the track is clicked. - * @param {!Event} e Mouse-down event. - * @private - */ -Slider.prototype.rectMouseDown_ = function(e) { - if (e.type === 'touchstart') { - if (e.changedTouches.length !== 1) { - return; - } - Slider.touchToMouse_(e) - } - var x = this.mouseToSvg_(e).x; - this.animateValue((x - this.KNOB_MIN_X_) / - (this.KNOB_MAX_X_ - this.KNOB_MIN_X_)); -}; - -/** - * Returns the slider's value (0.0 - 1.0). - * @return {number} Current value. - */ -Slider.prototype.getValue = function() { - return this.value_; -}; - -/** - * Animates the slider's value (0.0 - 1.0). - * @param {number} value New value. - */ -Slider.prototype.animateValue = function(value) { - // Clear any ongoing animations. - while (this.animationTasks_.length) { - clearTimeout(this.animationTasks_.pop()); - } - var duration = 200; // Milliseconds to animate for. - var steps = 10; // Number of steps to animate. - var oldValue = this.getValue(); - var thisSlider = this; - var stepFunc = function(i) { - return function() { - var newVal = i * (value - oldValue) / (steps - 1) + oldValue; - thisSlider.setValue(newVal); - }; - } - for (var i = 0; i < steps; i++) { - this.animationTasks_.push(setTimeout(stepFunc(i), i * duration / steps)); - } -}; - -/** - * Sets the slider's value (0.0 - 1.0). - * @param {number} value New value. - */ -Slider.prototype.setValue = function(value) { - this.value_ = Math.min(Math.max(value, 0), 1); - var x = this.KNOB_MIN_X_ + - (this.KNOB_MAX_X_ - this.KNOB_MIN_X_) * this.value_; - this.knob_.setAttribute('transform', - 'translate(' + x + ',' + this.KNOB_Y_ + ')'); - this.knobTarget_.setAttribute('cx', x); - this.changeFunc_ && this.changeFunc_(this.value_); -}; - -/** - * Convert the mouse coordinates into SVG coordinates. - * @param {!Object} e Object with x and y mouse coordinates. - * @return {!Object} Object with x and y properties in SVG coordinates. - * @private - */ -Slider.prototype.mouseToSvg_ = function(e) { - var svgPoint = this.SVG_.createSVGPoint(); - svgPoint.x = e.clientX; - svgPoint.y = e.clientY; - var matrix = this.SVG_.getScreenCTM().inverse(); - return svgPoint.matrixTransform(matrix); -}; - -/** - * Bind an event to a function call. - * @param {!Node} node Node upon which to listen. - * @param {string} name Event name to listen to (e.g. 'mousedown'). - * @param {Object} thisObject The value of 'this' in the function. - * @param {!Function} func Function to call when event is triggered. - * @private - */ -Slider.bindEvent_ = function(node, name, thisObject, func) { - var wrapFunc = function(e) { - func.apply(thisObject, arguments); - }; - node.addEventListener(name, wrapFunc, false); -}; - -/** - * Map the touch event's properties to be compatible with a mouse event. - * @param {TouchEvent} e Event to modify. - */ -Slider.touchToMouse_ = function(e) { - var touchPoint = e.changedTouches[0]; - e.clientX = touchPoint.clientX; - e.clientY = touchPoint.clientY; -}; diff --git a/demos/plane/soy/COPYING b/demos/plane/soy/COPYING deleted file mode 100644 index d64569567..000000000 --- a/demos/plane/soy/COPYING +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/demos/plane/soy/README b/demos/plane/soy/README deleted file mode 100644 index 51aa75caf..000000000 --- a/demos/plane/soy/README +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2009 Google LLC -// -// 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. - - -Contents: - -+ SoyToJsSrcCompiler.jar - Executable jar that compiles template files into JavaScript files. - -+ SoyMsgExtractor.jar - Executable jar that extracts messages from template files into XLF files. - -+ soyutils.js - Helper utilities required by all JavaScript code that SoyToJsSrcCompiler - generates. Equivalent functionality to soyutils_usegoog.js, but this - version does not need Closure Library. - - -Instructions: - -+ A simple Hello World for JavaScript: - http://code.google.com/closure/templates/docs/helloworld_js.html - -+ Complete documentation: - http://code.google.com/closure/templates/ - -+ Closure Templates project on Google Code: - http://code.google.com/p/closure-templates/ - - -Notes: - -+ Closure Templates requires Java 6 or higher: - http://www.java.com/ diff --git a/demos/plane/soy/SoyMsgExtractor.jar b/demos/plane/soy/SoyMsgExtractor.jar deleted file mode 100644 index d5c112f5e..000000000 Binary files a/demos/plane/soy/SoyMsgExtractor.jar and /dev/null differ diff --git a/demos/plane/soy/SoyToJsSrcCompiler.jar b/demos/plane/soy/SoyToJsSrcCompiler.jar deleted file mode 100644 index bb3b7a979..000000000 Binary files a/demos/plane/soy/SoyToJsSrcCompiler.jar and /dev/null differ diff --git a/demos/plane/soy/soyutils.js b/demos/plane/soy/soyutils.js deleted file mode 100644 index ca9191612..000000000 --- a/demos/plane/soy/soyutils.js +++ /dev/null @@ -1,3299 +0,0 @@ -/** - * @license - * Copyright 2008 Google LLC - * - * 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 - * Utility functions and classes for Soy. - * - *

- * The top portion of this file contains utilities for Soy users:

    - *
  • soy.StringBuilder: Compatible with the 'stringbuilder' code style. - *
  • soy.renderElement: Render template and set as innerHTML of an element. - *
  • soy.renderAsFragment: Render template and return as HTML fragment. - *
- * - *

- * The bottom portion of this file contains utilities that should only be called - * by Soy-generated JS code. Please do not use these functions directly from - * your hand-writen code. Their names all start with '$$'. - * - * @author Garrett Boyer - * @author Mike Samuel - * @author Kai Huang - * @author Aharon Lanin - */ - - -// COPIED FROM nogoog_shim.js - -// Create closure namespaces. -var goog = goog || {}; - - -goog.DEBUG = false; - - -goog.inherits = function(childCtor, parentCtor) { - /** @constructor */ - function tempCtor() {}; - tempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new tempCtor(); - childCtor.prototype.constructor = childCtor; - - /** - * Calls superclass constructor/method. - * @param {!Object} me Should always be "this". - * @param {string} methodName - * @param {...*} var_args - * @return {?} The return value of the superclass method/constructor. - */ - childCtor.base = function(me, methodName, var_args) { - var args = Array.prototype.slice.call(arguments, 2); - return parentCtor.prototype[methodName].apply(me, args); - }; -}; - - -// Just enough browser detection for this file. -if (!goog.userAgent) { - goog.userAgent = (function() { - var userAgent = ""; - if ("undefined" !== typeof navigator && navigator - && "string" == typeof navigator.userAgent) { - userAgent = navigator.userAgent; - } - var isOpera = userAgent.indexOf('Opera') == 0; - return { - jscript: { - /** - * @type {boolean} - */ - HAS_JSCRIPT: 'ScriptEngine' in this - }, - /** - * @type {boolean} - */ - OPERA: isOpera, - /** - * @type {boolean} - */ - IE: !isOpera && userAgent.indexOf('MSIE') != -1, - /** - * @type {boolean} - */ - WEBKIT: !isOpera && userAgent.indexOf('WebKit') != -1 - }; - })(); -} - -if (!goog.asserts) { - goog.asserts = { - /** - * @param {*} condition Condition to check. - */ - assert: function (condition) { - if (!condition) { - throw Error('Assertion error'); - } - }, - /** - * @param {...*} var_args - */ - fail: function (var_args) {} - }; -} - - -// Stub out the document wrapper used by renderAs*. -if (!goog.dom) { - goog.dom = {}; - /** - * @param {Document=} d - * @constructor - */ - goog.dom.DomHelper = function(d) { - this.document_ = d || document; - }; - /** - * @return {!Document} - */ - goog.dom.DomHelper.prototype.getDocument = function() { - return this.document_; - }; - /** - * Creates a new element. - * @param {string} name Tag name. - * @return {!Element} - */ - goog.dom.DomHelper.prototype.createElement = function(name) { - return this.document_.createElement(name); - }; - /** - * Creates a new document fragment. - * @return {!DocumentFragment} - */ - goog.dom.DomHelper.prototype.createDocumentFragment = function() { - return this.document_.createDocumentFragment(); - }; -} - - -if (!goog.format) { - goog.format = { - insertWordBreaks: function(str, maxCharsBetweenWordBreaks) { - str = String(str); - - var resultArr = []; - var resultArrLen = 0; - - // These variables keep track of important state inside str. - var isInTag = false; // whether we're inside an HTML tag - var isMaybeInEntity = false; // whether we might be inside an HTML entity - var numCharsWithoutBreak = 0; // number of chars since last word break - var flushIndex = 0; // index of first char not yet flushed to resultArr - - for (var i = 0, n = str.length; i < n; ++i) { - var charCode = str.charCodeAt(i); - - // If hit maxCharsBetweenWordBreaks, and not space next, then add . - if (numCharsWithoutBreak >= maxCharsBetweenWordBreaks && - // space - charCode != 32) { - resultArr[resultArrLen++] = str.substring(flushIndex, i); - flushIndex = i; - resultArr[resultArrLen++] = goog.format.WORD_BREAK; - numCharsWithoutBreak = 0; - } - - if (isInTag) { - // If inside an HTML tag and we see '>', it's the end of the tag. - if (charCode == 62) { - isInTag = false; - } - - } else if (isMaybeInEntity) { - switch (charCode) { - // Inside an entity, a ';' is the end of the entity. - // The entity that just ended counts as one char, so increment - // numCharsWithoutBreak. - case 59: // ';' - isMaybeInEntity = false; - ++numCharsWithoutBreak; - break; - // If maybe inside an entity and we see '<', we weren't actually in - // an entity. But now we're inside and HTML tag. - case 60: // '<' - isMaybeInEntity = false; - isInTag = true; - break; - // If maybe inside an entity and we see ' ', we weren't actually in - // an entity. Just correct the state and reset the - // numCharsWithoutBreak since we just saw a space. - case 32: // ' ' - isMaybeInEntity = false; - numCharsWithoutBreak = 0; - break; - } - - } else { // !isInTag && !isInEntity - switch (charCode) { - // When not within a tag or an entity and we see '<', we're now - // inside an HTML tag. - case 60: // '<' - isInTag = true; - break; - // When not within a tag or an entity and we see '&', we might be - // inside an entity. - case 38: // '&' - isMaybeInEntity = true; - break; - // When we see a space, reset the numCharsWithoutBreak count. - case 32: // ' ' - numCharsWithoutBreak = 0; - break; - // When we see a non-space, increment the numCharsWithoutBreak. - default: - ++numCharsWithoutBreak; - break; - } - } - } - - // Flush the remaining chars at the end of the string. - resultArr[resultArrLen++] = str.substring(flushIndex); - - return resultArr.join(''); - }, - /** - * String inserted as a word break by insertWordBreaks(). Safari requires - * , Opera needs the ­ entity, though this will give a - * visible hyphen at breaks. IE8+ use a zero width space. Other browsers - * just use . - * @type {string} - * @private - */ - WORD_BREAK: - goog.userAgent.WEBKIT ? '' : - goog.userAgent.OPERA ? '­' : - goog.userAgent.IE ? '​' : - '' - }; -} - - -if (!goog.i18n) { - goog.i18n = { - bidi: {} - }; -} - - -/** - * Constant that defines whether or not the current locale is an RTL locale. - * - * @type {boolean} - */ -goog.i18n.bidi.IS_RTL = false; - - -/** - * Directionality enum. - * @enum {number} - */ -goog.i18n.bidi.Dir = { - /** - * Left-to-right. - */ - LTR: 1, - - /** - * Right-to-left. - */ - RTL: -1, - - /** - * Neither left-to-right nor right-to-left. - */ - NEUTRAL: 0, - - /** - * A historical misnomer for NEUTRAL. - * @deprecated For "neutral", use NEUTRAL; for "unknown", use null. - */ - UNKNOWN: 0 -}; - - -/** - * Convert a directionality given in various formats to a goog.i18n.bidi.Dir - * constant. Useful for interaction with different standards of directionality - * representation. - * - * @param {goog.i18n.bidi.Dir|number|boolean|null} givenDir Directionality given - * in one of the following formats: - * 1. A goog.i18n.bidi.Dir constant. - * 2. A number (positive = LTR, negative = RTL, 0 = neutral). - * 3. A boolean (true = RTL, false = LTR). - * 4. A null for unknown directionality. - * @param {boolean=} opt_noNeutral Whether a givenDir of zero or - * goog.i18n.bidi.Dir.NEUTRAL should be treated as null, i.e. unknown, in - * order to preserve legacy behavior. - * @return {?goog.i18n.bidi.Dir} A goog.i18n.bidi.Dir constant matching the - * given directionality. If given null, returns null (i.e. unknown). - */ -goog.i18n.bidi.toDir = function(givenDir, opt_noNeutral) { - if (typeof givenDir == 'number') { - // This includes the non-null goog.i18n.bidi.Dir case. - return givenDir > 0 ? goog.i18n.bidi.Dir.LTR : - givenDir < 0 ? goog.i18n.bidi.Dir.RTL : - opt_noNeutral ? null : goog.i18n.bidi.Dir.NEUTRAL; - } else if (givenDir == null) { - return null; - } else { - // Must be typeof givenDir == 'boolean'. - return givenDir ? goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR; - } -}; - - -/** - * Estimates the directionality of a string based on relative word counts. - * If the number of RTL words is above a certain percentage of the total number - * of strongly directional words, returns RTL. - * Otherwise, if any words are strongly or weakly LTR, returns LTR. - * Otherwise, returns NEUTRAL. - * Numbers are counted as weakly LTR. - * @param {string} str The string to be checked. - * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped. - * Default: false. - * @return {goog.i18n.bidi.Dir} Estimated overall directionality of {@code str}. - */ -goog.i18n.bidi.estimateDirection = function(str, opt_isHtml) { - var rtlCount = 0; - var totalCount = 0; - var hasWeaklyLtr = false; - var tokens = soyshim.$$bidiStripHtmlIfNecessary_(str, opt_isHtml). - split(soyshim.$$bidiWordSeparatorRe_); - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i]; - if (soyshim.$$bidiRtlDirCheckRe_.test(token)) { - rtlCount++; - totalCount++; - } else if (soyshim.$$bidiIsRequiredLtrRe_.test(token)) { - hasWeaklyLtr = true; - } else if (soyshim.$$bidiLtrCharRe_.test(token)) { - totalCount++; - } else if (soyshim.$$bidiHasNumeralsRe_.test(token)) { - hasWeaklyLtr = true; - } - } - - return totalCount == 0 ? - (hasWeaklyLtr ? goog.i18n.bidi.Dir.LTR : goog.i18n.bidi.Dir.NEUTRAL) : - (rtlCount / totalCount > soyshim.$$bidiRtlDetectionThreshold_ ? - goog.i18n.bidi.Dir.RTL : goog.i18n.bidi.Dir.LTR); -}; - - -/** - * Utility class for formatting text for display in a potentially - * opposite-directionality context without garbling. Provides the following - * functionality: - * - * @param {goog.i18n.bidi.Dir|number|boolean|null} dir The context - * directionality, in one of the following formats: - * 1. A goog.i18n.bidi.Dir constant. NEUTRAL is treated the same as null, - * i.e. unknown, for backward compatibility with legacy calls. - * 2. A number (positive = LTR, negative = RTL, 0 = unknown). - * 3. A boolean (true = RTL, false = LTR). - * 4. A null for unknown directionality. - * @constructor - */ -goog.i18n.BidiFormatter = function(dir) { - /** - * The overall directionality of the context in which the formatter is being - * used. - * @type {?goog.i18n.bidi.Dir} - * @private - */ - this.dir_ = goog.i18n.bidi.toDir(dir, true /* opt_noNeutral */); -}; - -/** - * @return {?goog.i18n.bidi.Dir} The context directionality. - */ -goog.i18n.BidiFormatter.prototype.getContextDir = function() { - return this.dir_; -}; - -/** - * Returns 'dir="ltr"' or 'dir="rtl"', depending on the given directionality, if - * it is not the same as the context directionality. Otherwise, returns the - * empty string. - * - * @param {goog.i18n.bidi.Dir} dir A directionality. - * @return {string} 'dir="rtl"' for RTL text in non-RTL context; 'dir="ltr"' for - * LTR text in non-LTR context; else, the empty string. - */ -goog.i18n.BidiFormatter.prototype.knownDirAttr = function(dir) { - return !dir || dir == this.dir_ ? '' : dir < 0 ? 'dir="rtl"' : 'dir="ltr"'; -}; - -/** - * Returns the trailing horizontal edge, i.e. "right" or "left", depending on - * the global bidi directionality. - * @return {string} "left" for RTL context and "right" otherwise. - */ -goog.i18n.BidiFormatter.prototype.endEdge = function () { - return this.dir_ < 0 ? 'left' : 'right'; -}; - -/** - * Returns the Unicode BiDi mark matching the context directionality (LRM for - * LTR context directionality, RLM for RTL context directionality), or the - * empty string for unknown context directionality. - * - * @return {string} LRM for LTR context directionality and RLM for RTL context - * directionality. - */ -goog.i18n.BidiFormatter.prototype.mark = function () { - return ( - (this.dir_ > 0) ? '\u200E' /*LRM*/ : - (this.dir_ < 0) ? '\u200F' /*RLM*/ : - ''); -}; - -/** - * Returns a Unicode bidi mark matching the context directionality (LRM or RLM) - * if the directionality or the exit directionality of {@code text} are opposite - * to the context directionality. Otherwise returns the empty string. - * If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes - * in text, making the logic suitable for HTML and HTML-escaped text. - * @param {?goog.i18n.bidi.Dir} textDir {@code text}'s overall directionality, - * or null if unknown and needs to be estimated. - * @param {string} text The text whose directionality is to be estimated. - * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped. - * Default: false. - * @return {string} A Unicode bidi mark matching the context directionality, or - * the empty string when either the context directionality is unknown or - * neither the text's overall nor its exit directionality is opposite to it. - */ -goog.i18n.BidiFormatter.prototype.markAfterKnownDir = function ( - textDir, text, opt_isHtml) { - if (textDir == null) { - textDir = goog.i18n.bidi.estimateDirection(text, opt_isHtml); - } - return ( - this.dir_ > 0 && (textDir < 0 || - soyshim.$$bidiIsRtlExitText_(text, opt_isHtml)) ? '\u200E' : // LRM - this.dir_ < 0 && (textDir > 0 || - soyshim.$$bidiIsLtrExitText_(text, opt_isHtml)) ? '\u200F' : // RLM - ''); -}; - -/** - * Formats an HTML string for use in HTML output of the context directionality, - * so an opposite-directionality string is neither garbled nor garbles what - * follows it. - * - * @param {?goog.i18n.bidi.Dir} textDir {@code str}'s overall directionality, or - * null if unknown and needs to be estimated. - * @param {string} str The input text (HTML or HTML-escaped). - * @param {boolean=} placeholder This argument exists for consistency with the - * Closure Library. Specifying it has no effect. - * @return {string} The input text after applying the above processing. - */ -goog.i18n.BidiFormatter.prototype.spanWrapWithKnownDir = function( - textDir, str, placeholder) { - if (textDir == null) { - textDir = goog.i18n.bidi.estimateDirection(str, true); - } - var reset = this.markAfterKnownDir(textDir, str, true); - if (textDir > 0 && this.dir_ <= 0) { - str = '' + str + ''; - } else if (textDir < 0 && this.dir_ >= 0) { - str = '' + str + ''; - } - return str + reset; -}; - -/** - * Returns the leading horizontal edge, i.e. "left" or "right", depending on - * the global bidi directionality. - * @return {string} "right" for RTL context and "left" otherwise. - */ -goog.i18n.BidiFormatter.prototype.startEdge = function () { - return this.dir_ < 0 ? 'right' : 'left'; -}; - -/** - * Formats an HTML-escaped string for use in HTML output of the context - * directionality, so an opposite-directionality string is neither garbled nor - * garbles what follows it. - * As opposed to {@link #spanWrapWithKnownDir}, this makes use of unicode bidi - * formatting characters. In HTML, it should only be used inside attribute - * values and elements that do not allow markup, e.g. an 'option' tag. - * - * @param {?goog.i18n.bidi.Dir} textDir {@code str}'s overall directionality, or - * null if unknown and needs to be estimated. - * @param {string} str The input text (HTML-escaped). - * @param {boolean=} opt_isHtml Whether {@code str} is HTML / HTML-escaped. - * Default: false. - * @return {string} The input text after applying the above processing. - */ -goog.i18n.BidiFormatter.prototype.unicodeWrapWithKnownDir = function( - textDir, str, opt_isHtml) { - if (textDir == null) { - textDir = goog.i18n.bidi.estimateDirection(str, opt_isHtml); - } - var reset = this.markAfterKnownDir(textDir, str, opt_isHtml); - if (textDir > 0 && this.dir_ <= 0) { - str = '\u202A' + str + '\u202C'; - } else if (textDir < 0 && this.dir_ >= 0) { - str = '\u202B' + str + '\u202C'; - } - return str + reset; -}; - - -if (!goog.string) { - goog.string = { - /** - * Converts \r\n, \r, and \n to
s - * @param {*} str The string in which to convert newlines. - * @param {boolean=} opt_xml Whether to use XML compatible tags. - * @return {string} A copy of {@code str} with converted newlines. - */ - newLineToBr: function(str, opt_xml) { - - str = String(str); - - // This quick test helps in the case when there are no chars to replace, - // in the worst case this makes barely a difference to the time taken. - if (!goog.string.NEWLINE_TO_BR_RE_.test(str)) { - return str; - } - - return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '
' : '
'); - }, - urlEncode: encodeURIComponent, - /** - * Regular expression used within newlineToBr(). - * @type {RegExp} - * @private - */ - NEWLINE_TO_BR_RE_: /[\r\n]/ - }; -} - -/** - * Utility class to facilitate much faster string concatenation in IE, - * using Array.join() rather than the '+' operator. For other browsers - * we simply use the '+' operator. - * - * @param {Object|number|string|boolean=} opt_a1 Optional first initial item - * to append. - * @param {...Object|number|string|boolean} var_args Other initial items to - * append, e.g., new goog.string.StringBuffer('foo', 'bar'). - * @constructor - */ -goog.string.StringBuffer = function(opt_a1, var_args) { - /** - * Internal buffer for the string to be concatenated. - * @type {string|Array} - * @private - */ - this.buffer_ = goog.userAgent.jscript.HAS_JSCRIPT ? [] : ''; - - if (opt_a1 != null) { - this.append.apply(this, arguments); - } -}; - - -/** - * Length of internal buffer (faster than calling buffer_.length). - * Only used for IE. - * @type {number} - * @private - */ -goog.string.StringBuffer.prototype.bufferLength_ = 0; - -/** - * Appends one or more items to the string. - * - * Calling this with null, undefined, or empty arguments is an error. - * - * @param {Object|number|string|boolean} a1 Required first string. - * @param {Object|number|string|boolean=} opt_a2 Optional second string. - * @param {...Object|number|string|boolean} var_args Other items to append, - * e.g., sb.append('foo', 'bar', 'baz'). - * @return {goog.string.StringBuffer} This same StringBuilder object. - */ -goog.string.StringBuffer.prototype.append = function(a1, opt_a2, var_args) { - - if (goog.userAgent.jscript.HAS_JSCRIPT) { - if (opt_a2 == null) { // no second argument (note: undefined == null) - // Array assignment is 2x faster than Array push. Also, use a1 - // directly to avoid arguments instantiation, another 2x improvement. - this.buffer_[this.bufferLength_++] = a1; - } else { - var arr = /**@type {Array}*/(this.buffer_); - arr.push.apply(arr, arguments); - this.bufferLength_ = this.buffer_.length; - } - - } else { - - // Use a1 directly to avoid arguments instantiation for single-arg case. - this.buffer_ += a1; - if (opt_a2 != null) { // no second argument (note: undefined == null) - for (var i = 1; i < arguments.length; i++) { - this.buffer_ += arguments[i]; - } - } - } - - return this; -}; - - -/** - * Clears the string. - */ -goog.string.StringBuffer.prototype.clear = function() { - - if (goog.userAgent.jscript.HAS_JSCRIPT) { - this.buffer_.length = 0; // reuse array to avoid creating new object - this.bufferLength_ = 0; - - } else { - this.buffer_ = ''; - } -}; - - -/** - * Returns the concatenated string. - * - * @return {string} The concatenated string. - */ -goog.string.StringBuffer.prototype.toString = function() { - - if (goog.userAgent.jscript.HAS_JSCRIPT) { - var str = this.buffer_.join(''); - // Given a string with the entire contents, simplify the StringBuilder by - // setting its contents to only be this string, rather than many fragments. - this.clear(); - if (str) { - this.append(str); - } - return str; - - } else { - return /** @type {string} */ (this.buffer_); - } -}; - - -if (!goog.soy) goog.soy = { - /** - * Helper function to render a Soy template and then set the - * output string as the innerHTML of an element. It is recommended - * to use this helper function instead of directly setting - * innerHTML in your hand-written code, so that it will be easier - * to audit the code for cross-site scripting vulnerabilities. - * - * @param {Function} template The Soy template defining element's content. - * @param {Object=} opt_templateData The data for the template. - * @param {Object=} opt_injectedData The injected data for the template. - * @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM - * nodes will be created. - */ - renderAsElement: function( - template, opt_templateData, opt_injectedData, opt_dom) { - return /** @type {!Element} */ (soyshim.$$renderWithWrapper_( - template, opt_templateData, opt_dom, true /* asElement */, - opt_injectedData)); - }, - /** - * Helper function to render a Soy template into a single node or - * a document fragment. If the rendered HTML string represents a - * single node, then that node is returned (note that this is - * *not* a fragment, despite them name of the method). Otherwise a - * document fragment is returned containing the rendered nodes. - * - * @param {Function} template The Soy template defining element's content. - * @param {Object=} opt_templateData The data for the template. - * @param {Object=} opt_injectedData The injected data for the template. - * @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM - * nodes will be created. - * @return {!Node} The resulting node or document fragment. - */ - renderAsFragment: function( - template, opt_templateData, opt_injectedData, opt_dom) { - return soyshim.$$renderWithWrapper_( - template, opt_templateData, opt_dom, false /* asElement */, - opt_injectedData); - }, - /** - * Helper function to render a Soy template and then set the output string as - * the innerHTML of an element. It is recommended to use this helper function - * instead of directly setting innerHTML in your hand-written code, so that it - * will be easier to audit the code for cross-site scripting vulnerabilities. - * - * NOTE: New code should consider using goog.soy.renderElement instead. - * - * @param {Element} element The element whose content we are rendering. - * @param {Function} template The Soy template defining the element's content. - * @param {Object=} opt_templateData The data for the template. - * @param {Object=} opt_injectedData The injected data for the template. - */ - renderElement: function( - element, template, opt_templateData, opt_injectedData) { - element.innerHTML = template(opt_templateData, null, opt_injectedData); - }, - data: {} -}; - - -/** - * A type of textual content. - * - * This is an enum of type Object so that these values are unforgeable. - * - * @enum {!Object} - */ -goog.soy.data.SanitizedContentKind = { - - /** - * A snippet of HTML that does not start or end inside a tag, comment, entity, - * or DOCTYPE; and that does not contain any executable code - * (JS, {@code }s, etc.) from a different trust domain. - */ - HTML: goog.DEBUG ? {sanitizedContentKindHtml: true} : {}, - - /** - * Executable Javascript code or expression, safe for insertion in a - * script-tag or event handler context, known to be free of any - * attacker-controlled scripts. This can either be side-effect-free - * Javascript (such as JSON) or Javascript that's entirely under Google's - * control. - */ - JS: goog.DEBUG ? {sanitizedContentJsChars: true} : {}, - - /** - * A sequence of code units that can appear between quotes (either kind) in a - * JS program without causing a parse error, and without causing any side - * effects. - *

- * The content should not contain unescaped quotes, newlines, or anything else - * that would cause parsing to fail or to cause a JS parser to finish the - * string its parsing inside the content. - *

- * The content must also not end inside an escape sequence ; no partial octal - * escape sequences or odd number of '{@code \}'s at the end. - */ - JS_STR_CHARS: goog.DEBUG ? {sanitizedContentJsStrChars: true} : {}, - - /** A properly encoded portion of a URI. */ - URI: goog.DEBUG ? {sanitizedContentUri: true} : {}, - - /** - * Repeated attribute names and values. For example, - * {@code dir="ltr" foo="bar" onclick="trustedFunction()" checked}. - */ - ATTRIBUTES: goog.DEBUG ? {sanitizedContentHtmlAttribute: true} : {}, - - // TODO: Consider separating rules, declarations, and values into - // separate types, but for simplicity, we'll treat explicitly blessed - // SanitizedContent as allowed in all of these contexts. - /** - * A CSS3 declaration, property, value or group of semicolon separated - * declarations. - */ - CSS: goog.DEBUG ? {sanitizedContentCss: true} : {}, - - /** - * Unsanitized plain-text content. - * - * This is effectively the "null" entry of this enum, and is sometimes used - * to explicitly mark content that should never be used unescaped. Since any - * string is safe to use as text, being of ContentKind.TEXT makes no - * guarantees about its safety in any other context such as HTML. - */ - TEXT: goog.DEBUG ? {sanitizedContentKindText: true} : {} -}; - - - -/** - * A string-like object that carries a content-type and a content direction. - * - * IMPORTANT! Do not create these directly, nor instantiate the subclasses. - * Instead, use a trusted, centrally reviewed library as endorsed by your team - * to generate these objects. Otherwise, you risk accidentally creating - * SanitizedContent that is attacker-controlled and gets evaluated unescaped in - * templates. - * - * @constructor - */ -goog.soy.data.SanitizedContent = function() { - throw Error('Do not instantiate directly'); -}; - - -/** - * The context in which this content is safe from XSS attacks. - * @type {goog.soy.data.SanitizedContentKind} - */ -goog.soy.data.SanitizedContent.prototype.contentKind; - - -/** - * The content's direction; null if unknown and thus to be estimated when - * necessary. - * @type {?goog.i18n.bidi.Dir} - */ -goog.soy.data.SanitizedContent.prototype.contentDir = null; - - -/** - * The already-safe content. - * @type {string} - */ -goog.soy.data.SanitizedContent.prototype.content; - - -/** @override */ -goog.soy.data.SanitizedContent.prototype.toString = function() { - return this.content; -}; - - -var soy = { esc: {} }; -var soydata = {}; -soydata.VERY_UNSAFE = {}; -var soyshim = { $$DEFAULT_TEMPLATE_DATA_: {} }; -/** - * Helper function to render a Soy template into a single node or a document - * fragment. If the rendered HTML string represents a single node, then that - * node is returned. Otherwise a document fragment is created and returned - * (wrapped in a DIV element if #opt_singleNode is true). - * - * @param {Function} template The Soy template defining the element's content. - * @param {Object=} opt_templateData The data for the template. - * @param {(goog.dom.DomHelper|Document)=} opt_dom The context in which DOM - * nodes will be created. - * @param {boolean=} opt_asElement Whether to wrap the fragment in an - * element if the template does not render a single element. If true, - * result is always an Element. - * @param {Object=} opt_injectedData The injected data for the template. - * @return {!Node} The resulting node or document fragment. - * @private - */ -soyshim.$$renderWithWrapper_ = function( - template, opt_templateData, opt_dom, opt_asElement, opt_injectedData) { - - var dom = opt_dom || document; - var wrapper = dom.createElement('div'); - wrapper.innerHTML = template( - opt_templateData || soyshim.$$DEFAULT_TEMPLATE_DATA_, undefined, - opt_injectedData); - - // If the template renders as a single element, return it. - if (wrapper.childNodes.length == 1) { - var firstChild = wrapper.firstChild; - if (!opt_asElement || firstChild.nodeType == 1 /* Element */) { - return /** @type {!Node} */ (firstChild); - } - } - - // If we're forcing it to be a single element, return the wrapper DIV. - if (opt_asElement) { - return wrapper; - } - - // Otherwise, create and return a fragment. - var fragment = dom.createDocumentFragment(); - while (wrapper.firstChild) { - fragment.appendChild(wrapper.firstChild); - } - return fragment; -}; - - -/** - * Strips str of any HTML mark-up and escapes. Imprecise in several ways, but - * precision is not very important, since the result is only meant to be used - * for directionality detection. - * Based on goog.i18n.bidi.stripHtmlIfNeeded_(). - * @param {string} str The string to be stripped. - * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped. - * Default: false. - * @return {string} The stripped string. - * @private - */ -soyshim.$$bidiStripHtmlIfNecessary_ = function(str, opt_isHtml) { - return opt_isHtml ? str.replace(soyshim.$$BIDI_HTML_SKIP_RE_, '') : str; -}; - - -/** - * Simplified regular expression for am HTML tag (opening or closing) or an HTML - * escape - the things we want to skip over in order to ignore their ltr - * characters. - * Copied from goog.i18n.bidi.htmlSkipReg_. - * @type {RegExp} - * @private - */ -soyshim.$$BIDI_HTML_SKIP_RE_ = /<[^>]*>|&[^;]+;/g; - - -/** - * A practical pattern to identify strong LTR character. This pattern is not - * theoretically correct according to unicode standard. It is simplified for - * performance and small code size. - * Copied from goog.i18n.bidi.ltrChars_. - * @type {string} - * @private - */ -soyshim.$$bidiLtrChars_ = - 'A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02B8\u0300-\u0590\u0800-\u1FFF' + - '\u200E\u2C00-\uFB1C\uFE00-\uFE6F\uFEFD-\uFFFF'; - - -/** - * A practical pattern to identify strong RTL character. This pattern is not - * theoretically correct according to unicode standard. It is simplified for - * performance and small code size. - * Copied from goog.i18n.bidi.rtlChars_. - * @type {string} - * @private - */ -soyshim.$$bidiRtlChars_ = '\u0591-\u07FF\u200F\uFB1D-\uFDFF\uFE70-\uFEFC'; - - -/** - * Regular expressions to check if a piece of text is of RTL directionality - * on first character with strong directionality. - * Based on goog.i18n.bidi.rtlDirCheckRe_. - * @type {RegExp} - * @private - */ -soyshim.$$bidiRtlDirCheckRe_ = new RegExp( - '^[^' + soyshim.$$bidiLtrChars_ + ']*[' + soyshim.$$bidiRtlChars_ + ']'); - - -/** - * Regular expression to check for LTR characters. - * Based on goog.i18n.bidi.ltrCharReg_. - * @type {RegExp} - * @private - */ -soyshim.$$bidiLtrCharRe_ = new RegExp('[' + soyshim.$$bidiLtrChars_ + ']'); - - -/** - * Regular expression to check if a string looks like something that must - * always be LTR even in RTL text, e.g. a URL. When estimating the - * directionality of text containing these, we treat these as weakly LTR, - * like numbers. - * Copied from goog.i18n.bidi.isRequiredLtrRe_. - * @type {RegExp} - * @private - */ -soyshim.$$bidiIsRequiredLtrRe_ = /^http:\/\/.*/; - - -/** - * Regular expression to check if a string contains any numerals. Used to - * differentiate between completely neutral strings and those containing - * numbers, which are weakly LTR. - * Copied from goog.i18n.bidi.hasNumeralsRe_. - * @type {RegExp} - * @private - */ -soyshim.$$bidiHasNumeralsRe_ = /\d/; - - -/** - * Regular expression to split a string into "words" for directionality - * estimation based on relative word counts. - * Copied from goog.i18n.bidi.wordSeparatorRe_. - * @type {RegExp} - * @private - */ -soyshim.$$bidiWordSeparatorRe_ = /\s+/; - - -/** - * This constant controls threshold of rtl directionality. - * Copied from goog.i18n.bidi.rtlDetectionThreshold_. - * @type {number} - * @private - */ -soyshim.$$bidiRtlDetectionThreshold_ = 0.40; - -/** - * Regular expressions to check if the last strongly-directional character in a - * piece of text is LTR. - * Based on goog.i18n.bidi.ltrExitDirCheckRe_. - * @type {RegExp} - * @private - */ -soyshim.$$bidiLtrExitDirCheckRe_ = new RegExp( - '[' + soyshim.$$bidiLtrChars_ + '][^' + soyshim.$$bidiRtlChars_ + ']*$'); - - -/** - * Regular expressions to check if the last strongly-directional character in a - * piece of text is RTL. - * Based on goog.i18n.bidi.rtlExitDirCheckRe_. - * @type {RegExp} - * @private - */ -soyshim.$$bidiRtlExitDirCheckRe_ = new RegExp( - '[' + soyshim.$$bidiRtlChars_ + '][^' + soyshim.$$bidiLtrChars_ + ']*$'); - - -/** - * Check if the exit directionality a piece of text is LTR, i.e. if the last - * strongly-directional character in the string is LTR. - * Based on goog.i18n.bidi.endsWithLtr(). - * @param {string} str string being checked. - * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped. - * Default: false. - * @return {boolean} Whether LTR exit directionality was detected. - * @private - */ -soyshim.$$bidiIsLtrExitText_ = function(str, opt_isHtml) { - str = soyshim.$$bidiStripHtmlIfNecessary_(str, opt_isHtml); - return soyshim.$$bidiLtrExitDirCheckRe_.test(str); -}; - - -/** - * Check if the exit directionality a piece of text is RTL, i.e. if the last - * strongly-directional character in the string is RTL. - * Based on goog.i18n.bidi.endsWithRtl(). - * @param {string} str string being checked. - * @param {boolean=} opt_isHtml Whether str is HTML / HTML-escaped. - * Default: false. - * @return {boolean} Whether RTL exit directionality was detected. - * @private - */ -soyshim.$$bidiIsRtlExitText_ = function(str, opt_isHtml) { - str = soyshim.$$bidiStripHtmlIfNecessary_(str, opt_isHtml); - return soyshim.$$bidiRtlExitDirCheckRe_.test(str); -}; - - -// ============================================================================= -// COPIED FROM soyutils_usegoog.js - - -// ----------------------------------------------------------------------------- -// StringBuilder (compatible with the 'stringbuilder' code style). - - -/** - * Utility class to facilitate much faster string concatenation in IE, - * using Array.join() rather than the '+' operator. For other browsers - * we simply use the '+' operator. - * - * @param {Object} var_args Initial items to append, - * e.g., new soy.StringBuilder('foo', 'bar'). - * @constructor - */ -soy.StringBuilder = goog.string.StringBuffer; - - -// ----------------------------------------------------------------------------- -// soydata: Defines typed strings, e.g. an HTML string {@code "ac"} is -// semantically distinct from the plain text string {@code "ac"} and smart -// templates can take that distinction into account. - -/** - * A type of textual content. - * - * This is an enum of type Object so that these values are unforgeable. - * - * @enum {!Object} - */ -soydata.SanitizedContentKind = goog.soy.data.SanitizedContentKind; - - -/** - * Checks whether a given value is of a given content kind. - * - * @param {*} value The value to be examined. - * @param {soydata.SanitizedContentKind} contentKind The desired content - * kind. - * @return {boolean} Whether the given value is of the given kind. - * @private - */ -soydata.isContentKind = function(value, contentKind) { - // TODO(user): This function should really include the assert on - // value.constructor that is currently sprinkled at most of the call sites. - // Unfortunately, that would require a (debug-mode-only) switch statement. - // TODO(user): Perhaps we should get rid of the contentKind property - // altogether and only at the constructor. - return value != null && value.contentKind === contentKind; -}; - - -/** - * Returns a given value's contentDir property, constrained to a - * goog.i18n.bidi.Dir value or null. Returns null if the value is null, - * undefined, a primitive or does not have a contentDir property, or the - * property's value is not 1 (for LTR), -1 (for RTL), or 0 (for neutral). - * - * @param {*} value The value whose contentDir property, if any, is to - * be returned. - * @return {?goog.i18n.bidi.Dir} The contentDir property. - */ -soydata.getContentDir = function(value) { - if (value != null) { - switch (value.contentDir) { - case goog.i18n.bidi.Dir.LTR: - return goog.i18n.bidi.Dir.LTR; - case goog.i18n.bidi.Dir.RTL: - return goog.i18n.bidi.Dir.RTL; - case goog.i18n.bidi.Dir.NEUTRAL: - return goog.i18n.bidi.Dir.NEUTRAL; - } - } - return null; -}; - - -/** - * Content of type {@link soydata.SanitizedContentKind.HTML}. - * - * The content is a string of HTML that can safely be embedded in a PCDATA - * context in your app. If you would be surprised to find that an HTML - * sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs) and - * you wouldn't write a template that produces {@code s} on security or privacy - * grounds, then don't pass {@code s} here. The default content direction is - * unknown, i.e. to be estimated when necessary. - * - * @constructor - * @extends {goog.soy.data.SanitizedContent} - */ -soydata.SanitizedHtml = function() { - goog.soy.data.SanitizedContent.call(this); // Throws an exception. -}; -goog.inherits(soydata.SanitizedHtml, goog.soy.data.SanitizedContent); - -/** @override */ -soydata.SanitizedHtml.prototype.contentKind = soydata.SanitizedContentKind.HTML; - -/** - * Returns a SanitizedHtml object for a particular value. The content direction - * is preserved. - * - * This HTML-escapes the value unless it is already SanitizedHtml. - * - * @param {*} value The value to convert. If it is already a SanitizedHtml - * object, it is left alone. - * @return {!soydata.SanitizedHtml} A SanitizedHtml object derived from the - * stringified value. It is escaped unless the input is SanitizedHtml. - */ -soydata.SanitizedHtml.from = function(value) { - // The check is soydata.isContentKind() inlined for performance. - if (value != null && - value.contentKind === soydata.SanitizedContentKind.HTML) { - goog.asserts.assert(value.constructor === soydata.SanitizedHtml); - return /** @type {!soydata.SanitizedHtml} */ (value); - } - return soydata.VERY_UNSAFE.ordainSanitizedHtml( - soy.esc.$$escapeHtmlHelper(String(value)), soydata.getContentDir(value)); -}; - - -/** - * Content of type {@link soydata.SanitizedContentKind.JS}. - * - * The content is Javascript source that when evaluated does not execute any - * attacker-controlled scripts. The content direction is LTR. - * - * @constructor - * @extends {goog.soy.data.SanitizedContent} - */ -soydata.SanitizedJs = function() { - goog.soy.data.SanitizedContent.call(this); // Throws an exception. -}; -goog.inherits(soydata.SanitizedJs, goog.soy.data.SanitizedContent); - -/** @override */ -soydata.SanitizedJs.prototype.contentKind = - soydata.SanitizedContentKind.JS; - -/** @override */ -soydata.SanitizedJs.prototype.contentDir = goog.i18n.bidi.Dir.LTR; - - -/** - * Content of type {@link soydata.SanitizedContentKind.JS_STR_CHARS}. - * - * The content can be safely inserted as part of a single- or double-quoted - * string without terminating the string. The default content direction is - * unknown, i.e. to be estimated when necessary. - * - * @constructor - * @extends {goog.soy.data.SanitizedContent} - */ -soydata.SanitizedJsStrChars = function() { - goog.soy.data.SanitizedContent.call(this); // Throws an exception. -}; -goog.inherits(soydata.SanitizedJsStrChars, goog.soy.data.SanitizedContent); - -/** @override */ -soydata.SanitizedJsStrChars.prototype.contentKind = - soydata.SanitizedContentKind.JS_STR_CHARS; - -/** - * Content of type {@link soydata.SanitizedContentKind.URI}. - * - * The content is a URI chunk that the caller knows is safe to emit in a - * template. The content direction is LTR. - * - * @constructor - * @extends {goog.soy.data.SanitizedContent} - */ -soydata.SanitizedUri = function() { - goog.soy.data.SanitizedContent.call(this); // Throws an exception. -}; -goog.inherits(soydata.SanitizedUri, goog.soy.data.SanitizedContent); - -/** @override */ -soydata.SanitizedUri.prototype.contentKind = soydata.SanitizedContentKind.URI; - -/** @override */ -soydata.SanitizedUri.prototype.contentDir = goog.i18n.bidi.Dir.LTR; - - -/** - * Content of type {@link soydata.SanitizedContentKind.ATTRIBUTES}. - * - * The content should be safely embeddable within an open tag, such as a - * key="value" pair. The content direction is LTR. - * - * @constructor - * @extends {goog.soy.data.SanitizedContent} - */ -soydata.SanitizedHtmlAttribute = function() { - goog.soy.data.SanitizedContent.call(this); // Throws an exception. -}; -goog.inherits(soydata.SanitizedHtmlAttribute, goog.soy.data.SanitizedContent); - -/** @override */ -soydata.SanitizedHtmlAttribute.prototype.contentKind = - soydata.SanitizedContentKind.ATTRIBUTES; - -/** @override */ -soydata.SanitizedHtmlAttribute.prototype.contentDir = goog.i18n.bidi.Dir.LTR; - - -/** - * Content of type {@link soydata.SanitizedContentKind.CSS}. - * - * The content is non-attacker-exploitable CSS, such as {@code color:#c3d9ff}. - * The content direction is LTR. - * - * @constructor - * @extends {goog.soy.data.SanitizedContent} - */ -soydata.SanitizedCss = function() { - goog.soy.data.SanitizedContent.call(this); // Throws an exception. -}; -goog.inherits(soydata.SanitizedCss, goog.soy.data.SanitizedContent); - -/** @override */ -soydata.SanitizedCss.prototype.contentKind = - soydata.SanitizedContentKind.CSS; - -/** @override */ -soydata.SanitizedCss.prototype.contentDir = goog.i18n.bidi.Dir.LTR; - - -/** - * Unsanitized plain text string. - * - * While all strings are effectively safe to use as a plain text, there are no - * guarantees about safety in any other context such as HTML. This is - * sometimes used to mark that should never be used unescaped. - * - * @param {*} content Plain text with no guarantees. - * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if - * unknown and thus to be estimated when necessary. Default: null. - * @constructor - * @extends {goog.soy.data.SanitizedContent} - */ -soydata.UnsanitizedText = function(content, opt_contentDir) { - /** @override */ - this.content = String(content); - this.contentDir = opt_contentDir != null ? opt_contentDir : null; -}; -goog.inherits(soydata.UnsanitizedText, goog.soy.data.SanitizedContent); - -/** @override */ -soydata.UnsanitizedText.prototype.contentKind = - soydata.SanitizedContentKind.TEXT; - - -/** - * Empty string, used as a type in Soy templates. - * @enum {string} - * @private - */ -soydata.$$EMPTY_STRING_ = { - VALUE: '' -}; - - -/** - * Creates a factory for SanitizedContent types. - * - * This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can - * instantiate Sanitized* classes, without making the Sanitized* constructors - * publicly usable. Requiring all construction to use the VERY_UNSAFE names - * helps callers and their reviewers easily tell that creating SanitizedContent - * is not always safe and calls for careful review. - * - * @param {function(new: T)} ctor A constructor. - * @return {!function(*, ?goog.i18n.bidi.Dir=): T} A factory that takes - * content and an optional content direction and returns a new instance. If - * the content direction is undefined, ctor.prototype.contentDir is used. - * @template T - * @private - */ -soydata.$$makeSanitizedContentFactory_ = function(ctor) { - /** @type {function(new: goog.soy.data.SanitizedContent)} */ - function InstantiableCtor() {} - InstantiableCtor.prototype = ctor.prototype; - /** - * Creates a ctor-type SanitizedContent instance. - * - * @param {*} content The content to put in the instance. - * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction. If - * undefined, ctor.prototype.contentDir is used. - * @return {goog.soy.data.SanitizedContent} The new instance. It is actually - * of type T above (ctor's type, a descendant of SanitizedContent), but - * there is no way to express that here. - */ - function sanitizedContentFactory(content, opt_contentDir) { - var result = new InstantiableCtor(); - result.content = String(content); - if (opt_contentDir !== undefined) { - result.contentDir = opt_contentDir; - } - return result; - } - return sanitizedContentFactory; -}; - - -/** - * Creates a factory for SanitizedContent types that should always have their - * default directionality. - * - * This is a hack so that the soydata.VERY_UNSAFE.ordainSanitized* can - * instantiate Sanitized* classes, without making the Sanitized* constructors - * publicly usable. Requiring all construction to use the VERY_UNSAFE names - * helps callers and their reviewers easily tell that creating SanitizedContent - * is not always safe and calls for careful review. - * - * @param {function(new: T, string)} ctor A constructor. - * @return {!function(*): T} A factory that takes content and returns a new - * instance (with default directionality, i.e. ctor.prototype.contentDir). - * @template T - * @private - */ -soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_ = function(ctor) { - /** @type {function(new: goog.soy.data.SanitizedContent)} */ - function InstantiableCtor() {} - InstantiableCtor.prototype = ctor.prototype; - /** - * Creates a ctor-type SanitizedContent instance. - * - * @param {*} content The content to put in the instance. - * @return {goog.soy.data.SanitizedContent} The new instance. It is actually - * of type T above (ctor's type, a descendant of SanitizedContent), but - * there is no way to express that here. - */ - function sanitizedContentFactory(content) { - var result = new InstantiableCtor(); - result.content = String(content); - return result; - } - return sanitizedContentFactory; -}; - - -// ----------------------------------------------------------------------------- -// Sanitized content ordainers. Please use these with extreme caution (with the -// exception of markUnsanitizedText). A good recommendation is to limit usage -// of these to just a handful of files in your source tree where usages can be -// carefully audited. - - -/** - * Protects a string from being used in an noAutoescaped context. - * - * This is useful for content where there is significant risk of accidental - * unescaped usage in a Soy template. A great case is for user-controlled - * data that has historically been a source of vulernabilities. - * - * @param {*} content Text to protect. - * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if - * unknown and thus to be estimated when necessary. Default: null. - * @return {!soydata.UnsanitizedText} A wrapper that is rejected by the - * Soy noAutoescape print directive. - */ -soydata.markUnsanitizedText = function(content, opt_contentDir) { - return new soydata.UnsanitizedText(content, opt_contentDir); -}; - - -/** - * Takes a leap of faith that the provided content is "safe" HTML. - * - * @param {*} content A string of HTML that can safely be embedded in - * a PCDATA context in your app. If you would be surprised to find that an - * HTML sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs) - * and you wouldn't write a template that produces {@code s} on security or - * privacy grounds, then don't pass {@code s} here. - * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if - * unknown and thus to be estimated when necessary. Default: null. - * @return {!soydata.SanitizedHtml} Sanitized content wrapper that - * indicates to Soy not to escape when printed as HTML. - */ -soydata.VERY_UNSAFE.ordainSanitizedHtml = - soydata.$$makeSanitizedContentFactory_(soydata.SanitizedHtml); - - -/** - * Takes a leap of faith that the provided content is "safe" (non-attacker- - * controlled, XSS-free) Javascript. - * - * @param {*} content Javascript source that when evaluated does not - * execute any attacker-controlled scripts. - * @return {!soydata.SanitizedJs} Sanitized content wrapper that indicates to - * Soy not to escape when printed as Javascript source. - */ -soydata.VERY_UNSAFE.ordainSanitizedJs = - soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_( - soydata.SanitizedJs); - - -// TODO: This function is probably necessary, either externally or internally -// as an implementation detail. Generally, plain text will always work here, -// as there's no harm to unescaping the string and then re-escaping when -// finally printed. -/** - * Takes a leap of faith that the provided content can be safely embedded in - * a Javascript string without re-escaping. - * - * @param {*} content Content that can be safely inserted as part of a - * single- or double-quoted string without terminating the string. - * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if - * unknown and thus to be estimated when necessary. Default: null. - * @return {!soydata.SanitizedJsStrChars} Sanitized content wrapper that - * indicates to Soy not to escape when printed in a JS string. - */ -soydata.VERY_UNSAFE.ordainSanitizedJsStrChars = - soydata.$$makeSanitizedContentFactory_(soydata.SanitizedJsStrChars); - - -/** - * Takes a leap of faith that the provided content is "safe" to use as a URI - * in a Soy template. - * - * This creates a Soy SanitizedContent object which indicates to Soy there is - * no need to escape it when printed as a URI (e.g. in an href or src - * attribute), such as if it's already been encoded or if it's a Javascript: - * URI. - * - * @param {*} content A chunk of URI that the caller knows is safe to - * emit in a template. - * @return {!soydata.SanitizedUri} Sanitized content wrapper that indicates to - * Soy not to escape or filter when printed in URI context. - */ -soydata.VERY_UNSAFE.ordainSanitizedUri = - soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_( - soydata.SanitizedUri); - - -/** - * Takes a leap of faith that the provided content is "safe" to use as an - * HTML attribute. - * - * @param {*} content An attribute name and value, such as - * {@code dir="ltr"}. - * @return {!soydata.SanitizedHtmlAttribute} Sanitized content wrapper that - * indicates to Soy not to escape when printed as an HTML attribute. - */ -soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute = - soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_( - soydata.SanitizedHtmlAttribute); - - -/** - * Takes a leap of faith that the provided content is "safe" to use as CSS - * in a style attribute or block. - * - * @param {*} content CSS, such as {@code color:#c3d9ff}. - * @return {!soydata.SanitizedCss} Sanitized CSS wrapper that indicates to - * Soy there is no need to escape or filter when printed in CSS context. - */ -soydata.VERY_UNSAFE.ordainSanitizedCss = - soydata.$$makeSanitizedContentFactoryWithDefaultDirOnly_( - soydata.SanitizedCss); - - -// ----------------------------------------------------------------------------- -// Public utilities. - - -/** - * Helper function to render a Soy template and then set the output string as - * the innerHTML of an element. It is recommended to use this helper function - * instead of directly setting innerHTML in your hand-written code, so that it - * will be easier to audit the code for cross-site scripting vulnerabilities. - * - * NOTE: New code should consider using goog.soy.renderElement instead. - * - * @param {Element} element The element whose content we are rendering. - * @param {Function} template The Soy template defining the element's content. - * @param {Object=} opt_templateData The data for the template. - * @param {Object=} opt_injectedData The injected data for the template. - */ -soy.renderElement = goog.soy.renderElement; - - -/** - * Helper function to render a Soy template into a single node or a document - * fragment. If the rendered HTML string represents a single node, then that - * node is returned (note that this is *not* a fragment, despite them name of - * the method). Otherwise a document fragment is returned containing the - * rendered nodes. - * - * NOTE: New code should consider using goog.soy.renderAsFragment - * instead (note that the arguments are different). - * - * @param {Function} template The Soy template defining the element's content. - * @param {Object=} opt_templateData The data for the template. - * @param {Document=} opt_document The document used to create DOM nodes. If not - * specified, global document object is used. - * @param {Object=} opt_injectedData The injected data for the template. - * @return {!Node} The resulting node or document fragment. - */ -soy.renderAsFragment = function( - template, opt_templateData, opt_document, opt_injectedData) { - return goog.soy.renderAsFragment( - template, opt_templateData, opt_injectedData, - new goog.dom.DomHelper(opt_document)); -}; - - -/** - * Helper function to render a Soy template into a single node. If the rendered - * HTML string represents a single node, then that node is returned. Otherwise, - * a DIV element is returned containing the rendered nodes. - * - * NOTE: New code should consider using goog.soy.renderAsElement - * instead (note that the arguments are different). - * - * @param {Function} template The Soy template defining the element's content. - * @param {Object=} opt_templateData The data for the template. - * @param {Document=} opt_document The document used to create DOM nodes. If not - * specified, global document object is used. - * @param {Object=} opt_injectedData The injected data for the template. - * @return {!Element} Rendered template contents, wrapped in a parent DIV - * element if necessary. - */ -soy.renderAsElement = function( - template, opt_templateData, opt_document, opt_injectedData) { - return goog.soy.renderAsElement( - template, opt_templateData, opt_injectedData, - new goog.dom.DomHelper(opt_document)); -}; - - -// ----------------------------------------------------------------------------- -// Below are private utilities to be used by Soy-generated code only. - - -/** - * Whether the locale is right-to-left. - * - * @type {boolean} - */ -soy.$$IS_LOCALE_RTL = goog.i18n.bidi.IS_RTL; - - -/** - * Builds an augmented map. The returned map will contain mappings from both - * the base map and the additional map. If the same key appears in both, then - * the value from the additional map will be visible, while the value from the - * base map will be hidden. The base map will be used, but not modified. - * - * @param {!Object} baseMap The original map to augment. - * @param {!Object} additionalMap A map containing the additional mappings. - * @return {!Object} An augmented map containing both the original and - * additional mappings. - */ -soy.$$augmentMap = function(baseMap, additionalMap) { - - // Create a new map whose '__proto__' field is set to baseMap. - /** @constructor */ - function TempCtor() {} - TempCtor.prototype = baseMap; - var augmentedMap = new TempCtor(); - - // Add the additional mappings to the new map. - for (var key in additionalMap) { - augmentedMap[key] = additionalMap[key]; - } - - return augmentedMap; -}; - - -/** - * Checks that the given map key is a string. - * @param {*} key Key to check. - * @return {string} The given key. - */ -soy.$$checkMapKey = function(key) { - // TODO: Support map literal with nonstring key. - if ((typeof key) != 'string') { - throw Error( - 'Map literal\'s key expression must evaluate to string' + - ' (encountered type "' + (typeof key) + '").'); - } - return key; -}; - - -/** - * Gets the keys in a map as an array. There are no guarantees on the order. - * @param {Object} map The map to get the keys of. - * @return {Array} The array of keys in the given map. - */ -soy.$$getMapKeys = function(map) { - var mapKeys = []; - for (var key in map) { - mapKeys.push(key); - } - return mapKeys; -}; - - -/** - * Gets a consistent unique id for the given delegate template name. Two calls - * to this function will return the same id if and only if the input names are - * the same. - * - *

Important: This function must always be called with a string constant. - * - *

If Closure Compiler is not being used, then this is just this identity - * function. If Closure Compiler is being used, then each call to this function - * will be replaced with a short string constant, which will be consistent per - * input name. - * - * @param {string} delTemplateName The delegate template name for which to get a - * consistent unique id. - * @return {string} A unique id that is consistent per input name. - * - * @consistentIdGenerator - */ -soy.$$getDelTemplateId = function(delTemplateName) { - return delTemplateName; -}; - - -/** - * Map from registered delegate template key to the priority of the - * implementation. - * @type {Object} - * @private - */ -soy.$$DELEGATE_REGISTRY_PRIORITIES_ = {}; - -/** - * Map from registered delegate template key to the implementation function. - * @type {Object} - * @private - */ -soy.$$DELEGATE_REGISTRY_FUNCTIONS_ = {}; - - -/** - * Registers a delegate implementation. If the same delegate template key (id - * and variant) has been registered previously, then priority values are - * compared and only the higher priority implementation is stored (if - * priorities are equal, an error is thrown). - * - * @param {string} delTemplateId The delegate template id. - * @param {string} delTemplateVariant The delegate template variant (can be - * empty string). - * @param {number} delPriority The implementation's priority value. - * @param {Function} delFn The implementation function. - */ -soy.$$registerDelegateFn = function( - delTemplateId, delTemplateVariant, delPriority, delFn) { - - var mapKey = 'key_' + delTemplateId + ':' + delTemplateVariant; - var currPriority = soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey]; - if (currPriority === undefined || delPriority > currPriority) { - // Registering new or higher-priority function: replace registry entry. - soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey] = delPriority; - soy.$$DELEGATE_REGISTRY_FUNCTIONS_[mapKey] = delFn; - } else if (delPriority == currPriority) { - // Registering same-priority function: error. - throw Error( - 'Encountered two active delegates with the same priority ("' + - delTemplateId + ':' + delTemplateVariant + '").'); - } else { - // Registering lower-priority function: do nothing. - } -}; - - -/** - * Retrieves the (highest-priority) implementation that has been registered for - * a given delegate template key (id and variant). If no implementation has - * been registered for the key, then the fallback is the same id with empty - * variant. If the fallback is also not registered, and allowsEmptyDefault is - * true, then returns an implementation that is equivalent to an empty template - * (i.e. rendered output would be empty string). - * - * @param {string} delTemplateId The delegate template id. - * @param {string|number} delTemplateVariant The delegate template variant (can - * be an empty string, or a number when a global is used). - * @param {boolean} allowsEmptyDefault Whether to default to the empty template - * function if there's no active implementation. - * @return {Function} The retrieved implementation function. - */ -soy.$$getDelegateFn = function( - delTemplateId, delTemplateVariant, allowsEmptyDefault) { - - var delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_[ - 'key_' + delTemplateId + ':' + delTemplateVariant]; - if (! delFn && delTemplateVariant != '') { - // Fallback to empty variant. - delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_['key_' + delTemplateId + ':']; - } - - if (delFn) { - return delFn; - } else if (allowsEmptyDefault) { - return soy.$$EMPTY_TEMPLATE_FN_; - } else { - throw Error( - 'Found no active impl for delegate call to "' + delTemplateId + ':' + - delTemplateVariant + '" (and not allowemptydefault="true").'); - } -}; - - -/** - * Private helper soy.$$getDelegateFn(). This is the empty template function - * that is returned whenever there's no delegate implementation found. - * - * @param {Object=} opt_data - * @param {soy.StringBuilder=} opt_sb - * @param {Object=} opt_ijData - * @return {string} - * @private - */ -soy.$$EMPTY_TEMPLATE_FN_ = function(opt_data, opt_sb, opt_ijData) { - return ''; -}; - - -// ----------------------------------------------------------------------------- -// Internal sanitized content wrappers. - - -/** - * Creates a SanitizedContent factory for SanitizedContent types for internal - * Soy let and param blocks. - * - * This is a hack within Soy so that SanitizedContent objects created via let - * and param blocks will truth-test as false if they are empty string. - * Tricking the Javascript runtime to treat empty SanitizedContent as falsey is - * not possible, and changing the Soy compiler to wrap every boolean statement - * for just this purpose is impractical. Instead, we just avoid wrapping empty - * string as SanitizedContent, since it's a no-op for empty strings anyways. - * - * @param {function(new: T)} ctor A constructor. - * @return {!function(*, ?goog.i18n.bidi.Dir=): (T|soydata.$$EMPTY_STRING_)} - * A factory that takes content and an optional content direction and - * returns a new instance, or an empty string. If the content direction is - * undefined, ctor.prototype.contentDir is used. - * @template T - * @private - */ -soydata.$$makeSanitizedContentFactoryForInternalBlocks_ = function(ctor) { - /** @type {function(new: goog.soy.data.SanitizedContent)} */ - function InstantiableCtor() {} - InstantiableCtor.prototype = ctor.prototype; - /** - * Creates a ctor-type SanitizedContent instance. - * - * @param {*} content The content to put in the instance. - * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction. If - * undefined, ctor.prototype.contentDir is used. - * @return {goog.soy.data.SanitizedContent|soydata.$$EMPTY_STRING_} The new - * instance, or an empty string. A new instance is actually of type T - * above (ctor's type, a descendant of SanitizedContent), but there's no - * way to express that here. - */ - function sanitizedContentFactory(content, opt_contentDir) { - var contentString = String(content); - if (!contentString) { - return soydata.$$EMPTY_STRING_.VALUE; - } - var result = new InstantiableCtor(); - result.content = String(content); - if (opt_contentDir !== undefined) { - result.contentDir = opt_contentDir; - } - return result; - } - return sanitizedContentFactory; -}; - - -/** - * Creates a SanitizedContent factory for SanitizedContent types that should - * always have their default directionality for internal Soy let and param - * blocks. - * - * This is a hack within Soy so that SanitizedContent objects created via let - * and param blocks will truth-test as false if they are empty string. - * Tricking the Javascript runtime to treat empty SanitizedContent as falsey is - * not possible, and changing the Soy compiler to wrap every boolean statement - * for just this purpose is impractical. Instead, we just avoid wrapping empty - * string as SanitizedContent, since it's a no-op for empty strings anyways. - * - * @param {function(new: T)} ctor A constructor. - * @return {!function(*): (T|soydata.$$EMPTY_STRING_)} A - * factory that takes content and returns a - * new instance (with default directionality, i.e. - * ctor.prototype.contentDir), or an empty string. - * @template T - * @private - */ -soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_ = - function(ctor) { - /** @type {function(new: goog.soy.data.SanitizedContent)} */ - function InstantiableCtor() {} - InstantiableCtor.prototype = ctor.prototype; - /** - * Creates a ctor-type SanitizedContent instance. - * - * @param {*} content The content to put in the instance. - * @return {goog.soy.data.SanitizedContent|soydata.$$EMPTY_STRING_} The new - * instance, or an empty string. A new instance is actually of type T - * above (ctor's type, a descendant of SanitizedContent), but there's no - * way to express that here. - */ - function sanitizedContentFactory(content) { - var contentString = String(content); - if (!contentString) { - return soydata.$$EMPTY_STRING_.VALUE; - } - var result = new InstantiableCtor(); - result.content = String(content); - return result; - } - return sanitizedContentFactory; -}; - - -/** - * Creates kind="text" block contents (internal use only). - * - * @param {*} content Text. - * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if - * unknown and thus to be estimated when necessary. Default: null. - * @return {!soydata.UnsanitizedText|soydata.$$EMPTY_STRING_} Wrapped result. - */ -soydata.$$markUnsanitizedTextForInternalBlocks = function( - content, opt_contentDir) { - var contentString = String(content); - if (!contentString) { - return soydata.$$EMPTY_STRING_.VALUE; - } - return new soydata.UnsanitizedText(contentString, opt_contentDir); -}; - - -/** - * Creates kind="html" block contents (internal use only). - * - * @param {*} content Text. - * @param {?goog.i18n.bidi.Dir=} opt_contentDir The content direction; null if - * unknown and thus to be estimated when necessary. Default: null. - * @return {soydata.SanitizedHtml|soydata.$$EMPTY_STRING_} Wrapped result. - */ -soydata.VERY_UNSAFE.$$ordainSanitizedHtmlForInternalBlocks = - soydata.$$makeSanitizedContentFactoryForInternalBlocks_( - soydata.SanitizedHtml); - - -/** - * Creates kind="js" block contents (internal use only). - * - * @param {*} content Text. - * @return {soydata.SanitizedJs|soydata.$$EMPTY_STRING_} Wrapped result. - */ -soydata.VERY_UNSAFE.$$ordainSanitizedJsForInternalBlocks = - soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_( - soydata.SanitizedJs); - - -/** - * Creates kind="uri" block contents (internal use only). - * - * @param {*} content Text. - * @return {soydata.SanitizedUri|soydata.$$EMPTY_STRING_} Wrapped result. - */ -soydata.VERY_UNSAFE.$$ordainSanitizedUriForInternalBlocks = - soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_( - soydata.SanitizedUri); - - -/** - * Creates kind="attributes" block contents (internal use only). - * - * @param {*} content Text. - * @return {soydata.SanitizedHtmlAttribute|soydata.$$EMPTY_STRING_} Wrapped - * result. - */ -soydata.VERY_UNSAFE.$$ordainSanitizedAttributesForInternalBlocks = - soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_( - soydata.SanitizedHtmlAttribute); - - -/** - * Creates kind="css" block contents (internal use only). - * - * @param {*} content Text. - * @return {soydata.SanitizedCss|soydata.$$EMPTY_STRING_} Wrapped result. - */ -soydata.VERY_UNSAFE.$$ordainSanitizedCssForInternalBlocks = - soydata.$$makeSanitizedContentFactoryWithDefaultDirOnlyForInternalBlocks_( - soydata.SanitizedCss); - - -// ----------------------------------------------------------------------------- -// Escape/filter/normalize. - - -/** - * Returns a SanitizedHtml object for a particular value. The content direction - * is preserved. - * - * This HTML-escapes the value unless it is already SanitizedHtml. Escapes - * double quote '"' in addition to '&', '<', and '>' so that a string can be - * included in an HTML tag attribute value within double quotes. - * - * @param {*} value The value to convert. If it is already a SanitizedHtml - * object, it is left alone. - * @return {!soydata.SanitizedHtml} An escaped version of value. - */ -soy.$$escapeHtml = function(value) { - return soydata.SanitizedHtml.from(value); -}; - - -/** - * Strips unsafe tags to convert a string of untrusted HTML into HTML that - * is safe to embed. The content direction is preserved. - * - * @param {*} value The string-like value to be escaped. May not be a string, - * but the value will be coerced to a string. - * @return {!soydata.SanitizedHtml} A sanitized and normalized version of value. - */ -soy.$$cleanHtml = function(value) { - if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) { - goog.asserts.assert(value.constructor === soydata.SanitizedHtml); - return /** @type {!soydata.SanitizedHtml} */ (value); - } - return soydata.VERY_UNSAFE.ordainSanitizedHtml( - soy.$$stripHtmlTags(value, soy.esc.$$SAFE_TAG_ALLOWLIST_), - soydata.getContentDir(value)); -}; - - -/** - * Escapes HTML special characters in a string so that it can be embedded in - * RCDATA. - *

- * Escapes HTML special characters so that the value will not prematurely end - * the body of a tag like {@code }. - *

- * Will normalize known safe HTML to make sure that sanitized HTML (which could - * contain an innocuous {@code } don't prematurely end an RCDATA - * element. - * - * @param {*} value The string-like value to be escaped. May not be a string, - * but the value will be coerced to a string. - * @return {string} An escaped version of value. - */ -soy.$$escapeHtmlRcdata = function(value) { - if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) { - goog.asserts.assert(value.constructor === soydata.SanitizedHtml); - return soy.esc.$$normalizeHtmlHelper(value.content); - } - return soy.esc.$$escapeHtmlHelper(value); -}; - - -/** - * Matches any/only HTML5 void elements' start tags. - * See http://www.w3.org/TR/html-markup/syntax.html#syntax-elements - * @type {RegExp} - * @private - */ -soy.$$HTML5_VOID_ELEMENTS_ = new RegExp( - '^<(?:area|base|br|col|command|embed|hr|img|input' + - '|keygen|link|meta|param|source|track|wbr)\\b'); - - -/** - * Removes HTML tags from a string of known safe HTML. - * If opt_tagAllowlist is not specified or is empty, then - * the result can be used as an attribute value. - * - * @param {*} value The HTML to be escaped. May not be a string, but the - * value will be coerced to a string. - * @param {Object=} opt_tagAllowlist Has an own property whose - * name is a lower-case tag name and whose value is {@code 1} for - * each element that is allowed in the output. - * @return {string} A representation of value without disallowed tags, - * HTML comments, or other non-text content. - */ -soy.$$stripHtmlTags = function(value, opt_tagAllowlist) { - if (!opt_tagAllowlist) { - // If we have no allow-list, then use a fast track which elides all tags. - return String(value).replace(soy.esc.$$HTML_TAG_REGEX_, '') - // This is just paranoia since callers should normalize the result - // anyway, but if they didn't, it would be necessary to ensure that - // after the first replace non-tag uses of < do not recombine into - // tags as in "<script>alert(1337)script>". - .replace(soy.esc.$$LT_REGEX_, '<'); - } - - // Escapes '[' so that we can use [123] below to mark places where tags - // have been removed. - var html = String(value).replace(/\[/g, '['); - - // Consider all uses of '<' and replace allowlisted tags with markers like - // [1] which are indices into a list of approved tag names. - // Replace all other uses of < and > with entities. - var tags = []; - html = html.replace( - soy.esc.$$HTML_TAG_REGEX_, - function(tok, tagName) { - if (tagName) { - tagName = tagName.toLowerCase(); - if (opt_tagAllowlist.hasOwnProperty(tagName) && - opt_tagAllowlist[tagName]) { - var start = tok.charAt(1) === '/' ? ''; - return '[' + index + ']'; - } - } - return ''; - }); - - // Escape HTML special characters. Now there are no '<' in html that could - // start a tag. - html = soy.esc.$$normalizeHtmlHelper(html); - - var finalCloseTags = soy.$$balanceTags_(tags); - - // Now html contains no tags or less-than characters that could become - // part of a tag via a replacement operation and tags only contains - // approved tags. - // Reinsert the allow-listed tags. - html = html.replace( - /\[(\d+)\]/g, function(_, index) { return tags[index]; }); - - // Close any still open tags. - // This prevents unclosed formatting elements like

    and from - // breaking the layout of containing HTML. - return html + finalCloseTags; -}; - - -/** - * Throw out any close tags that don't correspond to start tags. - * If {@code
    } is used for formatting, embedded HTML shouldn't be able - * to use a mismatched {@code
    } to break page layout. - * - * @param {Array} tags an array of tags that will be modified in place - * include tags, the empty string, or concatenations of empty tags. - * @return {string} zero or more closed tags that close all elements that are - * opened in tags but not closed. - * @private - */ -soy.$$balanceTags_ = function(tags) { - var open = []; - for (var i = 0, n = tags.length; i < n; ++i) { - var tag = tags[i]; - if (tag.charAt(1) === '/') { - var openTagIndex = open.length - 1; - // NOTE: This is essentially lastIndexOf, but it's not supported in IE. - while (openTagIndex >= 0 && open[openTagIndex] != tag) { - openTagIndex--; - } - if (openTagIndex < 0) { - tags[i] = ''; // Drop close tag. - } else { - tags[i] = open.slice(openTagIndex).reverse().join(''); - open.length = openTagIndex; - } - } else if (!soy.$$HTML5_VOID_ELEMENTS_.test(tag)) { - open.push('Hello World - return soy.esc.$$filterHtmlElementNameHelper(value); -}; - - -/** - * Escapes characters in the value to make it valid content for a JS string - * literal. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} An escaped version of value. - * @deprecated - */ -soy.$$escapeJs = function(value) { - return soy.$$escapeJsString(value); -}; - - -/** - * Escapes characters in the value to make it valid content for a JS string - * literal. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} An escaped version of value. - */ -soy.$$escapeJsString = function(value) { - if (soydata.isContentKind(value, soydata.SanitizedContentKind.JS_STR_CHARS)) { - // TODO: It might still be worthwhile to normalize it to remove - // unescaped quotes, null, etc: replace(/(?:^|[^\])['"]/g, '\\$ - goog.asserts.assert(value.constructor === soydata.SanitizedJsStrChars); - return value.content; - } - return soy.esc.$$escapeJsStringHelper(value); -}; - - -/** - * Encodes a value as a JavaScript literal. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} A JavaScript code representation of the input. - */ -soy.$$escapeJsValue = function(value) { - // We surround values with spaces so that they can't be interpolated into - // identifiers by accident. - // We could use parentheses but those might be interpreted as a function call. - if (value == null) { // Intentionally matches undefined. - // Java returns null from maps where there is no corresponding key while - // JS returns undefined. - // We always output null for compatibility with Java which does not have a - // distinct undefined value. - return ' null '; - } - if (soydata.isContentKind(value, soydata.SanitizedContentKind.JS)) { - goog.asserts.assert(value.constructor === soydata.SanitizedJs); - return value.content; - } - switch (typeof value) { - case 'boolean': case 'number': - return ' ' + value + ' '; - default: - return "'" + soy.esc.$$escapeJsStringHelper(String(value)) + "'"; - } -}; - - -/** - * Escapes characters in the string to make it valid content for a JS regular - * expression literal. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} An escaped version of value. - */ -soy.$$escapeJsRegex = function(value) { - return soy.esc.$$escapeJsRegexHelper(value); -}; - - -/** - * Matches all URI mark characters that conflict with HTML attribute delimiters - * or that cannot appear in a CSS uri. - * From G.2: CSS grammar - *
    - *     url        ([!#$%&*-~]|{nonascii}|{escape})*
    - * 
    - * - * @type {RegExp} - * @private - */ -soy.$$problematicUriMarks_ = /['()]/g; - -/** - * @param {string} ch A single character in {@link soy.$$problematicUriMarks_}. - * @return {string} - * @private - */ -soy.$$pctEncode_ = function(ch) { - return '%' + ch.charCodeAt(0).toString(16); -}; - -/** - * Escapes a string so that it can be safely included in a URI. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} An escaped version of value. - */ -soy.$$escapeUri = function(value) { - if (soydata.isContentKind(value, soydata.SanitizedContentKind.URI)) { - goog.asserts.assert(value.constructor === soydata.SanitizedUri); - return soy.$$normalizeUri(value); - } - // Apostophes and parentheses are not matched by encodeURIComponent. - // They are technically special in URIs, but only appear in the obsolete mark - // production in Appendix D.2 of RFC 3986, so can be encoded without changing - // semantics. - var encoded = soy.esc.$$escapeUriHelper(value); - soy.$$problematicUriMarks_.lastIndex = 0; - if (soy.$$problematicUriMarks_.test(encoded)) { - return encoded.replace(soy.$$problematicUriMarks_, soy.$$pctEncode_); - } - return encoded; -}; - - -/** - * Removes rough edges from a URI by escaping any raw HTML/JS string delimiters. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} An escaped version of value. - */ -soy.$$normalizeUri = function(value) { - return soy.esc.$$normalizeUriHelper(value); -}; - - -/** - * Vets a URI's protocol and removes rough edges from a URI by escaping - * any raw HTML/JS string delimiters. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} An escaped version of value. - */ -soy.$$filterNormalizeUri = function(value) { - if (soydata.isContentKind(value, soydata.SanitizedContentKind.URI)) { - goog.asserts.assert(value.constructor === soydata.SanitizedUri); - return soy.$$normalizeUri(value); - } - return soy.esc.$$filterNormalizeUriHelper(value); -}; - - -/** - * Allows only data-protocol image URI's. - * - * @param {*} value The value to process. May not be a string, but the value - * will be coerced to a string. - * @return {!soydata.SanitizedUri} An escaped version of value. - */ -soy.$$filterImageDataUri = function(value) { - // NOTE: Even if it's a SanitizedUri, we will still filter it. - return soydata.VERY_UNSAFE.ordainSanitizedUri( - soy.esc.$$filterImageDataUriHelper(value)); -}; - - -/** - * Escapes a string so it can safely be included inside a quoted CSS string. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} An escaped version of value. - */ -soy.$$escapeCssString = function(value) { - return soy.esc.$$escapeCssStringHelper(value); -}; - - -/** - * Encodes a value as a CSS identifier part, keyword, or quantity. - * - * @param {*} value The value to escape. May not be a string, but the value - * will be coerced to a string. - * @return {string} A safe CSS identifier part, keyword, or quanitity. - */ -soy.$$filterCssValue = function(value) { - if (soydata.isContentKind(value, soydata.SanitizedContentKind.CSS)) { - goog.asserts.assert(value.constructor === soydata.SanitizedCss); - return value.content; - } - // Uses == to intentionally match null and undefined for Java compatibility. - if (value == null) { - return ''; - } - return soy.esc.$$filterCssValueHelper(value); -}; - - -/** - * Sanity-checks noAutoescape input for explicitly tainted content. - * - * SanitizedContentKind.TEXT is used to explicitly mark input that was never - * meant to be used unescaped. - * - * @param {*} value The value to filter. - * @return {*} The value, that we dearly hope will not cause an attack. - */ -soy.$$filterNoAutoescape = function(value) { - if (soydata.isContentKind(value, soydata.SanitizedContentKind.TEXT)) { - // Fail in development mode. - goog.asserts.fail( - 'Tainted SanitizedContentKind.TEXT for |noAutoescape: `%s`', - [value.content]); - // Return innocuous data in production. - return 'zSoyz'; - } - - return value; -}; - - -// ----------------------------------------------------------------------------- -// Basic directives/functions. - - -/** - * Converts \r\n, \r, and \n to
    s - * @param {*} value The string in which to convert newlines. - * @return {string|!soydata.SanitizedHtml} A copy of {@code value} with - * converted newlines. If {@code value} is SanitizedHtml, the return value - * is also SanitizedHtml, of the same known directionality. - */ -soy.$$changeNewlineToBr = function(value) { - var result = goog.string.newLineToBr(String(value), false); - if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) { - return soydata.VERY_UNSAFE.ordainSanitizedHtml( - result, soydata.getContentDir(value)); - } - return result; -}; - - -/** - * Inserts word breaks ('wbr' tags) into a HTML string at a given interval. The - * counter is reset if a space is encountered. Word breaks aren't inserted into - * HTML tags or entities. Entities count towards the character count; HTML tags - * do not. - * - * @param {*} value The HTML string to insert word breaks into. Can be other - * types, but the value will be coerced to a string. - * @param {number} maxCharsBetweenWordBreaks Maximum number of non-space - * characters to allow before adding a word break. - * @return {string|!soydata.SanitizedHtml} The string including word - * breaks. If {@code value} is SanitizedHtml, the return value - * is also SanitizedHtml, of the same known directionality. - */ -soy.$$insertWordBreaks = function(value, maxCharsBetweenWordBreaks) { - var result = goog.format.insertWordBreaks( - String(value), maxCharsBetweenWordBreaks); - if (soydata.isContentKind(value, soydata.SanitizedContentKind.HTML)) { - return soydata.VERY_UNSAFE.ordainSanitizedHtml( - result, soydata.getContentDir(value)); - } - return result; -}; - - -/** - * Truncates a string to a given max length (if it's currently longer), - * optionally adding ellipsis at the end. - * - * @param {*} str The string to truncate. Can be other types, but the value will - * be coerced to a string. - * @param {number} maxLen The maximum length of the string after truncation - * (including ellipsis, if applicable). - * @param {boolean} doAddEllipsis Whether to add ellipsis if the string needs - * truncation. - * @return {string} The string after truncation. - */ -soy.$$truncate = function(str, maxLen, doAddEllipsis) { - - str = String(str); - if (str.length <= maxLen) { - return str; // no need to truncate - } - - // If doAddEllipsis, either reduce maxLen to compensate, or else if maxLen is - // too small, just turn off doAddEllipsis. - if (doAddEllipsis) { - if (maxLen > 3) { - maxLen -= 3; - } else { - doAddEllipsis = false; - } - } - - // Make sure truncating at maxLen doesn't cut up a unicode surrogate pair. - if (soy.$$isHighSurrogate_(str.charAt(maxLen - 1)) && - soy.$$isLowSurrogate_(str.charAt(maxLen))) { - maxLen -= 1; - } - - // Truncate. - str = str.substring(0, maxLen); - - // Add ellipsis. - if (doAddEllipsis) { - str += '...'; - } - - return str; -}; - -/** - * Private helper for $$truncate() to check whether a char is a high surrogate. - * @param {string} ch The char to check. - * @return {boolean} Whether the given char is a unicode high surrogate. - * @private - */ -soy.$$isHighSurrogate_ = function(ch) { - return 0xD800 <= ch && ch <= 0xDBFF; -}; - -/** - * Private helper for $$truncate() to check whether a char is a low surrogate. - * @param {string} ch The char to check. - * @return {boolean} Whether the given char is a unicode low surrogate. - * @private - */ -soy.$$isLowSurrogate_ = function(ch) { - return 0xDC00 <= ch && ch <= 0xDFFF; -}; - - -// ----------------------------------------------------------------------------- -// Bidi directives/functions. - - -/** - * Cache of bidi formatter by context directionality, so we don't keep on - * creating new objects. - * @type {!Object} - * @private - */ -soy.$$bidiFormatterCache_ = {}; - - -/** - * Returns cached bidi formatter for bidiGlobalDir, or creates a new one. - * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1 - * if rtl, 0 if unknown. - * @return {goog.i18n.BidiFormatter} A formatter for bidiGlobalDir. - * @private - */ -soy.$$getBidiFormatterInstance_ = function(bidiGlobalDir) { - return soy.$$bidiFormatterCache_[bidiGlobalDir] || - (soy.$$bidiFormatterCache_[bidiGlobalDir] = - new goog.i18n.BidiFormatter(bidiGlobalDir)); -}; - - -/** - * Estimate the overall directionality of text. If opt_isHtml, makes sure to - * ignore the LTR nature of the mark-up and escapes in text, making the logic - * suitable for HTML and HTML-escaped text. - * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of - * estimating the directionality. - * - * @param {*} text The content whose directionality is to be estimated. - * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped. - * Default: false. - * @return {number} 1 if text is LTR, -1 if it is RTL, and 0 if it is neutral. - */ -soy.$$bidiTextDir = function(text, opt_isHtml) { - var contentDir = soydata.getContentDir(text); - if (contentDir != null) { - return contentDir; - } - var isHtml = opt_isHtml || - soydata.isContentKind(text, soydata.SanitizedContentKind.HTML); - return goog.i18n.bidi.estimateDirection(text + '', isHtml); -}; - - -/** - * Returns 'dir="ltr"' or 'dir="rtl"', depending on text's estimated - * directionality, if it is not the same as bidiGlobalDir. - * Otherwise, returns the empty string. - * If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes - * in text, making the logic suitable for HTML and HTML-escaped text. - * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of - * estimating the directionality. - * - * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1 - * if rtl, 0 if unknown. - * @param {*} text The content whose directionality is to be estimated. - * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped. - * Default: false. - * @return {soydata.SanitizedHtmlAttribute} 'dir="rtl"' for RTL text in non-RTL - * context; 'dir="ltr"' for LTR text in non-LTR context; - * else, the empty string. - */ -soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) { - var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir); - var contentDir = soydata.getContentDir(text); - if (contentDir == null) { - var isHtml = opt_isHtml || - soydata.isContentKind(text, soydata.SanitizedContentKind.HTML); - contentDir = goog.i18n.bidi.estimateDirection(text + '', isHtml); - } - return soydata.VERY_UNSAFE.ordainSanitizedHtmlAttribute( - formatter.knownDirAttr(contentDir)); -}; - - -/** - * Returns a Unicode BiDi mark matching bidiGlobalDir (LRM or RLM) if the - * directionality or the exit directionality of text are opposite to - * bidiGlobalDir. Otherwise returns the empty string. - * If opt_isHtml, makes sure to ignore the LTR nature of the mark-up and escapes - * in text, making the logic suitable for HTML and HTML-escaped text. - * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of - * estimating the directionality. - * - * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1 - * if rtl, 0 if unknown. - * @param {*} text The content whose directionality is to be estimated. - * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped. - * Default: false. - * @return {string} A Unicode bidi mark matching bidiGlobalDir, or the empty - * string when text's overall and exit directionalities both match - * bidiGlobalDir, or bidiGlobalDir is 0 (unknown). - */ -soy.$$bidiMarkAfter = function(bidiGlobalDir, text, opt_isHtml) { - var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir); - var isHtml = opt_isHtml || - soydata.isContentKind(text, soydata.SanitizedContentKind.HTML); - return formatter.markAfterKnownDir(soydata.getContentDir(text), text + '', - isHtml); -}; - - -/** - * Returns text wrapped in a according to its - * directionality - but only if that is neither neutral nor the same as the - * global context. Otherwise, returns text unchanged. - * Always treats text as HTML/HTML-escaped, i.e. ignores mark-up and escapes - * when estimating text's directionality. - * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of - * estimating the directionality. - * - * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1 - * if rtl, 0 if unknown. - * @param {*} text The string to be wrapped. Can be other types, but the value - * will be coerced to a string. - * @return {!goog.soy.data.SanitizedContent|string} The wrapped text. - */ -soy.$$bidiSpanWrap = function(bidiGlobalDir, text) { - var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir); - - // We always treat the value as HTML, because span-wrapping is only useful - // when its output will be treated as HTML (without escaping), and because - // |bidiSpanWrap is not itself specified to do HTML escaping in Soy. (Both - // explicit and automatic HTML escaping, if any, is done before calling - // |bidiSpanWrap because the BidiSpanWrapDirective Java class implements - // SanitizedContentOperator, but this does not mean that the input has to be - // HTML SanitizedContent. In legacy usage, a string that is not - // SanitizedContent is often printed in an autoescape="false" template or by - // a print with a |noAutoescape, in which case our input is just SoyData.) If - // the output will be treated as HTML, the input had better be safe - // HTML/HTML-escaped (even if it isn't HTML SanitizedData), or we have an XSS - // opportunity and a much bigger problem than bidi garbling. - var wrappedText = formatter.spanWrapWithKnownDir( - soydata.getContentDir(text), text + '', true /* opt_isHtml */); - - // Like other directives whose Java class implements SanitizedContentOperator, - // |bidiSpanWrap is called after the escaping (if any) has already been done, - // and thus there is no need for it to produce actual SanitizedContent. - return wrappedText; -}; - - -/** - * Returns text wrapped in Unicode BiDi formatting characters according to its - * directionality, i.e. either LRE or RLE at the beginning and PDF at the end - - * but only if text's directionality is neither neutral nor the same as the - * global context. Otherwise, returns text unchanged. - * Only treats soydata.SanitizedHtml as HTML/HTML-escaped, i.e. ignores mark-up - * and escapes when estimating text's directionality. - * If text has a goog.i18n.bidi.Dir-valued contentDir, this is used instead of - * estimating the directionality. - * - * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1 - * if rtl, 0 if unknown. - * @param {*} text The string to be wrapped. Can be other types, but the value - * will be coerced to a string. - * @return {!goog.soy.data.SanitizedContent|string} The wrapped string. - */ -soy.$$bidiUnicodeWrap = function(bidiGlobalDir, text) { - var formatter = soy.$$getBidiFormatterInstance_(bidiGlobalDir); - - // We treat the value as HTML if and only if it says it's HTML, even though in - // legacy usage, we sometimes have an HTML string (not SanitizedContent) that - // is passed to an autoescape="false" template or a {print $foo|noAutoescape}, - // with the output going into an HTML context without escaping. We simply have - // no way of knowing if this is what is happening when we get - // non-SanitizedContent input, and most of the time it isn't. - var isHtml = soydata.isContentKind(text, soydata.SanitizedContentKind.HTML); - var wrappedText = formatter.unicodeWrapWithKnownDir( - soydata.getContentDir(text), text + '', isHtml); - - // Bidi-wrapping a value converts it to the context directionality. Since it - // does not cost us anything, we will indicate this known direction in the - // output SanitizedContent, even though the intended consumer of that - // information - a bidi wrapping directive - has already been run. - var wrappedTextDir = formatter.getContextDir(); - - // Unicode-wrapping UnsanitizedText gives UnsanitizedText. - // Unicode-wrapping safe HTML or JS string data gives valid, safe HTML or JS - // string data. - // ATTENTION: Do these need to be ...ForInternalBlocks()? - if (soydata.isContentKind(text, soydata.SanitizedContentKind.TEXT)) { - return new soydata.UnsanitizedText(wrappedText, wrappedTextDir); - } - if (isHtml) { - return soydata.VERY_UNSAFE.ordainSanitizedHtml(wrappedText, wrappedTextDir); - } - if (soydata.isContentKind(text, soydata.SanitizedContentKind.JS_STR_CHARS)) { - return soydata.VERY_UNSAFE.ordainSanitizedJsStrChars( - wrappedText, wrappedTextDir); - } - - // Unicode-wrapping does not conform to the syntax of the other types of - // content. For lack of anything better to do, we we do not declare a content - // kind at all by falling through to the non-SanitizedContent case below. - // TODO(user): Consider throwing a runtime error on receipt of - // SanitizedContent other than TEXT, HTML, or JS_STR_CHARS. - - // The input was not SanitizedContent, so our output isn't SanitizedContent - // either. - return wrappedText; -}; - - -// ----------------------------------------------------------------------------- -// Generated code. - - - - - - - - -// START GENERATED CODE FOR ESCAPERS. - -/** - * @type {function (*) : string} - */ -soy.esc.$$escapeUriHelper = function(v) { - return encodeURIComponent(String(v)); -}; - -/** - * Maps characters to the escaped versions for the named escape directives. - * @type {Object} - * @private - */ -soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_ = { - '\x00': '\x26#0;', - '\x22': '\x26quot;', - '\x26': '\x26amp;', - '\x27': '\x26#39;', - '\x3c': '\x26lt;', - '\x3e': '\x26gt;', - '\x09': '\x26#9;', - '\x0a': '\x26#10;', - '\x0b': '\x26#11;', - '\x0c': '\x26#12;', - '\x0d': '\x26#13;', - ' ': '\x26#32;', - '-': '\x26#45;', - '\/': '\x26#47;', - '\x3d': '\x26#61;', - '`': '\x26#96;', - '\x85': '\x26#133;', - '\xa0': '\x26#160;', - '\u2028': '\x26#8232;', - '\u2029': '\x26#8233;' -}; - -/** - * A function that can be used with String.replace. - * @param {string} ch A single character matched by a compatible matcher. - * @return {string} A token in the output language. - * @private - */ -soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_ = function(ch) { - return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_[ch]; -}; - -/** - * Maps characters to the escaped versions for the named escape directives. - * @type {Object} - * @private - */ -soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = { - '\x00': '\\x00', - '\x08': '\\x08', - '\x09': '\\t', - '\x0a': '\\n', - '\x0b': '\\x0b', - '\x0c': '\\f', - '\x0d': '\\r', - '\x22': '\\x22', - '\x26': '\\x26', - '\x27': '\\x27', - '\/': '\\\/', - '\x3c': '\\x3c', - '\x3d': '\\x3d', - '\x3e': '\\x3e', - '\\': '\\\\', - '\x85': '\\x85', - '\u2028': '\\u2028', - '\u2029': '\\u2029', - '$': '\\x24', - '(': '\\x28', - ')': '\\x29', - '*': '\\x2a', - '+': '\\x2b', - ',': '\\x2c', - '-': '\\x2d', - '.': '\\x2e', - ':': '\\x3a', - '?': '\\x3f', - '[': '\\x5b', - ']': '\\x5d', - '^': '\\x5e', - '{': '\\x7b', - '|': '\\x7c', - '}': '\\x7d' -}; - -/** - * A function that can be used with String.replace. - * @param {string} ch A single character matched by a compatible matcher. - * @return {string} A token in the output language. - * @private - */ -soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = function(ch) { - return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_[ch]; -}; - -/** - * Maps characters to the escaped versions for the named escape directives. - * @type {Object} - * @private - */ -soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_ = { - '\x00': '\\0 ', - '\x08': '\\8 ', - '\x09': '\\9 ', - '\x0a': '\\a ', - '\x0b': '\\b ', - '\x0c': '\\c ', - '\x0d': '\\d ', - '\x22': '\\22 ', - '\x26': '\\26 ', - '\x27': '\\27 ', - '(': '\\28 ', - ')': '\\29 ', - '*': '\\2a ', - '\/': '\\2f ', - ':': '\\3a ', - ';': '\\3b ', - '\x3c': '\\3c ', - '\x3d': '\\3d ', - '\x3e': '\\3e ', - '@': '\\40 ', - '\\': '\\5c ', - '{': '\\7b ', - '}': '\\7d ', - '\x85': '\\85 ', - '\xa0': '\\a0 ', - '\u2028': '\\2028 ', - '\u2029': '\\2029 ' -}; - -/** - * A function that can be used with String.replace. - * @param {string} ch A single character matched by a compatible matcher. - * @return {string} A token in the output language. - * @private - */ -soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_ = function(ch) { - return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_[ch]; -}; - -/** - * Maps characters to the escaped versions for the named escape directives. - * @type {Object} - * @private - */ -soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = { - '\x00': '%00', - '\x01': '%01', - '\x02': '%02', - '\x03': '%03', - '\x04': '%04', - '\x05': '%05', - '\x06': '%06', - '\x07': '%07', - '\x08': '%08', - '\x09': '%09', - '\x0a': '%0A', - '\x0b': '%0B', - '\x0c': '%0C', - '\x0d': '%0D', - '\x0e': '%0E', - '\x0f': '%0F', - '\x10': '%10', - '\x11': '%11', - '\x12': '%12', - '\x13': '%13', - '\x14': '%14', - '\x15': '%15', - '\x16': '%16', - '\x17': '%17', - '\x18': '%18', - '\x19': '%19', - '\x1a': '%1A', - '\x1b': '%1B', - '\x1c': '%1C', - '\x1d': '%1D', - '\x1e': '%1E', - '\x1f': '%1F', - ' ': '%20', - '\x22': '%22', - '\x27': '%27', - '(': '%28', - ')': '%29', - '\x3c': '%3C', - '\x3e': '%3E', - '\\': '%5C', - '{': '%7B', - '}': '%7D', - '\x7f': '%7F', - '\x85': '%C2%85', - '\xa0': '%C2%A0', - '\u2028': '%E2%80%A8', - '\u2029': '%E2%80%A9', - '\uff01': '%EF%BC%81', - '\uff03': '%EF%BC%83', - '\uff04': '%EF%BC%84', - '\uff06': '%EF%BC%86', - '\uff07': '%EF%BC%87', - '\uff08': '%EF%BC%88', - '\uff09': '%EF%BC%89', - '\uff0a': '%EF%BC%8A', - '\uff0b': '%EF%BC%8B', - '\uff0c': '%EF%BC%8C', - '\uff0f': '%EF%BC%8F', - '\uff1a': '%EF%BC%9A', - '\uff1b': '%EF%BC%9B', - '\uff1d': '%EF%BC%9D', - '\uff1f': '%EF%BC%9F', - '\uff20': '%EF%BC%A0', - '\uff3b': '%EF%BC%BB', - '\uff3d': '%EF%BC%BD' -}; - -/** - * A function that can be used with String.replace. - * @param {string} ch A single character matched by a compatible matcher. - * @return {string} A token in the output language. - * @private - */ -soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = function(ch) { - return soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_[ch]; -}; - -/** - * Matches characters that need to be escaped for the named directives. - * @type RegExp - * @private - */ -soy.esc.$$MATCHER_FOR_ESCAPE_HTML_ = /[\x00\x22\x26\x27\x3c\x3e]/g; - -/** - * Matches characters that need to be escaped for the named directives. - * @type RegExp - * @private - */ -soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_ = /[\x00\x22\x27\x3c\x3e]/g; - -/** - * Matches characters that need to be escaped for the named directives. - * @type RegExp - * @private - */ -soy.esc.$$MATCHER_FOR_ESCAPE_HTML_NOSPACE_ = /[\x00\x09-\x0d \x22\x26\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g; - -/** - * Matches characters that need to be escaped for the named directives. - * @type RegExp - * @private - */ -soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_NOSPACE_ = /[\x00\x09-\x0d \x22\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g; - -/** - * Matches characters that need to be escaped for the named directives. - * @type RegExp - * @private - */ -soy.esc.$$MATCHER_FOR_ESCAPE_JS_STRING_ = /[\x00\x08-\x0d\x22\x26\x27\/\x3c-\x3e\\\x85\u2028\u2029]/g; - -/** - * Matches characters that need to be escaped for the named directives. - * @type RegExp - * @private - */ -soy.esc.$$MATCHER_FOR_ESCAPE_JS_REGEX_ = /[\x00\x08-\x0d\x22\x24\x26-\/\x3a\x3c-\x3f\x5b-\x5e\x7b-\x7d\x85\u2028\u2029]/g; - -/** - * Matches characters that need to be escaped for the named directives. - * @type RegExp - * @private - */ -soy.esc.$$MATCHER_FOR_ESCAPE_CSS_STRING_ = /[\x00\x08-\x0d\x22\x26-\x2a\/\x3a-\x3e@\\\x7b\x7d\x85\xa0\u2028\u2029]/g; - -/** - * Matches characters that need to be escaped for the named directives. - * @type RegExp - * @private - */ -soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = /[\x00- \x22\x27-\x29\x3c\x3e\\\x7b\x7d\x7f\x85\xa0\u2028\u2029\uff01\uff03\uff04\uff06-\uff0c\uff0f\uff1a\uff1b\uff1d\uff1f\uff20\uff3b\uff3d]/g; - -/** - * A pattern that vets values produced by the named directives. - * @type RegExp - * @private - */ -soy.esc.$$FILTER_FOR_FILTER_CSS_VALUE_ = /^(?!-*(?:expression|(?:moz-)?binding))(?:[.#]?-?(?:[_a-z0-9-]+)(?:-[_a-z0-9-]+)*-?|-?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[a-z]{1,2}|%)?|!important|)$/i; - -/** - * A pattern that vets values produced by the named directives. - * @type RegExp - * @private - */ -soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_ = /^(?:(?:https?|mailto):|[^&:\/?#]*(?:[\/?#]|$))/i; - -/** - * A pattern that vets values produced by the named directives. - * @type RegExp - * @private - */ -soy.esc.$$FILTER_FOR_FILTER_IMAGE_DATA_URI_ = /^data:image\/(?:bmp|gif|jpe?g|png|tiff|webp);base64,[a-z0-9+\/]+=*$/i; - -/** - * A pattern that vets values produced by the named directives. - * @type RegExp - * @private - */ -soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_ = /^(?!style|on|action|archive|background|cite|classid|codebase|data|dsync|href|longdesc|src|usemap)(?:[a-z0-9_$:-]*)$/i; - -/** - * A pattern that vets values produced by the named directives. - * @type RegExp - * @private - */ -soy.esc.$$FILTER_FOR_FILTER_HTML_ELEMENT_NAME_ = /^(?!script|style|title|textarea|xmp|no)[a-z0-9_$:-]*$/i; - -/** - * A helper for the Soy directive |escapeHtml - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$escapeHtmlHelper = function(value) { - var str = String(value); - return str.replace( - soy.esc.$$MATCHER_FOR_ESCAPE_HTML_, - soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_); -}; - -/** - * A helper for the Soy directive |normalizeHtml - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$normalizeHtmlHelper = function(value) { - var str = String(value); - return str.replace( - soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_, - soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_); -}; - -/** - * A helper for the Soy directive |escapeHtmlNospace - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$escapeHtmlNospaceHelper = function(value) { - var str = String(value); - return str.replace( - soy.esc.$$MATCHER_FOR_ESCAPE_HTML_NOSPACE_, - soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_); -}; - -/** - * A helper for the Soy directive |normalizeHtmlNospace - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$normalizeHtmlNospaceHelper = function(value) { - var str = String(value); - return str.replace( - soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_NOSPACE_, - soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_); -}; - -/** - * A helper for the Soy directive |escapeJsString - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$escapeJsStringHelper = function(value) { - var str = String(value); - return str.replace( - soy.esc.$$MATCHER_FOR_ESCAPE_JS_STRING_, - soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_); -}; - -/** - * A helper for the Soy directive |escapeJsRegex - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$escapeJsRegexHelper = function(value) { - var str = String(value); - return str.replace( - soy.esc.$$MATCHER_FOR_ESCAPE_JS_REGEX_, - soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_); -}; - -/** - * A helper for the Soy directive |escapeCssString - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$escapeCssStringHelper = function(value) { - var str = String(value); - return str.replace( - soy.esc.$$MATCHER_FOR_ESCAPE_CSS_STRING_, - soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_); -}; - -/** - * A helper for the Soy directive |filterCssValue - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$filterCssValueHelper = function(value) { - var str = String(value); - if (!soy.esc.$$FILTER_FOR_FILTER_CSS_VALUE_.test(str)) { - return 'zSoyz'; - } - return str; -}; - -/** - * A helper for the Soy directive |normalizeUri - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$normalizeUriHelper = function(value) { - var str = String(value); - return str.replace( - soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_, - soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_); -}; - -/** - * A helper for the Soy directive |filterNormalizeUri - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$filterNormalizeUriHelper = function(value) { - var str = String(value); - if (!soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_.test(str)) { - return '#zSoyz'; - } - return str.replace( - soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_, - soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_); -}; - -/** - * A helper for the Soy directive |filterImageDataUri - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$filterImageDataUriHelper = function(value) { - var str = String(value); - if (!soy.esc.$$FILTER_FOR_FILTER_IMAGE_DATA_URI_.test(str)) { - return 'data:image/gif;base64,zSoyz'; - } - return str; -}; - -/** - * A helper for the Soy directive |filterHtmlAttributes - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$filterHtmlAttributesHelper = function(value) { - var str = String(value); - if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTES_.test(str)) { - return 'zSoyz'; - } - return str; -}; - -/** - * A helper for the Soy directive |filterHtmlElementName - * @param {*} value Can be of any type but will be coerced to a string. - * @return {string} The escaped text. - */ -soy.esc.$$filterHtmlElementNameHelper = function(value) { - var str = String(value); - if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ELEMENT_NAME_.test(str)) { - return 'zSoyz'; - } - return str; -}; - -/** - * Matches all tags, HTML comments, and DOCTYPEs in tag soup HTML. - * By removing these, and replacing any '<' or '>' characters with - * entities we guarantee that the result can be embedded into a - * an attribute without introducing a tag boundary. - * - * @type {RegExp} - * @private - */ -soy.esc.$$HTML_TAG_REGEX_ = /<(?:!|\/?([a-zA-Z][a-zA-Z0-9:\-]*))(?:[^>'"]|"[^"]*"|'[^']*')*>/g; - -/** - * Matches all occurrences of '<'. - * - * @type {RegExp} - * @private - */ -soy.esc.$$LT_REGEX_ = /} - * @private - */ -soy.esc.$$SAFE_TAG_ALLOWLIST_ = {'b': 1, 'br': 1, 'em': 1, 'i': 1, 's': 1, 'sub': 1, 'sup': 1, 'u': 1}; - -// END GENERATED CODE diff --git a/demos/plane/style.css b/demos/plane/style.css deleted file mode 100644 index 16e2c6f41..000000000 --- a/demos/plane/style.css +++ /dev/null @@ -1,97 +0,0 @@ -body { - background-color: #fff; - font-family: sans-serif; - margin-top: 0; -} -h1 { - font-weight: normal; - font-size: 140%; -} -.farSide { - text-align: right; -} -html[dir="RTL"] .farSide { - text-align: left; -} -.tab { - padding: 6px 12px; - text-decoration: none; - color: #000; -} -#selected { - font-weight: bold; - background-color: #ddd; - border-radius: 20px; -} - -/* Pulse the language menu once to draw attention to it. */ -#languageBorder { - border-radius: 4px; - animation: pulse 2s ease-in-out forwards; - animation-delay: 2s; -} -@keyframes pulse { - 0% { background-color: #fff } - 50% { background-color: #f00 } - 100% { background-color: #fff } -} - -#blockly { - height: 300px; - width: 100%; - border-style: solid; - border-color: #ddd; - border-width: 0 1px 1px 0; -} - -/* SVG Plane. */ -#plane { - overflow: hidden; -} -#fuselage { - fill: #fff; - stroke: #000; -} -#wing, #tail { - fill: #ddd; - stroke: #444; -} -.crew { - fill: #f44; - stroke: #000; -} -.seat1st { - fill: #88f; - stroke: #000; -} -.seat2nd { - fill: #8b8; - stroke: #000; -} -#seatYes, #seatNo { - font-size: 40pt; -} -text { - font-family: sans-serif; - font-size: 20pt; - fill: #444; -} -html[dir="RTL"] #plane text { - text-anchor: end; -} - -/* Slider. */ -.sliderTrack { - stroke: #aaa; - stroke-width: 6px; - stroke-linecap: round; -} -.sliderKnob { - fill: #ddd; - stroke: #bbc; - stroke-width: 1px; - stroke-linejoin: round; -} -.sliderKnob:hover { - fill: #eee; -} diff --git a/demos/plane/template.soy b/demos/plane/template.soy deleted file mode 100644 index cbccc9ae4..000000000 --- a/demos/plane/template.soy +++ /dev/null @@ -1,225 +0,0 @@ -{namespace planepage} - -/** - * This is a Closure Template. - * - * See the README.txt for details. - */ - -/** - * Translated messages for use in JavaScript. - */ -{template .messages} -
    - {msg meaning="Plane.rows" desc="page text - Total number of rows of seats on an airplane.\n\nParameters:\n* %1 - number of rows of seats on an airplane. It is always an integer greater than or equal to zero."}Rows: %1{/msg} - {msg meaning="Plane.getRows" desc="block text - The number of rows on the airplane, to be used in a mathematical equation, such as: 'seats = 4 x '''rows (5)''''.\n\nParameters:\n* %1 - number of rows of seats on an airplane. It is always an integer greater than or equal to zero."}rows (%1){/msg} - {msg meaning="Plane.rows1" desc="page text - The number of rows of first-class seats on the airplane. You can see the block at [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=3].\n\nParameters:\n* %1 - number of rows of first-class seats on an airplane. It is always an integer greater than or equal to zero."}1st class rows: %1{/msg} - {msg meaning="Plane.getRows1" desc="block text - The number of rows of first-class seats on the, to be used in a mathematical equation. See [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=3].\n\nParameters:\n* %1 - number of rows of first-class seats on an airplane. It is always an integer greater than or equal to zero."}1st class rows (%1){/msg} - {msg meaning="Plane.rows2" desc="page text - The number of rows of second-class seats on the airplane. %1 is an integer greater or equal to zero. See [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=3].\n\nParameters:\n* %1 - number of rows of second-class seats on an airplane. It is always an integer greater than or equal to zero."}2nd class rows: %1{/msg} - {msg meaning="Plane.getRows2" desc="block text - The number of rows of second-class (also called 'economy class') seats on the airplane, to be used in a mathematical expression.\n\nParameters:\n* %1 - number of rows of second-class seats on an airplane. It is always an integer greater than or equal to zero."}2nd class rows (%1){/msg} - {msg meaning="Plane.seats" desc="page text - The total number of seats on the airplane.\n\nParameters:\n* %1 - number of seats on an airplane. It is always either the next message or an integer greater than or equal to zero."}Seats: %1{/msg} - {msg meaning="Plane.placeholder" desc="page text - A word or symbol indicating that this numeric value has not yet been determined."}?{/msg} - {msg meaning="Plane.setSeats" desc="block text - The first half of a mathematical equation determining the number of seats in an airplane, such as: ''''seats =''' 4 x rows'."}seats ={/msg} -
    -{/template} - -/** - * Web page structure. - */ -{template .start} - {call .messages /} - - - - - -
    -

    Blockly‏ >{sp} - Demos‏ >{sp} - - {msg meaning="Plane.plane" desc="title - Specifies that this is Blockly's '''Plane''' (airplane) tutorial. The word 'plane' was chosen over 'airplane' in English because it is shorter and less formal."} - Plane Seat Calculator - {/msg} - - {sp} {sp} - {for $i in range(1, $ij.maxLevel + 1)} - {sp} - {if $i == $ij.level} - {$i} - {else} - {if $i < $ij.level} - - {else} - {$i} - {/if} - {/if} - {/for} -

    -
    - - - -
    - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {if $ij.level > 1} - - - {/if} - - -

    - {switch $ij.level} - {case 1} - {msg meaning="Plane.description1" desc="instructions - Note that in [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=1 this level], there is only one type of seat on the plane."}An airplane has a number of rows of passenger seats. Each row contains four seats.{/msg} - {case 2} - {msg meaning="Plane.description2" desc="instructions - Note that in [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=2 this level], there are two types of seats on this plane."}An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats.{/msg} - {case 3} - {msg meaning="Plane.description3" desc="instructions - Note that in [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=3 this level], there are three types of seats on this plane. Be sure to use the same terms for '1st class' and '2nd class' as you did for the earlier messages."}An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats.{/msg} - {/switch} -

    -

    - {msg meaning="Plane.instructions" desc="page text - This text appears below the airplane graphic and above the space for the user to create the formula. The number of rows an the graphic may be changed by the user with a slider. See [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=1] for a picture."}Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above).{/msg} -

    - - - - - - - {call .toolbox /} -
    -{/template} - -/** - * Toolboxes for each level. - */ -{template .toolbox} - -{/template} diff --git a/demos/plane/xlf/extracted_msgs.xlf b/demos/plane/xlf/extracted_msgs.xlf deleted file mode 100644 index 6a4fd44ff..000000000 --- a/demos/plane/xlf/extracted_msgs.xlf +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - Rows: %1 - page text - Total number of rows of seats on an airplane.\n\nParameters:\n* %1 - number of rows of seats on an airplane. It is always an integer greater than or equal to zero. - Plane.rows - - - seats = - block text - The first half of a mathematical equation determining the number of seats in an airplane, such as: ''''seats =''' 4 x rows'. - Plane.setSeats - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - instructions - Note that in [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=3 this level], there are three types of seats on this plane. Be sure to use the same terms for '1st class' and '2nd class' as you did for the earlier messages. - Plane.description3 - - - ? - page text - A word or symbol indicating that this numeric value has not yet been determined. - Plane.placeholder - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - page text - This text appears below the airplane graphic and above the space for the user to create the formula. The number of rows an the graphic may be changed by the user with a slider. See [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=1] for a picture. - Plane.instructions - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - instructions - Note that in [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=2 this level], there are two types of seats on this plane. - Plane.description2 - - - 1st class rows (%1) - block text - The number of rows of first-class seats on the, to be used in a mathematical equation. See [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=3].\n\nParameters:\n* %1 - number of rows of first-class seats on an airplane. It is always an integer greater than or equal to zero. - Plane.getRows1 - - - 2nd class rows: %1 - page text - The number of rows of second-class seats on the airplane. %1 is an integer greater or equal to zero. See [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=3].\n\nParameters:\n* %1 - number of rows of second-class seats on an airplane. It is always an integer greater than or equal to zero. - Plane.rows2 - - - Seats: %1 - page text - The total number of seats on the airplane.\n\nParameters:\n* %1 - number of seats on an airplane. It is always either the next message or an integer greater than or equal to zero. - Plane.seats - - - Plane Seat Calculator - title - Specifies that this is Blockly's '''Plane''' (airplane) tutorial. The word 'plane' was chosen over 'airplane' in English because it is shorter and less formal. - Plane.plane - - - rows (%1) - block text - The number of rows on the airplane, to be used in a mathematical equation, such as: 'seats = 4 x '''rows (5)''''.\n\nParameters:\n* %1 - number of rows of seats on an airplane. It is always an integer greater than or equal to zero. - Plane.getRows - - - 1st class rows: %1 - page text - The number of rows of first-class seats on the airplane. You can see the block at [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=3].\n\nParameters:\n* %1 - number of rows of first-class seats on an airplane. It is always an integer greater than or equal to zero. - Plane.rows1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - instructions - Note that in [http://blockly-share.appspot.com/static/apps/plane/plane.html?lang=en&level=1 this level], there is only one type of seat on the plane. - Plane.description1 - - - 2nd class rows (%1) - block text - The number of rows of second-class (also called 'economy class') seats on the airplane, to be used in a mathematical expression.\n\nParameters:\n* %1 - number of rows of second-class seats on an airplane. It is always an integer greater than or equal to zero. - Plane.getRows2 - - - - diff --git a/demos/plane/xlf/translated_msgs_ar.xlf b/demos/plane/xlf/translated_msgs_ar.xlf deleted file mode 100644 index c9b8e1689..000000000 --- a/demos/plane/xlf/translated_msgs_ar.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - الصفوف: %1 - - - seats = - المقاعد = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - طائرة بمقعدين في مقطورة الطيّار (للطيار ومساعده) وعدد من المقاعد في صفوف الدرجة الأولى والثانية. كل صف من صفوف الدرجة الأولى يحتوي على أربعة مقاعد. ويحتوي كل صف في الدرجة الثانية على خمسة مقاعد. - - - ? - ؟ - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - لبناء صيغة (أدناه) تقوم بحساب إجمالي عدد المقاعد في الطائرة عند تغيير الصفوف (أعلاه). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - طائرة بمقعدين في مقطورة الطيّار (للطيار ومساعده) وعدد من الصفوف يحتوي كل صف على أربعة مقاعد. - - - 1st class rows (%1) - صفوف الطبقة الأولى (%1) - - - 2nd class rows: %1 - صفوف الفئة الثانية: %1 - - - Seats: %1 - المقاعد: %1 - - - Plane Seat Calculator - آلة حاسبة لمقعد الطائرة - - - rows (%1) - الصفوف (%1) - - - 1st class rows: %1 - صفوف الطبقة الأولى: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - هنالك طائرة تحتوي على عدد من صفوف مقاعد الركاب. كل صف يحتوي على أربعة مقاعد. - - - 2nd class rows (%1) - صفوف الفئة الثانية: (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_be-tarask.xlf b/demos/plane/xlf/translated_msgs_be-tarask.xlf deleted file mode 100644 index 4580b5f1d..000000000 --- a/demos/plane/xlf/translated_msgs_be-tarask.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Радкоў: %1 - - - seats = - месцаў = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Самалёт мае два месцы ў кабіне экіпажа (пілот і другі пілот), і некалькі пасажырскіх шэрагаў месцаў 1-га кляса і 2-га кляса. Кожны шэраг 1-га кляса утрымлівае чатыры месцы. Кожны шэраг 2-га кляса ўтрымлівае пяць месцаў. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Пабудаваць формулу (ніжэй), якая падлічвае агульную колькасьць месцаў у самалёце пры зьмене радоў (гл. вышэй). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Самалёт мае два месцы ў кабіне экіпажа (пілот і другі пілот), і некалькі шэрагаў пасажырскіх сядзеньняў. Кожны шэраг утрымлівае чатыры месцы. - - - 1st class rows (%1) - радкі першага клясу (%1) - - - 2nd class rows: %1 - Радкі другога клясу: %1 - - - Seats: %1 - Месцаў: %1 - - - Plane Seat Calculator - Калькулятар месцаў у самалёце - - - rows (%1) - радкоў (%1) - - - 1st class rows: %1 - Радкі першага клясу: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Самалёт мае некалькі шэрагаў пасажырскіх сядзеньняў. Кожная шэраг утрымлівае чатыры месцы. - - - 2nd class rows (%1) - радкі другога клясу (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_br.xlf b/demos/plane/xlf/translated_msgs_br.xlf deleted file mode 100644 index 4a7fc0c26..000000000 --- a/demos/plane/xlf/translated_msgs_br.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Renkennadoù : %1 - - - seats = - azezennoù = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - En un nijerez ez eus div azezenn el logell leviañ(evit al loman hag an eil loman), hag un toullad renkennadoù azezennoù tremenidi kentañ hag eil klas. Peder azezenn zo e pep renkennad kentañ klas. Pemp azezenn zo e pemp renkennad eil klas. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Sevel ur formulenn (amañ dindan) evit jediñ an niver a azezennoù en holl en nijerez pa vez kemmet an niver a renkennadoù (amañ a-us). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - En un nijerez ez eus div azezenn el logell leviañ(evit al loman hag an eil loman), hag ur toullad renkennadoù azezennoù evit an dremenidi. Peder azezenn zo e pep renkennad. - - - 1st class rows (%1) - Renkennadoù kentañ klas (%1) - - - 2nd class rows: %1 - Renkennadoù eil klas : %1 - - - Seats: %1 - Azezennoù : %1 - - - Plane Seat Calculator - Jederez azezenn nijerez - - - rows (%1) - renkennadoù (%1) - - - 1st class rows: %1 - Renkennadoù kentañ klas : %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Un nijerez he deus un toullad renkennadoù azezennoù evit ar veajourien. Peder azezenn a zo e pep renkennad. - - - 2nd class rows (%1) - Renkennadoù eil klas (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_ca.xlf b/demos/plane/xlf/translated_msgs_ca.xlf deleted file mode 100644 index 17dfe65e0..000000000 --- a/demos/plane/xlf/translated_msgs_ca.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Files: %1 - - - seats = - seients = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Un avió té dos seients en la cabina de vol (pel pilot i copilot) i un nombre de files per seients de passatgers de primera classe i de segona classe. Cada fila de primera classe conté quatre seients. Cada fila de segona classe conté cinc seients. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Construïu una fórmula (a sota) que calculi el nombre total de seients de l'avió a mida que canviïn les files (a dalt). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Un avió té dos seients en la cabina de vol (pel pilot i pel copilot) i un nombre de files de seients de passatgers. Cada fila conté quatre seients. - - - 1st class rows (%1) - files de primera classe (%1) - - - 2nd class rows: %1 - files de segona classe: %1 - - - Seats: %1 - Seients: %1 - - - Plane Seat Calculator - Calculadora de seients d'avió - - - rows (%1) - files (%1) - - - 1st class rows: %1 - files de primera classe: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Un avió té un nombre de files de seients de passatgers. Cada fila conté quatre seients. - - - 2nd class rows (%1) - files de segona classe (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_da.xlf b/demos/plane/xlf/translated_msgs_da.xlf deleted file mode 100644 index 752fe24f5..000000000 --- a/demos/plane/xlf/translated_msgs_da.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Rækker: %1 - - - seats = - sæder = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Et fly har to pladser i cockpittet (til pilot og med-pilot), og et antal rækker af 1. klasses og 2. klasses passagersæder. Hver 1. klasses række indeholder fire sæder. Hver 2. klasses række indeholder fem sæder. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Opbyg en formel (nedenfor), der beregner det samlede antal pladser på flyet, hvis antal rækker ændres (ovenfor). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Et fly har to pladser i cockpittet (til pilot og med-pilot), og et antal rækker af passagersæder. Hver række indeholder fire sæder. - - - 1st class rows (%1) - 1. klasse rækker (%1) - - - 2nd class rows: %1 - 2. klasse rækker: %1 - - - Seats: %1 - Sæder: %1 - - - Plane Seat Calculator - Flysædelommeregner - - - rows (%1) - rækker (%1) - - - 1st class rows: %1 - 1. klasse rækker: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Et fly har et antal rækker af passagersæder. Hver række indeholder fire sæder. - - - 2nd class rows (%1) - 2. klasse rækker (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_de.xlf b/demos/plane/xlf/translated_msgs_de.xlf deleted file mode 100644 index f06bc7725..000000000 --- a/demos/plane/xlf/translated_msgs_de.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Reihen: %1 - - - seats = - Sitze = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Ein Flugzeug hat zwei Sitze im Pilotenstand (für den Piloten und Co-Piloten) und eine Anzahl an Reihen mit Passagiersitzen der 1. und 2. Klasse. Jede 1.-Klasse-Reihe enthält vier Sitze. Jede 2.-Klasse-Reihe enthält fünf Sitze. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Erstelle eine Formel (unten), die die gesamte Anzahl an Sitzen im Flugzeug berechnet, wenn die Reihen (oben) geändert werden. - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Ein Flugzeug hat zwei Sitze im Pilotenstand (für den Piloten und Co-Piloten) und eine Anzahl an Reihen mit Passagiersitzen. Jede Reihe enthält vier Sitze. - - - 1st class rows (%1) - Reihen der 1. Klasse (%1) - - - 2nd class rows: %1 - Reihen der 2. Klasse: %1 - - - Seats: %1 - Sitze: %1 - - - Plane Seat Calculator - Flugzeugsitzrechner - - - rows (%1) - Reihen (%1) - - - 1st class rows: %1 - Reihen der 1. Klasse: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Ein Flugzeug hat eine Anzahl an Reihen mit Passagiersitzen. Jede Reihe enthält vier Sitze. - - - 2nd class rows (%1) - Reihen der 2. Klasse (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_el.xlf b/demos/plane/xlf/translated_msgs_el.xlf deleted file mode 100644 index 5acb291d2..000000000 --- a/demos/plane/xlf/translated_msgs_el.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Σειρές: %1 - - - seats = - καθίσματα = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Ένα αεροπλάνο έχει δύο καθίσματα στον θάλαμο διακυβέρνησης (για τον κυβερνήτη και τον συγκυβερνήτη), καθώς και έναν αριθμό σειρών καθισμάτων για την 1η και 2η θέση. Κάθε σειρά της 1ης θέσης έχει τέσσερα καθίσματα και κάθε σειρά της 2ης θέσης έχει πέντε καθίσματα. - - - ? - ; - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Φτιάξε έναν τύπο (κάτω) που θα υπολογίζει τον συνολικό αριθμό καθισμάτων του αεροπλάνου καθώς αλλάζουν οι σειρές (πάνω). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Ένα αεροπλάνο έχει δύο καθίσματα στον θάλαμο διακυβέρνησης (για τον κυβερνήτη και τον συγκυβερνήτη), καθώς και έναν αριθμό από σειρές καθισμάτων επιβατών. Κάθε σειρά έχει τέσσερα καθίσματα. - - - 1st class rows (%1) - Σειρές 1ης θέσης (%1) - - - 2nd class rows: %1 - Σειρές 2ης θέσης: %1 - - - Seats: %1 - Καθίσματα: %1 - - - Plane Seat Calculator - Υπολογισμός Θέσεων Σε Αεροπλάνο - - - rows (%1) - σειρές (%1) - - - 1st class rows: %1 - Σειρές 1ης θέσης: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Ένα αεροπλάνο έχει έναν συγκεκριμένο αριθμό σειρών καθισμάτων επιβατών. Κάθε σειρά έχει τέσσερα καθίσματα. - - - 2nd class rows (%1) - Σειρές 2ης θέσης (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_en.xlf b/demos/plane/xlf/translated_msgs_en.xlf deleted file mode 100644 index e471a0008..000000000 --- a/demos/plane/xlf/translated_msgs_en.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Rows: %1 - - - seats = - seats = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - - - 1st class rows (%1) - 1st class rows (%1) - - - 2nd class rows: %1 - 2nd class rows: %1 - - - Seats: %1 - Seats: %1 - - - Plane Seat Calculator - Plane Seat Calculator - - - rows (%1) - rows (%1) - - - 1st class rows: %1 - 1st class rows: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - An airplane has a number of rows of passenger seats. Each row contains four seats. - - - 2nd class rows (%1) - 2nd class rows (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_es.xlf b/demos/plane/xlf/translated_msgs_es.xlf deleted file mode 100644 index e2022b5f1..000000000 --- a/demos/plane/xlf/translated_msgs_es.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Filas: %1 - - - seats = - asientos = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Un avión tiene dos asientos en la cabina de vuelo (para el piloto y co-piloto), y un número de filas de asientos para pasajeros de primera y segunda clase. Cada fila de la primera clase contiene cuatro asientos. Cada fila de la segunda clase contiene cinco asientos. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Construir una fórmula (abajo) que calcule el número total de asientos en el avión cuando las filas sean cambiadas (arriba). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Un avión tiene dos asientos en la cabina de vuelo (para el piloto y co-piloto), y un número de filas de asientos de pasajeros. Cada fila contiene cuatro asientos. - - - 1st class rows (%1) - Filas de primera clase: (%1) - - - 2nd class rows: %1 - Filas de segunda clase: %1 - - - Seats: %1 - Asientos: %1 - - - Plane Seat Calculator - Calculadora de asientos de avión - - - rows (%1) - filas (%1) - - - 1st class rows: %1 - Filas de primera clase: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Un avión  tiene un número de filas de asientos de pasajeros. Cada fila contiene cuatro asientos. - - - 2nd class rows (%1) - Filas de segunda clase: (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_et.xlf b/demos/plane/xlf/translated_msgs_et.xlf deleted file mode 100644 index d88be0a9f..000000000 --- a/demos/plane/xlf/translated_msgs_et.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Ridu: %1 - - - seats = - istmete arv = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Lennuki kokpitis on kaks istet (üks kummalegi piloodile), mingi arv ridu 1. klassi reisijatele ja mingi arv ridu 2. klassi reisijatele. Igas 1. klassi reas on neli istet, igas 2. klassi reas viis istet. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Ehita plokkidest valem, mis arvutab istmete arvu lennukis õigesti sõltumata ridade arvust (seda saad muuta lennuki juures oleva liuguriga). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Lennuki kokpitis on kaks istet (üks kummalegi piloodile) ja mingi arv istemridu reisijatele. Igas reas on neli istet. - - - 1st class rows (%1) - 1. klassi ridu (%1) - - - 2nd class rows: %1 - 2. klassi ridu: %1 - - - Seats: %1 - Istmeid: %1 - - - Plane Seat Calculator - Lennukiistmete kalkulaator - - - rows (%1) - rows (%1) - - - 1st class rows: %1 - 1. klassi ridu: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Lennukis on reisijate istmed mitmes reas. Igas reas on neli istet. - - - 2nd class rows (%1) - 2. klassi ridu (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_fa.xlf b/demos/plane/xlf/translated_msgs_fa.xlf deleted file mode 100644 index 264ec3104..000000000 --- a/demos/plane/xlf/translated_msgs_fa.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - ردیف: %1 - - - seats = - صندلی‌ها = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - یک هواپیما دو صندلی در کابین خلبان دارد (برای خلبان و کمک خلبان) و تهداد از صندلی‌ها مسافرین درجه یک و درجه دو. هر ردیف درجه یک شامل چهار صندلی است. هر ردیف درجه دو شامل پنج صندلی است. - - - ? - ؟ - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - یک فرمول بسازید (پایین) که تعداد کل صندلی‌های هواپیما با تغییر ردیف را حساب کند (بالا). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - یک هواپیما دو صندلی در عرشهٔ پرواز دارد (برای خلبان و کمک خلبان) و تعدادی صندلی مسافرین. هر ردیف شامل چهار صندلی است. - - - 1st class rows (%1) - اولین کلاس ردیف‌ها (%1) - - - 2nd class rows: %1 - دومین کلاس ردیف: %1 - - - Seats: %1 - صندلی‌ها: %1 - - - Plane Seat Calculator - محاسبه‌گر صندلی‌های هواپیما - - - rows (%1) - ردیف‌ها (%1) - - - 1st class rows: %1 - اولین ردیف کلاس: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - یک هواپیما تعداد از صندلی‌های مسافرین را دارد. هر ردیف شمال چهار صندلی است. - - - 2nd class rows (%1) - دومین کلاس ردیف‌ها (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_fr.xlf b/demos/plane/xlf/translated_msgs_fr.xlf deleted file mode 100644 index 9485da29b..000000000 --- a/demos/plane/xlf/translated_msgs_fr.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Rangées : %1 - - - seats = - sièges = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Un avion a deux sièges dans la cabine de pilotage (pour le pilote et le copilote), et un certain nombre de rangées de sièges passager de première et seconde classes. Chaque rangée de première classe contient quatre sièges. Chaque rangée de seconde classe contient cinq sièges. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Construire une formule (ci-dessous) qui calcule le nombre total de sièges dans l’avion quand le nombre de rangées est modifié (ci-dessus). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Un avion a deux sièges dans le poste de pilotage (pour le pilote et le copilote), et un certain nombre de rangées de sièges passager. Chaque rangée contient quatre sièges. - - - 1st class rows (%1) - rangées de première classe (%1) - - - 2nd class rows: %1 - rangées de seconde classe : %1 - - - Seats: %1 - Sièges : %1 - - - Plane Seat Calculator - Calculateur de sièges d’avion - - - rows (%1) - rangées (%1) - - - 1st class rows: %1 - rangées de première classe : %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Un avion a un nombre de rangées de sièges passager. Chaque rangée contient quatre sièges. - - - 2nd class rows (%1) - rangées de seconde classe (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_he.xlf b/demos/plane/xlf/translated_msgs_he.xlf deleted file mode 100644 index 55ac148c5..000000000 --- a/demos/plane/xlf/translated_msgs_he.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - שורות: %1 - - - seats = - מושבים = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - במטוס יש שני מושבים עבור הצוות (בשביל הטייס וטייס המשנה), ומספר שורות מושבים במחלקת הנוסעים הראשונה ובמחלקת הנוסעים השנייה. כל שורה במחלקה הראשונה מכילה ארבעה מושבים. כל שורה במחלקה השנייה מכילה חמישה מושבים. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - בנה נוסחה (למטה) אשר תחשב את סך כל המושבים במטוס בהתאם לשינוי מספר השורות (למעלה). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - במטוס יש שני מושבים עבור הצוות (בשביל הטייס וטייס המשנה), ומספר שורות עם מושבי נוסעים. בכל שורה יש ארבעה מושבים. - - - 1st class rows (%1) - שורות במחלקה ראשונה (%1) - - - 2nd class rows: %1 - שורות במחלקה שנייה: %1 - - - Seats: %1 - מושבים: %1 - - - Plane Seat Calculator - מחשבון מושב במטוס - - - rows (%1) - שורות (%1) - - - 1st class rows: %1 - שורות במחלקה ראשונה: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - במטוס יש מספר שורות עם מושבי נוסעים. בכל שורה יש ארבעה מושבים. - - - 2nd class rows (%1) - שורות במחלקה שנייה: (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_hrx.xlf b/demos/plane/xlf/translated_msgs_hrx.xlf deleted file mode 100644 index 263d30507..000000000 --- a/demos/plane/xlf/translated_msgs_hrx.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Reihe: %1 - - - seats = - Sitze = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - En Fluchzeich hot zwooi Sitze im Pilotstand (für den Pilot und Co-Pilot) und en Oonzohl an Reihe mit Passagiersitze der 1. und 2. Klasse. Jede 1.-Klasse-Reih enthält vier Sitze. Jede 2.-Klasse-Reih enthält fünf Sitze. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Erstell en Formel (unne), die die gesamte Oonzohl an Sitze im Fluchzeich berechnet, wenn die Reihe (uwe) geännert sin. - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - En Fluchzeich hot zwooi Sitze im Pilotestand (für den Pilot und Co-Pilot) und en Oonzohl an Reihe mit Passagiersitze. Jede Reih enthält vier Sitze. - - - 1st class rows (%1) - Reihe von der 1. Klasse (%1) - - - 2nd class rows: %1 - Reihe von der 2. Klasse: %1 - - - Seats: %1 - Sitz: %1 - - - Plane Seat Calculator - Fluchzeichsitzrechner - - - rows (%1) - Reihe (%1) - - - 1st class rows: %1 - Reihe von der 1. Klasse: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - En Fluchzeich hot en Oonzohl an Reihe mit Passagiersitze. Jede Reih enthält vier Sitze. - - - 2nd class rows (%1) - Reihe von der 2. Klasse (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_hu.xlf b/demos/plane/xlf/translated_msgs_hu.xlf deleted file mode 100644 index c44d1aa9d..000000000 --- a/demos/plane/xlf/translated_msgs_hu.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Sorok száma: %1 - - - seats = - Ülések száma = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Egy repülőgépnek 2 ülése van a pilótafülkében (a pilótának és a másodpilótának), az utasok 1. és 2. osztályon utazhatnak. Az 1. osztályon négy szék van egy sorban. A 2. osztályon öt szék van egy sorban. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Készítsd el a képletet (lent) amivel kiszámolható, hogy hány ülés van összesen a repülőgépen annak függvényében, ahogy (fent) állítod a sorok számát. - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Egy repülőgépnek 2 ülése van a pilótafülkében (a pilótának és a másodpilótának), az utasok több sorban ülnek az utastérben. Az utastér minden sorában négy szék van. - - - 1st class rows (%1) - 1. osztály sorai (%1) - - - 2nd class rows: %1 - 2. osztály: %1 sor - - - Seats: %1 - Ülések száma összesen: %1 - - - Plane Seat Calculator - Repülőgép alkalmazás - - - rows (%1) - Sorok száma (%1) - - - 1st class rows: %1 - 1. osztály: %1 sor - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Egy repülőgépen az utasok több sorban ülnek az utastérben. Az utastér minden sorában négy szék van. - - - 2nd class rows (%1) - 2. osztály sorai (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_ia.xlf b/demos/plane/xlf/translated_msgs_ia.xlf deleted file mode 100644 index 83ae2c6d2..000000000 --- a/demos/plane/xlf/translated_msgs_ia.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Filas: %1 - - - seats = - sedes = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Un avion ha duo sedes in le cabina (pro le pilota e le copilota) e un numero de filas de sedes pro passageros del prime classe e del secunde classes. Cata fila del prime classe contine quatro sedes. Cata fila del secunde classe contine cinque sedes. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Construe un formula (ci infra) que calcula le numero total de sedes in le avion quando le numero de filas es cambiate (ci supra). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Un avion ha duo sedes in le cabina (pro le pilota e le copilota) e un numero de filas de sedes pro passageros. Cata fila contine quatro sedes. - - - 1st class rows (%1) - filas de prime classe (%1) - - - 2nd class rows: %1 - Filas de secunde classe: %1 - - - Seats: %1 - Sedes: %1 - - - Plane Seat Calculator - Calculator de sedias de avion - - - rows (%1) - filas (%1) - - - 1st class rows: %1 - Filas de prime classe: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Un avion ha un numero de filas de sedes pro passageros. Cata fila contine quatro sedes. - - - 2nd class rows (%1) - filas de secunde classe (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_is.xlf b/demos/plane/xlf/translated_msgs_is.xlf deleted file mode 100644 index 3810f4b34..000000000 --- a/demos/plane/xlf/translated_msgs_is.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Raðir: %1 - - - seats = - sæti = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Flugvél er með tvö sæti í stjórnklefa (fyrir flugmanninn og aðstoðarflugmanninn) og einhvern fjölda sætaraða fyrir farþega á 1. og 2. farrými. Hver sætaröð á 1. farrými hefur fjögur sæti. Hver sætaröð á 2. farrými hefur fimm sæti. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Búðu til formúlu (hér fyrir neðan) sem reiknar heildarfjölda sæta í flugvélinni eftir því sem röðunum er breytt (hér fyrir ofan). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Flugvél er með tvö sæti í stjórnklefa (fyrir flugmanninn og aðstoðarflugmanninn) og einhvern fjölda sætaraða fyrir farþega. Hver sætaröð hefur fjögur sæti. - - - 1st class rows (%1) - raðir 1. farrými (%1) - - - 2nd class rows: %1 - Raðir 2. farrými: %1 - - - Seats: %1 - Sæti: %1 - - - Plane Seat Calculator - Flugsætareiknir - - - rows (%1) - raðir (%1) - - - 1st class rows: %1 - Raðir 1. farrými: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Flugvél er með einhvern fjölda sætaraða fyrir farþega. Í hverri röð eru fjögur sæti. - - - 2nd class rows (%1) - raðir 2. farrými (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_it.xlf b/demos/plane/xlf/translated_msgs_it.xlf deleted file mode 100644 index 27bad0dcf..000000000 --- a/demos/plane/xlf/translated_msgs_it.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - File: %1 - - - seats = - sedili = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Un aereo ha due posti nella cabina di pilotaggio (per il pilota e il co-pilota), e un numero di file in prima e seconda classe, con i posti a sedere dei passeggeri. Ogni fila della prima classe contiene quattro posti. Quelle invece della seconda classe, ne contengono cinque. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Costruisci una formula (sotto) che calcola il numero totale di posti a sedere su un aeroplano, così come cambiano le file di posti (sopra). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Un aeroplano ha due posti a sedere nella cabina di pilotaggio (per il pilota e co-pilota), e un numero di file con i posti a sedere dei passeggeri. Ogni fila contiene quattro posti. - - - 1st class rows (%1) - file 1ª classe (%1) - - - 2nd class rows: %1 - File 2ª classe: %1 - - - Seats: %1 - Sedili: %1 - - - Plane Seat Calculator - Calcolo posti aereo - - - rows (%1) - file (%1) - - - 1st class rows: %1 - File 1ª classe: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Un aeroplano ha un numero di file contenenti i posti a sedere dei passeggeri. Ogni fila, contiene quattro posti a sedere. - - - 2nd class rows (%1) - File 2ª classe (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_ja.xlf b/demos/plane/xlf/translated_msgs_ja.xlf deleted file mode 100644 index b04624a27..000000000 --- a/demos/plane/xlf/translated_msgs_ja.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - 列の数: %1 - - - seats = - 座席の数 = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - 飛行機には、操縦室の 2 つの座席 (操縦士と副操縦士) と、ファーストクラスとセカンドクラスの乗客の座席の列があります。それぞれの列に、ファーストクラスでは 4 つの座席、セカンドクラスでは 5 つの座席があります。 - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - 飛行機の座席の数を計算する式を、上で列の数を変更しても正しくなるように、下に入力してください。 - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - 飛行機には、操縦室の 2 つの座席 (操縦士と副操縦士) と、乗客の座席の列があります。それぞれの列に 4 つの座席があります。 - - - 1st class rows (%1) - ファーストクラスの列数 (%1) - - - 2nd class rows: %1 - セカンドクラスの列数: %1 - - - Seats: %1 - 座席の数: %1 - - - Plane Seat Calculator - 飛行機座席計算機 - - - rows (%1) - 列の数 (%1) - - - 1st class rows: %1 - ファーストクラスの列数: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - 飛行機に乗客の座席の列があります。それぞれの列に 4 つの座席があります。 - - - 2nd class rows (%1) - セカンドクラスの列数 (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_ko.xlf b/demos/plane/xlf/translated_msgs_ko.xlf deleted file mode 100644 index 07e23288e..000000000 --- a/demos/plane/xlf/translated_msgs_ko.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - 행 수: %1 - - - seats = - 좌석수 = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - 비행기는 비행 갑판(조종사와 부조종사용)에서 좌석 두 개가 있고, 1등석과 2등석 승객 좌석의 행 수가 있습니다. 각 1등석 행에는 시트 네 개가 포함되어 있습니다. 각 2등석 행에는 시트 다섯 개가 포함되어 있습니다. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - 행이 바뀐(위) 비행기에 좌석의 총 수를 계산하는 공식(아래)을 구축하세요. - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - 비행기는 비행 갑판(조종사와 부조종사용)에서 좌석 두 개가 있고, 승객 좌석의 행 수가 있습니다. 각 행에는 시트 네 개가 포함되어 있습니다. - - - 1st class rows (%1) - 1등석 행 수 (%1) - - - 2nd class rows: %1 - 2등석 행 수: %1 - - - Seats: %1 - 좌석 수: %1 - - - Plane Seat Calculator - 비행기 좌석 계산기 - - - rows (%1) - 행 수 (%1) - - - 1st class rows: %1 - 1등석 행 수: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - 비행기는 승객 좌석의 행 수가 있습니다. 각 행에는 시트 네 개가 포함되어 있습니다. - - - 2nd class rows (%1) - 2등석 행 수 (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_ms.xlf b/demos/plane/xlf/translated_msgs_ms.xlf deleted file mode 100644 index c34f084a3..000000000 --- a/demos/plane/xlf/translated_msgs_ms.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Baris: %1 - - - seats = - tempat duduk = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Sebuah kapal terbang mempunyai tempat duduk di kokpit (untuk juruterbang dan pembantunya) dan sebilangan baris tempat duduk penumpang kelas pertama dan kelas kedua. Setiap baris kelas pertama mengandungi empat tempat duduk. Setiap baris kelas pertama mengandungi lima tempat duduk. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Wujudkan formula (di bawah) yang mengira jumlah tempat duduk di dalam kapal terbang sedangkan baris-barisnya diubah (di atas). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Sebuah kapal terbang mempunyai tempat duduk di kokpit (untuk juruterbang dan pembantunya) dan sebilangan baris tempat duduk penumpang. Setiap baris mengandungi empat tempat duduk. - - - 1st class rows (%1) - baris kelas pertama (%1) - - - 2nd class rows: %1 - Baris kelas kedua: %1 - - - Seats: %1 - Tempat duduk: %1 - - - Plane Seat Calculator - Pengira Tempat Duduk Kapal Terbang - - - rows (%1) - baris (%1) - - - 1st class rows: %1 - Baris kelas pertama: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Sebuah kapal terbang mempunyai sebilangan baris tempat duduk penumpang. Setiap baris mengandungi empat tempat duduk. - - - 2nd class rows (%1) - baris kelas kedua (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_nb.xlf b/demos/plane/xlf/translated_msgs_nb.xlf deleted file mode 100644 index 99c9c6a99..000000000 --- a/demos/plane/xlf/translated_msgs_nb.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Rader: %1 - - - seats = - seter = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Et fly har to seter i cockpit (for piloten og andrepiloten), og et antall rader med passasjerseter på første og andre klasse. Hver av radene på første klasse har fire seter. Hver av radene på andre klasse har fem seter. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Bygg en formel (under) som beregner det totale antall seter på flyet etter hvert som radene endres (over). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Et fly har to seter i cockpit (for piloten og andrepiloten), og et antall rader med passasjerseter. Hver rad inneholder fire seter. - - - 1st class rows (%1) - Rader i første klasse (%1) - - - 2nd class rows: %1 - Rader i andre klasse: %1 - - - Seats: %1 - Seter: %1 - - - Plane Seat Calculator - Flysetekalkulator - - - rows (%1) - rader (%1) - - - 1st class rows: %1 - Rader i første klasse: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Et fly har et antall rader med passasjerseter. Hver rad inneholder fire seter. - - - 2nd class rows (%1) - Rader i andre klasse (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_nl.xlf b/demos/plane/xlf/translated_msgs_nl.xlf deleted file mode 100644 index 6f36fa026..000000000 --- a/demos/plane/xlf/translated_msgs_nl.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Rijen: %1 - - - seats = - stoelen= - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Een vliegtuig heeft twee stoelen in de cockpit (voor de piloot en de copiloot) en een aantal rijen voor 1e klasse en 2e klasse passagiers. Iedere rij in de 1e klasse heeft vier stoelen. Iedere rij in de 2e klasse heeft vijf stoelen. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Maak hieronder een formule die het totale aantal stoelen in het vliegtuig berekent als het aantal rijen hierboven wordt aangepast. - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Een vliegtuig heeft twee stoelen in de cockpit (voor de piloot en de copiloot) en een aantal rijen met stoelen voor passagiers. Iedere rij bevat vier stoelen. - - - 1st class rows (%1) - Rijen 1e klas (%1) - - - 2nd class rows: %1 - Rijen 2e klas: %1 - - - Seats: %1 - Zitplaatsen: %1 - - - Plane Seat Calculator - Vliegtuigstoelencalculator - - - rows (%1) - rijen (%1) - - - 1st class rows: %1 - Rijen 1e klas: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Een vliegtuig heeft een aantal rijen met stoelen. Iedere rij heeft vier stoelen. - - - 2nd class rows (%1) - Rijen 2e klas (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_pl.xlf b/demos/plane/xlf/translated_msgs_pl.xlf deleted file mode 100644 index 4c8b044e2..000000000 --- a/demos/plane/xlf/translated_msgs_pl.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Rzędów: %1 - - - seats = - siedzeń = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Samolot ma dwa miejsca w kabinie pilotów (dla pierwszego i drugiego pilota) oraz rzędy siedzeń dla pasażerów pierwszej i drugiej klasy. Każdy rząd pierwszej klasy składa się z czterech siedzeń. Każdy rząd drugiej klasy składa się z pięciu siedzeń. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Zbuduj wzór (poniżej), który pozwala obliczyć łączną liczbę siedzeń w samolocie w funkcji zmieniającej się liczby rzędów (powyżej). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Samolot ma dwa miejsca w kabinie pilotów (dla pierwszego i drugiego pilota) oraz rzędy siedzeń dla pasażerów. Każdy taki rząd składa się z czterech siedzeń. - - - 1st class rows (%1) - Rzędów w pierwszej klasie (%1) - - - 2nd class rows: %1 - Rzędów w drugiej klasie: %1 - - - Seats: %1 - Siedzeń: %1 - - - Plane Seat Calculator - Kalkulator miejsc w samolocie. - - - rows (%1) - rzędów (%1) - - - 1st class rows: %1 - Rzędów w pierwszej klasie: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Samolot ma kilka rzędów siedzeń pasażerów. Każdy rząd zawiera cztery miejsca. - - - 2nd class rows (%1) - Rzędów w drugiej klasie (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_pms.xlf b/demos/plane/xlf/translated_msgs_pms.xlf deleted file mode 100644 index 0fef91217..000000000 --- a/demos/plane/xlf/translated_msgs_pms.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Linie: %1 - - - seats = - sedij = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - N'avion a l'ha doi sedij ant la cabin-a ëd pilotage (për ël pilòta e ël cò-pilòta) e un chèich nùmer ëd file ëd sedij pr'ij passagé ëd prima e sconda classa. Minca fila ëd prima classa a conten quatr sedij. Minca fila ëd seconda classa a conten sinch sedij. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Fabriché na fórmola (sì-sota) ch'a fa 'l cont dël nùmer total ëd sedij ant l'avion cand che ël nùmer dle file a cangia (sì-dzora). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - N'avion a l'ha doi sedij ant la cabin-a ëd pilotage (për ël pilòta e ël cò-pilòta), e un chèich nùmer ëd file ëd sedij pr'ij passagé. Minca fila a conten quatr sedij. - - - 1st class rows (%1) - linie ëd prima classa (%1) - - - 2nd class rows: %1 - linie ëd seconda classa: %1 - - - Seats: %1 - Sedij: %1 - - - Plane Seat Calculator - Calcolator ëd sedij d'avion - - - rows (%1) - linie (%1) - - - 1st class rows: %1 - linie ëd prima classa: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - N'avion a l'ha un nùmer ëd file ëd sedij da passëgé. Minca fila a l'ha quatr sedij. - - - 2nd class rows (%1) - linie ëd seconda classa (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_pt-br.xlf b/demos/plane/xlf/translated_msgs_pt-br.xlf deleted file mode 100644 index 7bdd9ccff..000000000 --- a/demos/plane/xlf/translated_msgs_pt-br.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Filas: %1 - - - seats = - assentos = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Um avião tem dois assentos na cabine de comando (para o piloto e o copiloto) e um número de filas de assentos na primeira e na segunda classe. Cada fila da primeira classe contém quatro assentos. Cada fila da segunda classe contém cinco assentos. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Elabore uma fórmula (abaixo) que calcule o número total de assentos no avião a medida que as filas são alteradas (acima). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Um avião tem dois assentos na cabine de comando (para o piloto e o copiloto) e um número de filas de assentos para os passageiros. Cada fila contém quatro assentos. - - - 1st class rows (%1) - filas na primeira classe (%1) - - - 2nd class rows: %1 - filas na segunda classe: %1 - - - Seats: %1 - Assentos: %1 - - - Plane Seat Calculator - Calculadora de Assentos em Avião - - - rows (%1) - filas (%1) - - - 1st class rows: %1 - filas na primeira classe: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Um avião tem um número de filas de assentos para os passageiros. Cada fila contém quatro assentos. - - - 2nd class rows (%1) - filas na segunda classe (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_ro.xlf b/demos/plane/xlf/translated_msgs_ro.xlf deleted file mode 100644 index 614a3bda2..000000000 --- a/demos/plane/xlf/translated_msgs_ro.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Rânduri: %1 - - - seats = - scaune = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Un avion are două scaune în carlingă (pentru pilot și copilot) și un număr de rânduri cu scaune de clasa I și clasa a II-a pentru pasageri. Fiecare rând de clasa I conține patru scaune. Fiecare rând de clasa a II-a conține cinci scaune. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Construiește o formulă (mai jos) care calculează numărul total de locuri dintr-un avion în timp ce rândurile se schimbă (mai sus). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Un avion are două scaune în carlingă (pentru pilot și copilot) și un număr de rânduri cu scaune pentru pasageri. Fiecare rând conține patru scaune. - - - 1st class rows (%1) - rânduri de clasa I (%1) - - - 2nd class rows: %1 - rânduri de clasa a II-a: %1 - - - Seats: %1 - Scaune: %1 - - - Plane Seat Calculator - Calculator pentru locurile dintr-un avion - - - rows (%1) - rânduri (%1) - - - 1st class rows: %1 - rânduri de clasa I: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Un avion are un număr de rânduri cu scaune pentru pasageri. Fiecare rând conține patru scaune. - - - 2nd class rows (%1) - rânduri de clasa a II-a (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_ru.xlf b/demos/plane/xlf/translated_msgs_ru.xlf deleted file mode 100644 index d25b25458..000000000 --- a/demos/plane/xlf/translated_msgs_ru.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Рядов: %1 - - - seats = - места = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - В самолёте 2 места для пилота и его помощника, несколько рядов с пассажирскими местами первого класса, а также несколько рядов с пассажирскими местами второго класса. В каждом ряду первого класса 4 места. В каждом ряду второго класса 5 мест. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Постройте формулу в области ниже, которая поможет рассчитать общее количество мест в самолёте (как на рисунке выше). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - В самолёте 2 места для пилота и его помощника, а также несколько рядов с пассажирскими местами. В каждом ряду 4 места. - - - 1st class rows (%1) - ряды 1-го класса (%1) - - - 2nd class rows: %1 - Рядов 2-го класса: %1 - - - Seats: %1 - Мест: %1 - - - Plane Seat Calculator - Калькулятор посадочных мест в самолёте - - - rows (%1) - ряды (%1) - - - 1st class rows: %1 - Рядов 1-го класса: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - В самолёте несколько рядов с пассажирскими местами. В каждом ряду 4 места. - - - 2nd class rows (%1) - ряды 2-го класса (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_sc.xlf b/demos/plane/xlf/translated_msgs_sc.xlf deleted file mode 100644 index 632812356..000000000 --- a/demos/plane/xlf/translated_msgs_sc.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Fileras: %1 - - - seats = - cadironis = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Unu aparèchiu tenit duus cadironis in sa cabina de cumandu (po su pilota e su co-pilota), e unas cantu fileras de cadironis po passigeris de prima classi e de segunda classi. Dònnia filera de prima classi tenit cuatru cadironis. Dònnia filera de segunda classi tenit cincu cadironis. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Cuncorda una formula (innoi asuta) chi cumpudit su numeru totali de postus a setzi in s'aparechiu, a segunda de comenti mudant is fileras de postus (innoi in susu) - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Unu aparèchiu tenit duus cadironis in sa cabina de cumandu (po su pilota e su co-pilota), e unas cantu fileras de cadironis po passigeris. Dònnia filera tenit cuatru cadironis. - - - 1st class rows (%1) - fileras de primu classi (%1) - - - 2nd class rows: %1 - fileras de segunda classi: %1 - - - Seats: %1 - Cadironis: %1 - - - Plane Seat Calculator - Fai su contu de is cadironis de unu aparèchiu - - - rows (%1) - fileras (%1) - - - 1st class rows: %1 - fileras de primu classi: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Unu aparèchiu tenit unas cantu fileras de cadironis po passigeris. Dònnia filera tenit cuatru cadironis. - - - 2nd class rows (%1) - fileras de segunda classi (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_sv.xlf b/demos/plane/xlf/translated_msgs_sv.xlf deleted file mode 100644 index f3d836fba..000000000 --- a/demos/plane/xlf/translated_msgs_sv.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Rader: %1 - - - seats = - säten = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Ett flygplan har två säten i cockpiten (ett för piloten och ett för andrepiloten) och ett antal rader med passagerarsäten i första och andra klass. Varje rad i första klass innehåller fyra säten. Varje rad i andra klass innehåller fem säten. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Bygg en formel (nedan) som beräknar det totala antalet säten på flygplanet när raderna ändras (ovan). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Ett flygplan har två säten i cockpiten (ett för piloten och ett för andrepiloten) och ett antal rader med passagerarsäten. Varje rad innehåller fyra säten. - - - 1st class rows (%1) - Rader i första klass (%1) - - - 2nd class rows: %1 - Rader i andra klass: %1 - - - Seats: %1 - Säten: %1 - - - Plane Seat Calculator - Plansäteskalkylator - - - rows (%1) - rader (%1) - - - 1st class rows: %1 - Rader i första klass: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Ett flygplan har ett antal rader med passagerarsäten. Varje rad innehåller fyra säten. - - - 2nd class rows (%1) - Rader i andra klass (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_th.xlf b/demos/plane/xlf/translated_msgs_th.xlf deleted file mode 100644 index 0967d4d19..000000000 --- a/demos/plane/xlf/translated_msgs_th.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - %1 แถว - - - seats = - จำนวนที่นั่ง = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - ภายในเครื่องบินจะมีที่นั่งนักบินอยู่ 2 ที่ (สำหรับนักบิน และผู้ช่วยนักบิน) และจะมีแถวที่นั่งสำหรับผู้โดยสาร "ชั้นเฟิร์สคลาส" และ "ชั้นธุรกิจ" อยู่จำนวนหนึ่ง โดยในชั้นเฟิร์สคลาสจะมีแถวละ 4 ที่นั่ง ส่วนในชั้นธุรกิจจะมีแถวละ 5 ที่นั่ง - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - สร้างสูตรคำนวณ (ด้านล่าง) เพื่อคำนวณหาจำนวนที่นั่งทั้งหมดบนเครื่องบิน ตามจำนวนแถวที่เปลี่ยนไป (ด้านบน) - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - ภายในเครื่องบินจะมีที่นั่งนักบินอยู่ 2 ที่ (สำหรับนักบิน และผู้ช่วยนักบิน) และมีแถวที่นั่งผู้โดยสารอยู่จำนวนหนึ่ง ในแต่ละแถวจะมี 4 ที่นั่ง - - - 1st class rows (%1) - จำนวนแถวชั้นเฟิร์สคลาส (%1) - - - 2nd class rows: %1 - ชั้นธุรกิจ %1 แถว - - - Seats: %1 - คำนวณได้ทั้งหมด %1 ที่นั่ง - - - Plane Seat Calculator - ระบบคำนวณที่นั่งบนเครื่องบิน - - - rows (%1) - จำนวนแถว (%1) - - - 1st class rows: %1 - ชั้นเฟิร์สคลาส %1 แถว - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - ภายในเครื่องบินประกอบไปด้วยแถวของที่นั่งผู้โดยสาร ในแต่ละแถวจะมี 4 ที่นั่ง - - - 2nd class rows (%1) - จำนวนแถวชั้นธุรกิจ (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_tr.xlf b/demos/plane/xlf/translated_msgs_tr.xlf deleted file mode 100644 index 678541a32..000000000 --- a/demos/plane/xlf/translated_msgs_tr.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Sıralar: %1 - - - seats = - koltuklar = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Bir uçağın uçuş güvertesinde iki koltuğu (pilot ve yardımcı pilot için), ve belirli sayıda birinci sınıf ve ikinci sınıf yolcu koltuğu sırası vardır. Her birinci sınıf sıra dört koltuk içerir. Her ikinci sınıf sıra beş koltuk içerir. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Sıralar(üstte) değiştikçe uçaktaki toplam koltuk sayısını hesaplayan bir formül(altta) oluşturun. - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Bir uçağın uçuş güvertesinde iki koltuğu (pilot ve yardımcı pilot için), ve belirli sayıda koltuk sırası vardır. Her sıra dört koltuk içerir. - - - 1st class rows (%1) - Birinci sınıf sıralar (%1) - - - 2nd class rows: %1 - İkinci sınıf sıralar: %1 - - - Seats: %1 - Koltuklar: %1 - - - Plane Seat Calculator - Uçak Koltuğu Hesaplayıcı - - - rows (%1) - sıralar (%1) - - - 1st class rows: %1 - Birinci sınıf sıralar: (%1) - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Bir uçağın belirli sayıda koltuk sırası vardır. Her sıra dört koltuk içerir. - - - 2nd class rows (%1) - İkinci sınıf sıralar (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_uk.xlf b/demos/plane/xlf/translated_msgs_uk.xlf deleted file mode 100644 index d5e7682a5..000000000 --- a/demos/plane/xlf/translated_msgs_uk.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Рядки: %1 - - - seats = - місць= - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Літак має два місця в кабіні екіпажу (пілот і другий пілот), і кілька рядів 1-го класу 2-го класу пасажирських місць. Кожний ряд 1-го класу містить чотири місця. Кожен ряд 2-го класу містить п'ять місць. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Побудувати формулу (нижче), яка обчислює кількість місць на літаку при зміні рядків (див. вище). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Літак має два місця в кабіні екіпажу (пілот і другий пілот), і кілька рядів пасажирських сидінь. Кожен рядок містить чотири місця. - - - 1st class rows (%1) - рядів 1-го класу (%1) - - - 2nd class rows: %1 - рядів 2-го класу: %1 - - - Seats: %1 - Місць: %1 - - - Plane Seat Calculator - Калькулятор місць у літаку - - - rows (%1) - рядки (%1) - - - 1st class rows: %1 - рядів 1-го класу: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Літак має кілька рядів пасажирських сидінь. Кожен ряд містить чотири місця. - - - 2nd class rows (%1) - рядів 2-го класу (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_vi.xlf b/demos/plane/xlf/translated_msgs_vi.xlf deleted file mode 100644 index 1f4ef6fcf..000000000 --- a/demos/plane/xlf/translated_msgs_vi.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - Số hàng ghế: %1 - - - seats = - Tính số chỗ ngồi = - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - Một chiếc máy bay này có hai chỗ ngồi ở sàn (cho phi công trưởng và phi công phó), và một số hàng ghế hạng 1 và hạng 2. Mỗi hàng hạng 1 có bốn chỗ ngồi. Mỗi hàng hạng 2 có năm chỗ ngồi. - - - ? - ? - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - Dưới đây hãy tạo công thức tính số chỗ ngồi trên máy bay để nó thay đổi tùy theo số lượng hàng ghế (hình trên). - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - Một máy bay có hai ghế trong buồng lái (dành cho phi công trưởng và phi công phụ), và một loạt hàng ghế cho hành khách. Mỗi hàng có bốn ghế (bốn chỗ ngồi). - - - 1st class rows (%1) - số hàng hạng nhất (%1) - - - 2nd class rows: %1 - Hàng hạng hai: %1 - - - Seats: %1 - Số chỗ ngồi: %1 - - - Plane Seat Calculator - Máy bay ghế máy tính - - - rows (%1) - đếm số hàng ghế (%1) - - - 1st class rows: %1 - Hàng hạng nhất: %1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - Máy bay có một số hàng ghế hành khách. Mỗi hàng có bốn chỗ ngồi. - - - 2nd class rows (%1) - số hàng hạng hai (%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_zh-hans.xlf b/demos/plane/xlf/translated_msgs_zh-hans.xlf deleted file mode 100644 index 2cbb7e858..000000000 --- a/demos/plane/xlf/translated_msgs_zh-hans.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - 行:%1 - - - seats = - 座位= - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - 一架飞机除了有两个座位供正副驾驶员,还有一定量行数的头等及经济乘客座位。头等每行共四座,经济每行共五座。 - - - ? - - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - 于下方写出一条公式以计算飞机上的座位总数。 - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - 一架飞机除了有两个座位供正副驾驶员,还有一定量行数的乘客座位。每行共四座。 - - - 1st class rows (%1) - 头等行(%1) - - - 2nd class rows: %1 - 经济等行:%1 - - - Seats: %1 - 座位:%1 - - - Plane Seat Calculator - 飞机座位计算器 - - - rows (%1) - 行 (%1) - - - 1st class rows: %1 - 头等行:%1 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - 一架飞机有一定量行数的乘客座位,每行共四座。 - - - 2nd class rows (%1) - 经济等行(%1) - - - - diff --git a/demos/plane/xlf/translated_msgs_zh-hant.xlf b/demos/plane/xlf/translated_msgs_zh-hant.xlf deleted file mode 100644 index 2dadf6d4e..000000000 --- a/demos/plane/xlf/translated_msgs_zh-hant.xlf +++ /dev/null @@ -1,63 +0,0 @@ - - - - - - Rows: %1 - 排:%1 - - - seats = - 座位= - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of 1st class and 2nd class passenger seats. Each 1st class row contains four seats. Each 2nd class row contains five seats. - 一架飛機除了有兩個座位供正副機師,還有一定量行數的頭等及經濟乘客座位。頭等艙每排都包含四個席位,經濟艙每排都包含五個席位。。 - - - ? - - - - Build a formula (below) that calculates the total number of seats on the airplane as the rows are changed (above). - 於下方寫出一條公式以計算飛機上的座位總數。 - - - An airplane has two seats in the flight deck (for the pilot and co-pilot), and a number of rows of passenger seats. Each row contains four seats. - 一架飛機除了有兩個座位供正副機師,還有一定量行數的乘客座位。每排都包含四個席位。 - - - 1st class rows (%1) - 頭等艙(%1) - - - 2nd class rows: %1 - 經濟艙:%1 排 - - - Seats: %1 - 座位:%1 - - - Plane Seat Calculator - 飛機座位計算器 - - - rows (%1) - 排(%1) - - - 1st class rows: %1 - 頭等艙:%1 排 - - - An airplane has a number of rows of passenger seats. Each row contains four seats. - 一架飛機有一定量行數的乘客座位,每排都包含四個席位。 - - - 2nd class rows (%1) - 經濟艙(%1) - - - -