diff options
Diffstat (limited to 'blockly/demos/plane/plane.js')
-rw-r--r-- | blockly/demos/plane/plane.js | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/blockly/demos/plane/plane.js b/blockly/demos/plane/plane.js new file mode 100644 index 0000000..99e5c17 --- /dev/null +++ b/blockly/demos/plane/plane.js @@ -0,0 +1,445 @@ +/** + * Blockly Demos: Plane Seat Calculator + * + * Copyright 2012 Google Inc. + * https://developers.google.com/blockly/ + * + * 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 JavaScript for Blockly's Plane Seat Calculator demo. + * @author fraser@google.com (Neil Fraser) + */ +'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 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); +}; + +/** + * 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 = + '<xml>' + + ' <block type="plane_set_seats" deletable="false" x="70" y="70">' + + ' </block>' + + '</xml>'; + 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)); + + // Update blocks to show values. + function updateBlocks(blocks) { + for (var i = 0, block; block = blocks[i]; i++) { + block.customUpdate && block.customUpdate(); + } + } + updateBlocks(Plane.workspace.getAllBlocks()); + updateBlocks(Plane.workspace.flyout_.workspace_.getAllBlocks()); +}; + +/** + * 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.setAttribute('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.setAttribute('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('<script src="generated/' + Plane.LANG + '.js"></script>\n'); |