123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561 |
- /* BlocksCAD utilities for languages and dialogs */
- 'use strict';
- var BSUtils = BSUtils || {};
- /**
- * Lookup for names of supported languages. Keys should be in ISO 639 format.
- */
- BSUtils.LANGUAGE_NAME = {
- 'en': 'English',
- // 'de': 'Deutsch',
- // 'fr': 'Français',
- // 'sl' : 'Slovenščina',
- 'zh-hant': '繁體中文',
- 'zh-hans': '简体中文'
- // 'es': 'Español'
- };
- // BSUtils.LANGUAGE_NAME = {
- // 'ar': 'العربية',
- // 'be-tarask': 'Taraškievica',
- // 'br': 'Brezhoneg',
- // 'ca': 'Català',
- // 'cs': 'Česky',
- // 'da': 'Dansk',
- // 'el': 'Ελληνικά',
- // 'en': 'English',
- // 'es': 'Español',
- // 'fa': 'فارسی',
- // 'he': 'עברית',
- // 'hrx': 'Hunsrik',
- // 'hu': 'Magyar',
- // 'ia': 'Interlingua',
- // 'is': 'Íslenska',
- // 'it': 'Italiano',
- // 'ja': '日本語',
- // 'ko': '한국어',
- // 'mk': 'Македонски',
- // 'ms': 'Bahasa Melayu',
- // 'nb': 'Norsk Bokmål',
- // 'nl': 'Nederlands, Vlaams',
- // 'oc': 'Lenga d\'òc',
- // 'pl': 'Polski',
- // 'pms': 'Piemontèis',
- // 'pt-br': 'Português Brasileiro',
- // 'ro': 'Română',
- // 'ru': 'Русский',
- // 'sc': 'Sardu',
- // 'sk': 'Slovenčina',
- // 'sr': 'Српски',
- // 'sv': 'Svenska',
- // 'th': 'ภาษาไทย',
- // 'tlh': 'tlhIngan Hol',
- // 'tr': 'Türkçe',
- // 'uk': 'Українська',
- // 'vi': 'Tiếng Việt',
- // 'zh-hans': '簡體中文',
- // 'zh-hant': '正體中文'
- // };
- /**
- * List of RTL languages.
- */
- BSUtils.LANGUAGE_RTL = ['ar', 'fa', 'he'];
- /**
- * List of languages supported by this app. Values should be in ISO 639 format.
- * @type !Array.<string>=
- */
- BSUtils.LANGUAGES = undefined;
- /**
- * 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.
- */
- BSUtils.getStringParamFromUrl = function(name, defaultValue) {
- var val =
- window.location.search.match(new RegExp('[?&]' + name + '=([^&]+)'));
- return val ? decodeURIComponent(val[1].replace(/\+/g, '%20')) : defaultValue;
- };
- /**
- * Get the language of this user from the URL.
- * @return {string} User's language.
- */
- BSUtils.getLang = function() {
- var lang = BSUtils.getStringParamFromUrl('lang', '');
- if (BSUtils.LANGUAGE_NAME[lang] === undefined) {
- // check to see if we had a previous language stored in localStorage.
- var pLang = Blockscad.getLangFromLS();
- if (pLang && pLang != 'null') {
- lang = pLang;
- }
- // Default to English.
- else lang = 'en';
- }
- Blockscad.pLang = lang;
- return lang;
- };
- /**
- * User's language (e.g. "en").
- * @type string=
- */
- BSUtils.LANG = BSUtils.getLang();
- /**
- * Is the current language (BSUtils.LANG) an RTL language?
- * @return {boolean} True if RTL, false if LTR.
- */
- BSUtils.isRtl = function() {
- return BSUtils.LANGUAGE_RTL.indexOf(BSUtils.LANG) != -1;
- };
- /**
- * Initialize Blockly for a readonly iframe. Called on page load.
- * XML argument may be generated from the console with:
- * encodeURIComponent(Blockly.Xml.domToText(Blockly.Xml.workspaceToDom(Blockly.mainWorkspace)).slice(5, -6))
- */
- BSUtils.initReadonly = function() {
- Blockly.inject(document.getElementById('blockly'),
- {path: './',
- readOnly: true,
- rtl: BSUtils.isRtl(),
- scrollbars: false});
- // Add the blocks.
- var xml = BSUtils.getStringParamFromUrl('xml', '');
- xml = Blockly.Xml.textToDom('<xml>' + xml + '</xml>');
- Blockly.Xml.domToWorkspace(xml, Blockly.mainWorkspace);
- };
- /**
- * Load blocks saved on App Engine Storage or in session/local storage.
- * @param {string} defaultXml Text representation of default blocks.
- */
- BSUtils.loadBlocks = function(defaultXml) {
- if (defaultXml) {
- // Load the editor with default starting blocks.
- var xml = Blockly.Xml.textToDom(defaultXml);
- Blockly.Xml.domToWorkspace(xml, Blockly.mainWorkspace);
- } else if ('BlocklyStorage' in window) {
- // Restore saved blocks in a separate thread so that subsequent
- // initialization is not affected from a failed load.
- window.setTimeout(BlocklyStorage.restoreBlocks, 0);
- }
- };
- /**
- * Save the blocks and reload with a different language.
- */
- BSUtils.changeLanguage = function() {
- // Store the blocks for the duration of the reload.
- console.log("in changeLanguage");
- BlocklyStorage.backupBlocks_();
- var newLang = encodeURIComponent($(this).data("lang"));
- console.log("newLang is:",newLang);
- // var newLang = encodeURIComponent(
- // languageMenu.options[languageMenu.selectedIndex].value);
- // console.log("newLang is:",newLang);
- 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;
- };
- /**
- * Is the dialog currently onscreen?
- * @private
- */
- BSUtils.isDialogVisible_ = false;
- /**
- * A closing dialog should animate towards this element.
- * @type Element
- * @private
- */
- BSUtils.dialogOrigin_ = null;
- /**
- * A function to call when a dialog closes.
- * @type Function
- * @private
- */
- BSUtils.dialogDispose_ = null;
- /**
- * Show the dialog pop-up.
- * @param {!Element} content DOM element to display in the dialog.
- * @param {Element} origin Animate the dialog opening/closing from/to this
- * DOM element. If null, don't show any animations for opening or closing.
- * @param {boolean} animate Animate the dialog opening (if origin not null).
- * @param {boolean} modal If true, grey out background and prevent interaction.
- * @param {!Object} style A dictionary of style rules for the dialog.
- * @param {Function} disposeFunc An optional function to call when the dialog
- * closes. Normally used for unhooking events.
- */
- BSUtils.showDialog = function(content, origin, animate, modal, style,
- disposeFunc) {
- if (BSUtils.isDialogVisible_) {
- BSUtils.hideDialog(false);
- }
- BSUtils.isDialogVisible_ = true;
- BSUtils.dialogOrigin_ = origin;
- BSUtils.dialogDispose_ = disposeFunc;
- var dialog = document.getElementById('dialog');
- var shadow = document.getElementById('dialogShadow');
- var border = document.getElementById('dialogBorder');
- // Copy all the specified styles to the dialog.
- for (var name in style) {
- dialog.style[name] = style[name];
- }
- if (modal) {
- shadow.style.visibility = 'visible';
- shadow.style.opacity = 0.3;
- var header = document.createElement('div');
- header.id = 'dialogHeader';
- dialog.appendChild(header);
- BSUtils.dialogMouseDownWrapper_ =
- Blockly.bindEvent_(header, 'mousedown', null,
- BSUtils.dialogMouseDown_);
- }
- dialog.appendChild(content);
- content.className = content.className.replace('dialogHiddenContent', '');
- function endResult() {
- // Check that the dialog wasn't closed during opening.
- if (BSUtils.isDialogVisible_) {
- dialog.style.visibility = 'visible';
- dialog.style.zIndex = 1;
- border.style.visibility = 'hidden';
- }
- }
- if (animate && origin) {
- BSUtils.matchBorder_(origin, false, 0.2);
- BSUtils.matchBorder_(dialog, true, 0.8);
- // In 175ms show the dialog and hide the animated border.
- window.setTimeout(endResult, 175);
- } else {
- // No animation. Just set the final state.
- endResult();
- }
- };
- /**
- * Horizontal start coordinate of dialog drag.
- */
- BSUtils.dialogStartX_ = 0;
- /**
- * Vertical start coordinate of dialog drag.
- */
- BSUtils.dialogStartY_ = 0;
- /**
- * Handle start of drag of dialog.
- * @param {!Event} e Mouse down event.
- * @private
- */
- BSUtils.dialogMouseDown_ = function(e) {
- BSUtils.dialogUnbindDragEvents_();
- if (Blockly.isRightButton(e)) {
- // Right-click.
- return;
- }
- // Left click (or middle click).
- // Record the starting offset between the current location and the mouse.
- var dialog = document.getElementById('dialog');
- BSUtils.dialogStartX_ = dialog.offsetLeft - e.clientX;
- BSUtils.dialogStartY_ = dialog.offsetTop - e.clientY;
- BSUtils.dialogMouseUpWrapper_ = Blockly.bindEvent_(document,
- 'mouseup', null, BSUtils.dialogUnbindDragEvents_);
- BSUtils.dialogMouseMoveWrapper_ = Blockly.bindEvent_(document,
- 'mousemove', null, BSUtils.dialogMouseMove_);
- // This event has been handled. No need to bubble up to the document.
- e.stopPropagation();
- };
- /**
- * Drag the dialog to follow the mouse.
- * @param {!Event} e Mouse move event.
- * @private
- */
- BSUtils.dialogMouseMove_ = function(e) {
- var dialog = document.getElementById('dialog');
- var dialogLeft = BSUtils.dialogStartX_ + e.clientX;
- var dialogTop = BSUtils.dialogStartY_ + e.clientY;
- dialogTop = Math.max(dialogTop, 0);
- dialogTop = Math.min(dialogTop, window.innerHeight - dialog.offsetHeight);
- dialogLeft = Math.max(dialogLeft, 0);
- dialogLeft = Math.min(dialogLeft, window.innerWidth - dialog.offsetWidth);
- dialog.style.left = dialogLeft + 'px';
- dialog.style.top = dialogTop + 'px';
- };
- /**
- * Stop binding to the global mouseup and mousemove events.
- * @private
- */
- BSUtils.dialogUnbindDragEvents_ = function() {
- if (BSUtils.dialogMouseUpWrapper_) {
- Blockly.unbindEvent_(BSUtils.dialogMouseUpWrapper_);
- BSUtils.dialogMouseUpWrapper_ = null;
- }
- if (BSUtils.dialogMouseMoveWrapper_) {
- Blockly.unbindEvent_(BSUtils.dialogMouseMoveWrapper_);
- BSUtils.dialogMouseMoveWrapper_ = null;
- }
- };
- /**
- * Hide the dialog pop-up.
- * @param {boolean} opt_animate Animate the dialog closing. Defaults to true.
- * Requires that origin was not null when dialog was opened.
- */
- BSUtils.hideDialog = function(opt_animate) {
- if (!BSUtils.isDialogVisible_) {
- return;
- }
- BSUtils.dialogUnbindDragEvents_();
- if (BSUtils.dialogMouseDownWrapper_) {
- Blockly.unbindEvent_(BSUtils.dialogMouseDownWrapper_);
- BSUtils.dialogMouseDownWrapper_ = null;
- }
- BSUtils.isDialogVisible_ = false;
- BSUtils.dialogDispose_ && BSUtils.dialogDispose_();
- BSUtils.dialogDispose_ = null;
- var origin = (opt_animate === false) ? null : BSUtils.dialogOrigin_;
- var dialog = document.getElementById('dialog');
- var shadow = document.getElementById('dialogShadow');
- var border = document.getElementById('dialogBorder');
- shadow.style.opacity = 0;
- function endResult() {
- shadow.style.visibility = 'hidden';
- border.style.visibility = 'hidden';
- }
- if (origin) {
- BSUtils.matchBorder_(dialog, false, 0.8);
- BSUtils.matchBorder_(origin, true, 0.2);
- // In 175ms hide both the shadow and the animated border.
- window.setTimeout(endResult, 175);
- } else {
- // No animation. Just set the final state.
- endResult();
- }
- dialog.style.visibility = 'hidden';
- dialog.style.zIndex = -1;
- var header = document.getElementById('dialogHeader');
- if (header) {
- header.parentNode.removeChild(header);
- }
- while (dialog.firstChild) {
- var content = dialog.firstChild;
- content.className += ' dialogHiddenContent';
- document.body.appendChild(content);
- }
- };
- /**
- * Match the animated border to the a element's size and location.
- * @param {!Element} element Element to match.
- * @param {boolean} animate Animate to the new location.
- * @param {number} opacity Opacity of border.
- * @private
- */
- BSUtils.matchBorder_ = function(element, animate, opacity) {
- if (!element) {
- return;
- }
- var border = document.getElementById('dialogBorder');
- var bBox = BSUtils.getBBox_(element);
- function change() {
- border.style.width = bBox.width + 'px';
- border.style.height = bBox.height + 'px';
- border.style.left = bBox.x + 'px';
- border.style.top = bBox.y + 'px';
- border.style.opacity = opacity;
- }
- if (animate) {
- border.className = 'dialogAnimate';
- window.setTimeout(change, 1);
- } else {
- border.className = '';
- change();
- }
- border.style.visibility = 'visible';
- };
- /**
- * Compute the absolute coordinates and dimensions of an HTML or SVG element.
- * @param {!Element} element Element to match.
- * @return {!Object} Contains height, width, x, and y properties.
- * @private
- */
- BSUtils.getBBox_ = function(element) {
- var height = element.offsetHeight;
- var width = element.offsetWidth;
- var x = 0;
- var y = 0;
- do {
- x += element.offsetLeft;
- y += element.offsetTop;
- element = element.offsetParent;
- } while (element);
- return {
- height: height,
- width: width,
- x: x,
- y: y
- };
- };
- /**
- * Display a storage-related modal dialog.
- * @param {string} message Text to alert.
- */
- BSUtils.storageAlert = function(message) {
- var container = document.getElementById('containerStorage');
- container.textContent = '';
- var lines = message.split('\n');
- for (var i = 0; i < lines.length; i++) {
- var p = document.createElement('p');
- p.appendChild(document.createTextNode(lines[i]));
- container.appendChild(p);
- }
- var content = document.getElementById('dialogStorage');
- var origin = document.getElementById('linkButton');
- var style = {
- width: '50%',
- left: '25%',
- top: '5em'
- };
- BSUtils.showDialog(content, origin, true, true, style,
- BSUtils.stopDialogKeyDown());
- BSUtils.startDialogKeyDown();
- };
- /**
- * If the user preses enter, escape, or space, hide the dialog.
- * @param {!Event} e Keyboard event.
- * @private
- */
- BSUtils.dialogKeyDown_ = function(e) {
- if (BSUtils.isDialogVisible_) {
- if (e.keyCode == 13 ||
- e.keyCode == 27 ||
- e.keyCode == 32) {
- BSUtils.hideDialog(true);
- e.stopPropagation();
- e.preventDefault();
- }
- }
- };
- /**
- * Start listening for BSUtils.dialogKeyDown_.
- */
- BSUtils.startDialogKeyDown = function() {
- document.body.addEventListener('keydown',
- BSUtils.dialogKeyDown_, true);
- };
- /**
- * Stop listening for BSUtils.dialogKeyDown_.
- */
- BSUtils.stopDialogKeyDown = function() {
- document.body.removeEventListener('keydown',
- BSUtils.dialogKeyDown_, true);
- };
- /**
- * 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.
- */
- BSUtils.getMsg = function(key) {
- var msg = BSUtils.getMsgOrNull(key);
- return msg === null ? '[Unknown message: ' + key + ']' : msg;
- };
- /**
- * 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 null if the element was not found.
- */
- BSUtils.getMsgOrNull = 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 null;
- }
- };
- /**
- * On touch enabled browsers, add touch-friendly variants of event handlers
- * for elements such as buttons whose event handlers are specified in the
- * markup. For example, ontouchend is treated as equivalent to onclick.
- */
- BSUtils.addTouchEvents = function() {
- // Do nothing if the browser doesn't support touch.
- if (!('ontouchstart' in document.documentElement)) {
- return;
- }
- // Treat ontouchend as equivalent to onclick for buttons.
- var buttons = document.getElementsByTagName('button');
- for (var i = 0, button; button = buttons[i]; i++) {
- if (!button.ontouchend) {
- button.ontouchend = button.onclick;
- }
- }
- };
- // Add events for touch devices when the window is done loading.
- window.addEventListener('load', BSUtils.addTouchEvents, false);
- /**
- * Bind a function to a button's click event.
- * On touch enabled browsers, ontouchend is treated as equivalent to onclick.
- * @param {string} id ID of button element.
- * @param {!Function} func Event handler to bind.
- */
- BSUtils.bindClick = function(el, func) {
- if (typeof el == 'string') {
- el = document.getElementById(el);
- }
- el.addEventListener('click', func, true);
- el.addEventListener('touchend', func, true);
- };
|