├── .gitignore ├── README.md ├── templates ├── panel.html ├── login.html ├── delete-project.html ├── new-project.html ├── project-panel-list.html └── project-link-list.html ├── package.json ├── svg ├── icon-pg.svg ├── icon-progress.svg ├── anim.svg └── icon.svg ├── js ├── strings.js ├── widgets │ ├── bootstrap-alert.js │ ├── bootstrap-popover.js │ └── bootstrap-tooltip.js ├── base64.js ├── jszip.js └── qrcode.js ├── nls ├── strings.js ├── fr │ └── strings.js └── root │ └── strings.js ├── css └── pgb.css ├── eve.js └── main.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | templates/hardcodedlogin.html 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PhoneGap Extension for Brackets 2 | ============================== 3 | 4 | ## Deprecated 5 | 6 | This project is no longer maintained and is in an archived state. If you wish to revive the project, please consider forking it. 7 | 8 | Notices, terms and conditions pertaining to third party software are located at http://www.adobe.com/go/thirdparty/ and incorporated by reference herein. 9 | -------------------------------------------------------------------------------- /templates/panel.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
{{Strings.COMMAND_NAME}}
4 |
5 | × 6 |
7 |
8 |
 
9 |
-------------------------------------------------------------------------------- /templates/login.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 |

5 | 6 |
-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brackets-phonegap", 3 | "description": "PhoneGap extension for Brackets.", 4 | "version": "0.1.1", 5 | "keywords": [".html", ".css", ".js"], 6 | "homepage": "https://github.com/adobe/brackets-phonegap/", 7 | "bugs": "https://github.com/adobe/brackets-phonegap/issues", 8 | "repository" : { 9 | "type" : "git", 10 | "url" : "https://github.com/adobe/brackets-phonegap.git" 11 | }, 12 | "engines": { 13 | "brackets": ">=0.30.0" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /templates/delete-project.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /templates/new-project.html: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /svg/icon-pg.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /templates/project-panel-list.html: -------------------------------------------------------------------------------- 1 | 2 | {{#projects}} 3 | 4 | 5 | 6 | 11 | 12 | 16 | 19 | 20 | {{/projects}} 21 |
icon{{title}} 7 | {{#platforms}} 8 | 9 | {{/platforms}} 10 | 13 | {{Strings.REBUILD_LINK}} 14 | 15 | 17 | {{Strings.DELETE_LINK}} 18 |
-------------------------------------------------------------------------------- /js/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 25 | /*global define */ 26 | 27 | define(function (require, exports, module) { 28 | "use strict"; 29 | 30 | module.exports = require("i18n!nls/strings"); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /nls/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 25 | /*global define */ 26 | 27 | define(function (require, exports, module) { 28 | 29 | 'use strict'; 30 | 31 | module.exports = { 32 | root: true, 33 | "fr": true 34 | }; 35 | }); 36 | -------------------------------------------------------------------------------- /templates/project-link-list.html: -------------------------------------------------------------------------------- 1 | 37 | -------------------------------------------------------------------------------- /js/widgets/bootstrap-alert.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-alert.js v2.3.1 3 | * http://twitter.github.com/bootstrap/javascript.html#alerts 4 | * ========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* ALERT CLASS DEFINITION 27 | * ====================== */ 28 | 29 | var dismiss = '[data-dismiss="alert"]' 30 | , Alert = function (el) { 31 | $(el).on('click', dismiss, this.close) 32 | } 33 | 34 | Alert.prototype.close = function (e) { 35 | var $this = $(this) 36 | , selector = $this.attr('data-target') 37 | , $parent 38 | 39 | if (!selector) { 40 | selector = $this.attr('href') 41 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 42 | } 43 | 44 | $parent = $(selector) 45 | 46 | e && e.preventDefault() 47 | 48 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 49 | 50 | $parent.trigger(e = $.Event('close')) 51 | 52 | if (e.isDefaultPrevented()) return 53 | 54 | $parent.removeClass('in') 55 | 56 | function removeElement() { 57 | $parent 58 | .trigger('closed') 59 | .remove() 60 | } 61 | 62 | $.support.transition && $parent.hasClass('fade') ? 63 | $parent.on($.support.transition.end, removeElement) : 64 | removeElement() 65 | } 66 | 67 | 68 | /* ALERT PLUGIN DEFINITION 69 | * ======================= */ 70 | 71 | var old = $.fn.alert 72 | 73 | $.fn.alert = function (option) { 74 | return this.each(function () { 75 | var $this = $(this) 76 | , data = $this.data('alert') 77 | if (!data) $this.data('alert', (data = new Alert(this))) 78 | if (typeof option == 'string') data[option].call($this) 79 | }) 80 | } 81 | 82 | $.fn.alert.Constructor = Alert 83 | 84 | 85 | /* ALERT NO CONFLICT 86 | * ================= */ 87 | 88 | $.fn.alert.noConflict = function () { 89 | $.fn.alert = old 90 | return this 91 | } 92 | 93 | 94 | /* ALERT DATA-API 95 | * ============== */ 96 | 97 | $(document).on('click.alert.data-api', dismiss, Alert.prototype.close) 98 | 99 | }(window.jQuery); -------------------------------------------------------------------------------- /svg/icon-progress.svg: -------------------------------------------------------------------------------- 1 | 22 | 25 | 26 | 32 | 33 | 35 | 36 | 39 | 42 | 43 | 44 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /nls/fr/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 25 | /*global define */ 26 | 27 | define({ 28 | "MENU_NAME": "PhoneGap", 29 | "COMMAND_NAME": "PhoneGap Build", 30 | "USERNAME_PLACEHOLDER": "Adresse électronique/ID Adobe", 31 | "PASSWORD_PLACEHOLDER": "Mot de passe", 32 | "LOGIN_BUTTON_LABEL": "Se connecter", 33 | "LOGIN_FAILED_DIALOG_TITLE": "Echec de la connexion", 34 | "LOGIN_FAILED_DIALOG_MESSAGE": "Votre nom d’utilisateur ou mot de passe n’ont pas été reconnus.", 35 | "LOGIN_SUCCESS_MESSAGE": "Vos projets PhoneGap sont répertoriés ci-dessous. Pour associer votre code à un projet existant, ouvrez le menu PhoneGap, puis choisissez : ", 36 | "OPEN_PANEL_MENU_ENTRY": "Ouvrir le panneau PhoneGap Build", 37 | "LINK_PROJECT_MENU_ITEM": "Associer à un projet PhoneGap Build", 38 | "UNLINK_OPTION": "Aucun (Dissocier projet)", 39 | "LINK_DIALOG_TITLE": "Projets PhoneGap Build", 40 | "LINK_DIALOG_INSTRUCTIONS": "Sélectionnez le projet PhoneGap Build à associer à ce répertoire.", 41 | "LINK_SUCCESSFUL_MESSAGE": "Projet associé avec succès. Pour télécharger votre code pour le service PhoneGap Build, choisissez PhoneGap → ", 42 | "PROJECT_NOT_LINKED_MESSAGE": "Vous devez d’abord associer votre dossier à un projet PhoneGap Build. Ouvrez le menu PhoneGap et sélectionnez : ", 43 | "SEND_FILES_MENU_ENTRY": "Transférer des fichiers vers PhoneGap Build", 44 | "UPLOAD_CONFIRMATION_MESSAGE": "Voulez-vous vraiment remplacer vos fichiers du serveur par les fichiers de ce répertoire ?", 45 | "REBUILD_LINK": "Regénérer", 46 | "REBUILDING_MESSAGE": "Regénération", 47 | "REBUILDING_SUCCESS_MESSAGE": "Votre projet est en cours de regénération. Cliquez sur le nom du projet pour ouvrir la page de l’application dans votre navigateur.", 48 | "REBUILT_SUCCESS_MESSAGE": "La regénération de votre projet est terminée.", 49 | "NEW_PROJECT_OPTION": "Créer un projet PhoneGap Build", 50 | "NEW_DIALOG_TITLE": "Nouveau projet PhoneGap Build", 51 | "NEW_DIALOG_MESSAGE": "Créez un projet PhoneGap Build à partir du dossier en cours.", 52 | "NEW_DIALOG_APP_NAME": "Nom de l’application", 53 | "NEW_ALERT_MESSAGE": "Nouveau projet PhoneGap Build créé sous le nom", 54 | "DELETE_LINK": "Supprimer", 55 | "DELETE_CONFIRMATION_TITLE" : "Supprimer PhoneGap Build Project", 56 | "DELETE_CONFIRMATION_MESSAGE": "Voulez-vous vraiment supprimer ce projet ?" 57 | }); 58 | -------------------------------------------------------------------------------- /js/widgets/bootstrap-popover.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * bootstrap-popover.js v2.3.1 3 | * http://twitter.github.com/bootstrap/javascript.html#popovers 4 | * =========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * =========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* POPOVER PUBLIC CLASS DEFINITION 27 | * =============================== */ 28 | 29 | var Popover = function (element, options) { 30 | this.init('popover', element, options) 31 | } 32 | 33 | 34 | /* NOTE: POPOVER EXTENDS BOOTSTRAP-TOOLTIP.js 35 | ========================================== */ 36 | 37 | Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype, { 38 | 39 | constructor: Popover 40 | 41 | , setContent: function () { 42 | var $tip = this.tip() 43 | , title = this.getTitle() 44 | , content = this.getContent() 45 | 46 | $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) 47 | $tip.find('.popover-content')[this.options.html ? 'html' : 'text'](content) 48 | 49 | $tip.removeClass('fade top bottom left right in') 50 | } 51 | 52 | , hasContent: function () { 53 | return this.getTitle() || this.getContent() 54 | } 55 | 56 | , getContent: function () { 57 | var content 58 | , $e = this.$element 59 | , o = this.options 60 | 61 | content = (typeof o.content == 'function' ? o.content.call($e[0]) : o.content) 62 | || $e.attr('data-content') 63 | 64 | return content 65 | } 66 | 67 | , tip: function () { 68 | if (!this.$tip) { 69 | this.$tip = $(this.options.template) 70 | } 71 | return this.$tip 72 | } 73 | 74 | , destroy: function () { 75 | this.hide().$element.off('.' + this.type).removeData(this.type) 76 | } 77 | 78 | }) 79 | 80 | 81 | /* POPOVER PLUGIN DEFINITION 82 | * ======================= */ 83 | 84 | var old = $.fn.popover 85 | 86 | $.fn.popover = function (option) { 87 | return this.each(function () { 88 | var $this = $(this) 89 | , data = $this.data('popover') 90 | , options = typeof option == 'object' && option 91 | if (!data) $this.data('popover', (data = new Popover(this, options))) 92 | if (typeof option == 'string') data[option]() 93 | }) 94 | } 95 | 96 | $.fn.popover.Constructor = Popover 97 | 98 | $.fn.popover.defaults = $.extend({} , $.fn.tooltip.defaults, { 99 | placement: 'right' 100 | , trigger: 'click' 101 | , content: '' 102 | , template: '

' 103 | }) 104 | 105 | 106 | /* POPOVER NO CONFLICT 107 | * =================== */ 108 | 109 | $.fn.popover.noConflict = function () { 110 | $.fn.popover = old 111 | return this 112 | } 113 | 114 | }(window.jQuery); 115 | -------------------------------------------------------------------------------- /nls/root/strings.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, indent: 4, maxerr: 50 */ 25 | /*global define */ 26 | 27 | define({ 28 | "MENU_NAME" : "PhoneGap", 29 | "COMMAND_NAME" : "PhoneGap Build", 30 | "USERNAME_PLACEHOLDER" : "Email/AdobeId", 31 | "PASSWORD_PLACEHOLDER" : "Password", 32 | "LOGIN_BUTTON_LABEL" : "Login", 33 | "LOGIN_FAILED_DIALOG_TITLE" : "Login Failed", 34 | "LOGIN_FAILED_DIALOG_MESSAGE" : "Your username or password were not recognized.", 35 | "LOGIN_SUCCESS_MESSAGE" : "Your existing PhoneGap Build projects are listed below. To associate your code with an existing project, open the PhoneGap menu and select: ", 36 | "OPEN_PANEL_MENU_ENTRY" : "Open PhoneGap Build Panel", 37 | "LINK_PROJECT_MENU_ITEM" : "Link with PhoneGap Build Project", 38 | "UNLINK_OPTION" : "None (unlink project)", 39 | "LINK_DIALOG_TITLE" : "PhoneGap Build Projects", 40 | "LINK_DIALOG_INSTRUCTIONS" : "Select which PhoneGap Build project you would like to link to this directory.", 41 | "LINK_SUCCESSFUL_MESSAGE" : "Project successfully linked. To push your code to the PhoneGap Build service, choose PhoneGap → ", 42 | "PROJECT_NOT_LINKED_MESSAGE" : "You must first link your folder to an existing PhoneGap Build project. Open the PhoneGap menu and select: ", 43 | "SEND_FILES_MENU_ENTRY" : "Send Files to PhoneGap Build", 44 | "UPLOAD_CONFIRMATION_MESSAGE" : "Are you sure you want to overwrite your files on the server with the files from this directory?", 45 | "REBUILD_LINK" : "Rebuild", 46 | "REBUILDING_MESSAGE" : "Rebuilding", 47 | "REBUILDING_SUCCESS_MESSAGE" : "Your project is rebuilding. Click on the project name to open the application page in your browser.", 48 | "REBUILT_SUCCESS_MESSAGE" : "Your project is finished building.", 49 | "NEW_PROJECT_OPTION" : "Create new PhoneGap Build project", 50 | "NEW_DIALOG_TITLE" : "New PhoneGap Build Project", 51 | "NEW_DIALOG_MESSAGE" : "Create a new PhoneGap Build project based on the current folder.", 52 | "NEW_DIALOG_APP_NAME" : "App Name", 53 | "NEW_ALERT_MESSAGE" : "Created a new PhoneGap Build project named", 54 | "DELETE_LINK" : "Delete", 55 | "DELETE_CONFIRMATION_TITLE" : "Delete PhoneGap Build Project", 56 | "DELETE_CONFIRMATION_MESSAGE" : "Are you sure you want to delete this project?" 57 | }); -------------------------------------------------------------------------------- /svg/anim.svg: -------------------------------------------------------------------------------- 1 | 22 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 71 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /css/pgb.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | #pgb-btn:link { 25 | opacity: .5; 26 | } 27 | #pgb-btn:hover { 28 | opacity: .75; 29 | } 30 | #pgb-btn { 31 | height: 20px; 32 | width: 20px; 33 | margin: 0 8px -5px 0; 34 | display: inline-block; 35 | } 36 | #pgb-btn.normal { 37 | background-image: url("../svg/icon.svg"); 38 | background-position: 0 0; 39 | } 40 | #pgb-btn.disabled { 41 | background-image: url("../svg/icon.svg"); 42 | background-position: 0 -40px; 43 | } 44 | #pgb-btn.error { 45 | background-image: url("../svg/icon.svg"); 46 | background-position: 0 -20px; 47 | } 48 | #pgb-btn.progress { 49 | background-image: url("../svg/icon-progress.svg"); 50 | background-position: 0 0; 51 | } 52 | #pgb-panel span.icon { 53 | padding: 4px 0 0 20px; 54 | margin: 0 2px; 55 | background-image: url("../svg/icon.svg"); 56 | } 57 | #pgb-panel span.icon.ios-complete { 58 | background-position: -20px 0; 59 | } 60 | #pgb-panel span.icon.ios-null { 61 | background-position: -40px 0; 62 | } 63 | #pgb-panel span.icon.ios-error { 64 | background-position: -60px 0; 65 | } 66 | #pgb-panel span.icon.ios-pending { 67 | background-position: -80px 0; 68 | } 69 | #pgb-panel span.icon.android-complete { 70 | background-position: -20px -20px; 71 | } 72 | #pgb-panel span.icon.android-null { 73 | background-position: -40px -20px; 74 | } 75 | #pgb-panel span.icon.android-error { 76 | background-position: -60px -20px; 77 | } 78 | #pgb-panel span.icon.android-pending { 79 | background-position: -80px -20px; 80 | } 81 | #pgb-panel span.icon.winphone-complete { 82 | background-position: -20px -40px; 83 | } 84 | #pgb-panel span.icon.winphone-null { 85 | background-position: -40px -40px; 86 | } 87 | #pgb-panel span.icon.winphone-error { 88 | background-position: -60px -40px; 89 | } 90 | #pgb-panel span.icon.winphone-pending { 91 | background-position: -80px -40px; 92 | } 93 | #pgb-panel span.icon.blackberry-complete { 94 | background-position: -20px -60px; 95 | } 96 | #pgb-panel span.icon.blackberry-null { 97 | background-position: -40px -60px; 98 | } 99 | #pgb-panel span.icon.blackberry-error { 100 | background-position: -60px -60px; 101 | } 102 | #pgb-panel span.icon.blackberry-pending { 103 | background-position: -80px -60px; 104 | } 105 | #pgb-panel span.icon.webos-complete { 106 | background-position: -20px -80px; 107 | } 108 | #pgb-panel span.icon.webos-null { 109 | background-position: -40px -80px; 110 | } 111 | #pgb-panel span.icon.webos-error { 112 | background-position: -60px -80px; 113 | } 114 | #pgb-panel span.icon.webos-pending { 115 | background-position: -80px -80px; 116 | } 117 | #pgb-panel span.icon.symbian-complete { 118 | background-position: -20px -100px; 119 | } 120 | #pgb-panel span.icon.symbian-null { 121 | background-position: -40px -100px; 122 | } 123 | #pgb-panel span.icon.symbian-error { 124 | background-position: -60px -100px; 125 | } 126 | #pgb-panel span.icon.symbian-pending { 127 | background-position: -80px -100px; 128 | } 129 | #pgb-panel td { 130 | white-space: nowrap; 131 | text-overflow: ellipsis; 132 | } 133 | #pgb-panel { 134 | position: relative; 135 | } 136 | 137 | #pgb-anim { 138 | background: rgba(0, 0, 0, .75) url("../svg/anim.svg") no-repeat 50% 50%; 139 | display: none; 140 | width: 100%; 141 | height: 100%; 142 | position: absolute; 143 | top: 0; 144 | left: 0; 145 | } 146 | #pgb-link-container { 147 | width: 100%; 148 | height: 300px; 149 | text-align: center; 150 | overflow: scroll; 151 | } 152 | #pgb-link-container span.project-title { 153 | font-weight: bold; 154 | display: block; 155 | } 156 | 157 | #pgb-link-dialog-instructions { 158 | text-align: left; 159 | font-size: 1.1em; 160 | } 161 | 162 | #pgb-password { 163 | position: relative; 164 | } 165 | 166 | .pgb-project-label{ 167 | width: 100%; 168 | text-align: left; 169 | padding-top: 0; 170 | } 171 | 172 | .alert-message.pgb { 173 | text-align: center; 174 | } 175 | .alert-message a.pgb { 176 | margin: 5px; 177 | } 178 | 179 | .alert-message p { 180 | font-size: 1.2em; 181 | } 182 | 183 | .pgb-upload-progress { 184 | visibility: hidden; 185 | } 186 | .btn-mini{ 187 | font-size: 10px; 188 | padding: 3px 5px; 189 | } 190 | 191 | #pgb-btn-holder { 192 | text-align:center; 193 | padding-top: 2px; 194 | padding-left: 5px; 195 | } 196 | 197 | #pgb-btn-holder + .popover{ 198 | width: 900px; 199 | background-color: #FFF6E0; 200 | } 201 | 202 | #pgb-btn-holder + .popover .arrow{ 203 | margin-top: -30px; 204 | background-color: #FFF6E0; 205 | 206 | } 207 | 208 | #pgb-panel-project-list-table img{ 209 | height: 20px; 210 | width: 20px; 211 | } -------------------------------------------------------------------------------- /js/base64.js: -------------------------------------------------------------------------------- 1 | define(function (require, exports, module) { 2 | 3 | var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", 4 | charCode = String.fromCharCode; 5 | 6 | // public method for encoding 7 | exports.encode = function (input) { 8 | var output = "", 9 | chr1, chr2, chr3, enc1, enc2, enc3, enc4, 10 | i = 0; 11 | 12 | input = utf8_encode(input); 13 | 14 | while (i < input.length) { 15 | 16 | chr1 = input.charCodeAt(i++); 17 | chr2 = input.charCodeAt(i++); 18 | chr3 = input.charCodeAt(i++); 19 | 20 | enc1 = chr1 >> 2; 21 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 22 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 23 | enc4 = chr3 & 63; 24 | 25 | if (isNaN(chr2)) { 26 | enc3 = enc4 = 64; 27 | } else if (isNaN(chr3)) { 28 | enc4 = 64; 29 | } 30 | 31 | output = output + 32 | keyStr.charAt(enc1) + keyStr.charAt(enc2) + 33 | keyStr.charAt(enc3) + keyStr.charAt(enc4); 34 | 35 | } 36 | 37 | return output; 38 | }; 39 | 40 | 41 | exports.encodeBinary = function (input) { 42 | var output = "", 43 | bytebuffer, 44 | encodedCharIndexes = [], 45 | inx = 0, 46 | paddingBytes = 0; 47 | 48 | while (inx < input.length) { 49 | // Fill byte buffer array 50 | bytebuffer = new Array(3); 51 | for (jnx = 0; jnx < bytebuffer.length; jnx++) 52 | if (inx < input.length) 53 | bytebuffer[jnx] = input.charCodeAt(inx++) & 0xff; // throw away high-order byte, as documented at: https://developer.mozilla.org/En/Using_XMLHttpRequest#Handling_binary_data 54 | else 55 | bytebuffer[jnx] = 0; 56 | 57 | // Get each encoded character, 6 bits at a time 58 | // index 1: first 6 bits 59 | encodedCharIndexes[0] = bytebuffer[0] >> 2; 60 | // index 2: second 6 bits (2 least significant bits from input byte 1 + 4 most significant bits from byte 2) 61 | encodedCharIndexes[1] = ((bytebuffer[0] & 0x3) << 4) | (bytebuffer[1] >> 4); 62 | // index 3: third 6 bits (4 least significant bits from input byte 2 + 2 most significant bits from byte 3) 63 | encodedCharIndexes[2] = ((bytebuffer[1] & 0x0f) << 2) | (bytebuffer[2] >> 6); 64 | // index 3: forth 6 bits (6 least significant bits from input byte 3) 65 | encodedCharIndexes[3] = bytebuffer[2] & 0x3f; 66 | 67 | // Determine whether padding happened, and adjust accordingly 68 | paddingBytes = inx - (input.length - 1); 69 | switch (paddingBytes) { 70 | case 2: 71 | // Set last 2 characters to padding char 72 | encodedCharIndexes[3] = 64; 73 | encodedCharIndexes[2] = 64; 74 | break; 75 | case 1: 76 | // Set last character to padding char 77 | encodedCharIndexes[3] = 64; 78 | break; 79 | default: 80 | break; // No padding - proceed 81 | } 82 | // Now we will grab each appropriate character out of our keystring 83 | // based on our index array and append it to the output string 84 | for (jnx = 0; jnx < encodedCharIndexes.length; jnx++) { 85 | output += keyStr.charAt(encodedCharIndexes[jnx]); 86 | } 87 | } 88 | return output; 89 | }; 90 | 91 | // public method for decoding 92 | exports.decode = function (input) { 93 | var output = "", 94 | chr1, chr2, chr3, 95 | enc1, enc2, enc3, enc4, 96 | i = 0; 97 | 98 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 99 | 100 | while (i < input.length) { 101 | 102 | enc1 = keyStr.indexOf(input.charAt(i++)); 103 | enc2 = keyStr.indexOf(input.charAt(i++)); 104 | enc3 = keyStr.indexOf(input.charAt(i++)); 105 | enc4 = keyStr.indexOf(input.charAt(i++)); 106 | 107 | chr1 = (enc1 << 2) | (enc2 >> 4); 108 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 109 | chr3 = ((enc3 & 3) << 6) | enc4; 110 | 111 | output = output + charCode(chr1); 112 | 113 | if (enc3 != 64) { 114 | output = output + charCode(chr2); 115 | } 116 | if (enc4 != 64) { 117 | output = output + charCode(chr3); 118 | } 119 | 120 | } 121 | 122 | output = utf8_decode(output); 123 | 124 | return output; 125 | 126 | }; 127 | 128 | // private method for UTF-8 encoding 129 | var utf8_encode = function (string) { 130 | string = string.replace(/\r\n/g,"\n"); 131 | var utftext = ""; 132 | 133 | for (var n = 0; n < string.length; n++) { 134 | 135 | var c = string.charCodeAt(n); 136 | 137 | if (c < 128) { 138 | utftext += charCode(c); 139 | } 140 | else if((c > 127) && (c < 2048)) { 141 | utftext += charCode((c >> 6) | 192); 142 | utftext += charCode((c & 63) | 128); 143 | } 144 | else { 145 | utftext += charCode((c >> 12) | 224); 146 | utftext += charCode(((c >> 6) & 63) | 128); 147 | utftext += charCode((c & 63) | 128); 148 | } 149 | 150 | } 151 | 152 | return utftext; 153 | }; 154 | 155 | // private method for UTF-8 decoding 156 | var utf8_decode = function (utftext) { 157 | var string = "", 158 | i = 0, 159 | c1, c2, c3, 160 | c = c1 = c2 = 0; 161 | 162 | while ( i < utftext.length ) { 163 | 164 | c = utftext.charCodeAt(i); 165 | 166 | if (c < 128) { 167 | string += charCode(c); 168 | i++; 169 | } 170 | else if((c > 191) && (c < 224)) { 171 | c2 = utftext.charCodeAt(i+1); 172 | string += charCode(((c & 31) << 6) | (c2 & 63)); 173 | i += 2; 174 | } 175 | else { 176 | c2 = utftext.charCodeAt(i+1); 177 | c3 = utftext.charCodeAt(i+2); 178 | string += charCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 179 | i += 3; 180 | } 181 | 182 | } 183 | 184 | return string; 185 | }; 186 | 187 | }); -------------------------------------------------------------------------------- /svg/icon.svg: -------------------------------------------------------------------------------- 1 | 22 | 25 | 26 | 33 | 47 | 65 | 79 | 92 | 116 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /js/widgets/bootstrap-tooltip.js: -------------------------------------------------------------------------------- 1 | /* =========================================================== 2 | * bootstrap-tooltip.js v2.3.1 3 | * http://twitter.github.com/bootstrap/javascript.html#tooltips 4 | * Inspired by the original jQuery.tipsy by Jason Frame 5 | * =========================================================== 6 | * Copyright 2012 Twitter, Inc. 7 | * 8 | * Licensed under the Apache License, Version 2.0 (the "License"); 9 | * you may not use this file except in compliance with the License. 10 | * You may obtain a copy of the License at 11 | * 12 | * http://www.apache.org/licenses/LICENSE-2.0 13 | * 14 | * Unless required by applicable law or agreed to in writing, software 15 | * distributed under the License is distributed on an "AS IS" BASIS, 16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | * See the License for the specific language governing permissions and 18 | * limitations under the License. 19 | * ========================================================== */ 20 | 21 | 22 | !function ($) { 23 | 24 | "use strict"; // jshint ;_; 25 | 26 | 27 | /* TOOLTIP PUBLIC CLASS DEFINITION 28 | * =============================== */ 29 | 30 | var Tooltip = function (element, options) { 31 | this.init('tooltip', element, options) 32 | } 33 | 34 | Tooltip.prototype = { 35 | 36 | constructor: Tooltip 37 | 38 | , init: function (type, element, options) { 39 | var eventIn 40 | , eventOut 41 | , triggers 42 | , trigger 43 | , i 44 | 45 | this.type = type 46 | this.$element = $(element) 47 | this.options = this.getOptions(options) 48 | this.enabled = true 49 | 50 | triggers = this.options.trigger.split(' ') 51 | 52 | for (i = triggers.length; i--;) { 53 | trigger = triggers[i] 54 | if (trigger == 'click') { 55 | this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) 56 | } else if (trigger != 'manual') { 57 | eventIn = trigger == 'hover' ? 'mouseenter' : 'focus' 58 | eventOut = trigger == 'hover' ? 'mouseleave' : 'blur' 59 | this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) 60 | this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) 61 | } 62 | } 63 | 64 | this.options.selector ? 65 | (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : 66 | this.fixTitle() 67 | } 68 | 69 | , getOptions: function (options) { 70 | options = $.extend({}, $.fn[this.type].defaults, this.$element.data(), options) 71 | 72 | if (options.delay && typeof options.delay == 'number') { 73 | options.delay = { 74 | show: options.delay 75 | , hide: options.delay 76 | } 77 | } 78 | 79 | return options 80 | } 81 | 82 | , enter: function (e) { 83 | var defaults = $.fn[this.type].defaults 84 | , options = {} 85 | , self 86 | 87 | this._options && $.each(this._options, function (key, value) { 88 | if (defaults[key] != value) options[key] = value 89 | }, this) 90 | 91 | self = $(e.currentTarget)[this.type](options).data(this.type) 92 | 93 | if (!self.options.delay || !self.options.delay.show) return self.show() 94 | 95 | clearTimeout(this.timeout) 96 | self.hoverState = 'in' 97 | this.timeout = setTimeout(function() { 98 | if (self.hoverState == 'in') self.show() 99 | }, self.options.delay.show) 100 | } 101 | 102 | , leave: function (e) { 103 | var self = $(e.currentTarget)[this.type](this._options).data(this.type) 104 | 105 | if (this.timeout) clearTimeout(this.timeout) 106 | if (!self.options.delay || !self.options.delay.hide) return self.hide() 107 | 108 | self.hoverState = 'out' 109 | this.timeout = setTimeout(function() { 110 | if (self.hoverState == 'out') self.hide() 111 | }, self.options.delay.hide) 112 | } 113 | 114 | , show: function () { 115 | var $tip 116 | , pos 117 | , actualWidth 118 | , actualHeight 119 | , placement 120 | , tp 121 | , e = $.Event('show') 122 | 123 | if (this.hasContent() && this.enabled) { 124 | this.$element.trigger(e) 125 | if (e.isDefaultPrevented()) return 126 | $tip = this.tip() 127 | this.setContent() 128 | 129 | if (this.options.animation) { 130 | $tip.addClass('fade') 131 | } 132 | 133 | placement = typeof this.options.placement == 'function' ? 134 | this.options.placement.call(this, $tip[0], this.$element[0]) : 135 | this.options.placement 136 | 137 | $tip 138 | .detach() 139 | .css({ top: 0, left: 0, display: 'block' }) 140 | 141 | this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) 142 | 143 | pos = this.getPosition() 144 | 145 | actualWidth = $tip[0].offsetWidth 146 | actualHeight = $tip[0].offsetHeight 147 | 148 | switch (placement) { 149 | case 'bottom': 150 | tp = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2} 151 | break 152 | case 'top': 153 | tp = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2} 154 | break 155 | case 'left': 156 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth} 157 | break 158 | case 'right': 159 | tp = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width} 160 | break 161 | } 162 | 163 | this.applyPlacement(tp, placement) 164 | this.$element.trigger('shown') 165 | } 166 | } 167 | 168 | , applyPlacement: function(offset, placement){ 169 | var $tip = this.tip() 170 | , width = $tip[0].offsetWidth 171 | , height = $tip[0].offsetHeight 172 | , actualWidth 173 | , actualHeight 174 | , delta 175 | , replace 176 | 177 | $tip 178 | .offset(offset) 179 | .addClass(placement) 180 | .addClass('in') 181 | 182 | actualWidth = $tip[0].offsetWidth 183 | actualHeight = $tip[0].offsetHeight 184 | 185 | if (placement == 'top' && actualHeight != height) { 186 | offset.top = offset.top + height - actualHeight 187 | replace = true 188 | } 189 | 190 | if (placement == 'bottom' || placement == 'top') { 191 | delta = 0 192 | 193 | if (offset.left < 0){ 194 | delta = offset.left * -2 195 | offset.left = 0 196 | $tip.offset(offset) 197 | actualWidth = $tip[0].offsetWidth 198 | actualHeight = $tip[0].offsetHeight 199 | } 200 | 201 | this.replaceArrow(delta - width + actualWidth, actualWidth, 'left') 202 | } else { 203 | this.replaceArrow(actualHeight - height, actualHeight, 'top') 204 | } 205 | 206 | if (replace) $tip.offset(offset) 207 | } 208 | 209 | , replaceArrow: function(delta, dimension, position){ 210 | this 211 | .arrow() 212 | .css(position, delta ? (50 * (1 - delta / dimension) + "%") : '') 213 | } 214 | 215 | , setContent: function () { 216 | var $tip = this.tip() 217 | , title = this.getTitle() 218 | 219 | $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) 220 | $tip.removeClass('fade in top bottom left right') 221 | } 222 | 223 | , hide: function () { 224 | var that = this 225 | , $tip = this.tip() 226 | , e = $.Event('hide') 227 | 228 | this.$element.trigger(e) 229 | if (e.isDefaultPrevented()) return 230 | 231 | $tip.removeClass('in') 232 | 233 | function removeWithAnimation() { 234 | var timeout = setTimeout(function () { 235 | $tip.off($.support.transition.end).detach() 236 | }, 500) 237 | 238 | $tip.one($.support.transition.end, function () { 239 | clearTimeout(timeout) 240 | $tip.detach() 241 | }) 242 | } 243 | 244 | $.support.transition && this.$tip.hasClass('fade') ? 245 | removeWithAnimation() : 246 | $tip.detach() 247 | 248 | this.$element.trigger('hidden') 249 | 250 | return this 251 | } 252 | 253 | , fixTitle: function () { 254 | var $e = this.$element 255 | if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') { 256 | $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') 257 | } 258 | } 259 | 260 | , hasContent: function () { 261 | return this.getTitle() 262 | } 263 | 264 | , getPosition: function () { 265 | var el = this.$element[0] 266 | return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : { 267 | width: el.offsetWidth 268 | , height: el.offsetHeight 269 | }, this.$element.offset()) 270 | } 271 | 272 | , getTitle: function () { 273 | var title 274 | , $e = this.$element 275 | , o = this.options 276 | 277 | title = $e.attr('data-original-title') 278 | || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) 279 | 280 | return title 281 | } 282 | 283 | , tip: function () { 284 | return this.$tip = this.$tip || $(this.options.template) 285 | } 286 | 287 | , arrow: function(){ 288 | return this.$arrow = this.$arrow || this.tip().find(".tooltip-arrow") 289 | } 290 | 291 | , validate: function () { 292 | if (!this.$element[0].parentNode) { 293 | this.hide() 294 | this.$element = null 295 | this.options = null 296 | } 297 | } 298 | 299 | , enable: function () { 300 | this.enabled = true 301 | } 302 | 303 | , disable: function () { 304 | this.enabled = false 305 | } 306 | 307 | , toggleEnabled: function () { 308 | this.enabled = !this.enabled 309 | } 310 | 311 | , toggle: function (e) { 312 | var self = e ? $(e.currentTarget)[this.type](this._options).data(this.type) : this 313 | self.tip().hasClass('in') ? self.hide() : self.show() 314 | } 315 | 316 | , destroy: function () { 317 | this.hide().$element.off('.' + this.type).removeData(this.type) 318 | } 319 | 320 | } 321 | 322 | 323 | /* TOOLTIP PLUGIN DEFINITION 324 | * ========================= */ 325 | 326 | var old = $.fn.tooltip 327 | 328 | $.fn.tooltip = function ( option ) { 329 | return this.each(function () { 330 | var $this = $(this) 331 | , data = $this.data('tooltip') 332 | , options = typeof option == 'object' && option 333 | if (!data) $this.data('tooltip', (data = new Tooltip(this, options))) 334 | if (typeof option == 'string') data[option]() 335 | }) 336 | } 337 | 338 | $.fn.tooltip.Constructor = Tooltip 339 | 340 | $.fn.tooltip.defaults = { 341 | animation: true 342 | , placement: 'top' 343 | , selector: false 344 | , template: '
' 345 | , trigger: 'hover focus' 346 | , title: '' 347 | , delay: 0 348 | , html: false 349 | , container: false 350 | } 351 | 352 | 353 | /* TOOLTIP NO CONFLICT 354 | * =================== */ 355 | 356 | $.fn.tooltip.noConflict = function () { 357 | $.fn.tooltip = old 358 | return this 359 | } 360 | 361 | }(window.jQuery); 362 | -------------------------------------------------------------------------------- /eve.js: -------------------------------------------------------------------------------- 1 | // ┌──────────────────────────────────────────────────────────────────────────────────────┐ \\ 2 | // │ Eve 0.3.5 - JavaScript Events Library │ \\ 3 | // ├──────────────────────────────────────────────────────────────────────────────────────┤ \\ 4 | // │ Copyright (c) 2008-2012 Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\ 5 | // │ Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license. │ \\ 6 | // └──────────────────────────────────────────────────────────────────────────────────────┘ \\ 7 | 8 | (function (glob) { 9 | var version = "0.3.5", 10 | has = "hasOwnProperty", 11 | separator = /[\.\/]/, 12 | wildcard = "*", 13 | fun = function () {}, 14 | numsort = function (a, b) { 15 | return a - b; 16 | }, 17 | current_event, 18 | stop, 19 | events = {n: {}}, 20 | /*\ 21 | * eve 22 | [ method ] 23 | 24 | * Fires event with given `name`, given scope and other parameters. 25 | 26 | > Arguments 27 | 28 | - name (string) name of the *event*, dot (`.`) or slash (`/`) separated 29 | - scope (object) context for the event handlers 30 | - varargs (...) the rest of arguments will be sent to event handlers 31 | 32 | = (object) array of returned values from the listeners 33 | \*/ 34 | eve = function (name, scope) { 35 | var e = events, 36 | oldstop = stop, 37 | args = Array.prototype.slice.call(arguments, 2), 38 | listeners = eve.listeners(name), 39 | z = 0, 40 | f = false, 41 | l, 42 | indexed = [], 43 | queue = {}, 44 | out = [], 45 | ce = current_event, 46 | errors = []; 47 | current_event = name; 48 | stop = 0; 49 | for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) { 50 | indexed.push(listeners[i].zIndex); 51 | if (listeners[i].zIndex < 0) { 52 | queue[listeners[i].zIndex] = listeners[i]; 53 | } 54 | } 55 | indexed.sort(numsort); 56 | while (indexed[z] < 0) { 57 | l = queue[indexed[z++]]; 58 | out.push(l.apply(scope, args)); 59 | if (stop) { 60 | stop = oldstop; 61 | return out; 62 | } 63 | } 64 | for (i = 0; i < ii; i++) { 65 | l = listeners[i]; 66 | if ("zIndex" in l) { 67 | if (l.zIndex == indexed[z]) { 68 | out.push(l.apply(scope, args)); 69 | if (stop) { 70 | break; 71 | } 72 | do { 73 | z++; 74 | l = queue[indexed[z]]; 75 | l && out.push(l.apply(scope, args)); 76 | if (stop) { 77 | break; 78 | } 79 | } while (l) 80 | } else { 81 | queue[l.zIndex] = l; 82 | } 83 | } else { 84 | out.push(l.apply(scope, args)); 85 | if (stop) { 86 | break; 87 | } 88 | } 89 | } 90 | stop = oldstop; 91 | current_event = ce; 92 | return out.length ? out : null; 93 | }; 94 | // Undocumented. Debug only. 95 | eve._events = events; 96 | /*\ 97 | * eve.listeners 98 | [ method ] 99 | ** 100 | * Internal method which gives you array of all event handlers that will be triggered by the given `name`. 101 | ** 102 | > Arguments 103 | ** 104 | - name (string) name of the event, dot (`.`) or slash (`/`) separated 105 | ** 106 | = (array) array of event handlers 107 | \*/ 108 | eve.listeners = function (name) { 109 | var names = name.split(separator), 110 | e = events, 111 | item, 112 | items, 113 | k, 114 | i, 115 | ii, 116 | j, 117 | jj, 118 | nes, 119 | es = [e], 120 | out = []; 121 | for (i = 0, ii = names.length; i < ii; i++) { 122 | nes = []; 123 | for (j = 0, jj = es.length; j < jj; j++) { 124 | e = es[j].n; 125 | items = [e[names[i]], e[wildcard]]; 126 | k = 2; 127 | while (k--) { 128 | item = items[k]; 129 | if (item) { 130 | nes.push(item); 131 | out = out.concat(item.f || []); 132 | } 133 | } 134 | } 135 | es = nes; 136 | } 137 | return out; 138 | }; 139 | 140 | /*\ 141 | * eve.on 142 | [ method ] 143 | ** 144 | * Binds given event handler with a given name. You can use wildcards “`*`” for the names: 145 | | eve.on("*.under.*", f); 146 | | eve("mouse.under.floor"); // triggers f 147 | * Use @eve to trigger the listener. 148 | ** 149 | > Arguments 150 | ** 151 | - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards 152 | - f (function) event handler function 153 | ** 154 | = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment. 155 | > Example: 156 | | eve.on("mouse", eatIt)(2); 157 | | eve.on("mouse", scream); 158 | | eve.on("mouse", catchIt)(1); 159 | * This will ensure that `catchIt()` function will be called before `eatIt()`. 160 | * 161 | * If you want to put your handler before non-indexed handlers, specify a negative value. 162 | * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”. 163 | \*/ 164 | eve.on = function (name, f) { 165 | var names = name.split(separator), 166 | e = events; 167 | for (var i = 0, ii = names.length; i < ii; i++) { 168 | e = e.n; 169 | e = e[names[i]] || (e[names[i]] = {n: {}}); 170 | } 171 | e.f = e.f || []; 172 | for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) { 173 | return fun; 174 | } 175 | e.f.push(f); 176 | return function (zIndex) { 177 | if (+zIndex == +zIndex) { 178 | f.zIndex = +zIndex; 179 | } 180 | }; 181 | }; 182 | /*\ 183 | * eve.stop 184 | [ method ] 185 | ** 186 | * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing. 187 | \*/ 188 | eve.stop = function () { 189 | stop = 1; 190 | }; 191 | /*\ 192 | * eve.nt 193 | [ method ] 194 | ** 195 | * Could be used inside event handler to figure out actual name of the event. 196 | ** 197 | > Arguments 198 | ** 199 | - subname (string) #optional subname of the event 200 | ** 201 | = (string) name of the event, if `subname` is not specified 202 | * or 203 | = (boolean) `true`, if current event’s name contains `subname` 204 | \*/ 205 | eve.nt = function (subname) { 206 | if (subname) { 207 | return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(current_event); 208 | } 209 | return current_event; 210 | }; 211 | /*\ 212 | * eve.off 213 | [ method ] 214 | ** 215 | * Removes given function from the list of event listeners assigned to given name. 216 | * If no arguments specified all the events will be cleared. 217 | ** 218 | > Arguments 219 | ** 220 | - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards 221 | - f (function) event handler function 222 | \*/ 223 | /*\ 224 | * eve.unbind 225 | [ method ] 226 | ** 227 | * See @eve.off 228 | \*/ 229 | eve.off = eve.unbind = function (name, f) { 230 | if (!name) { 231 | eve._events = events = {n: {}}; 232 | return; 233 | } 234 | var names = name.split(separator), 235 | e, 236 | key, 237 | splice, 238 | i, ii, j, jj, 239 | cur = [events]; 240 | for (i = 0, ii = names.length; i < ii; i++) { 241 | for (j = 0; j < cur.length; j += splice.length - 2) { 242 | splice = [j, 1]; 243 | e = cur[j].n; 244 | if (names[i] != wildcard) { 245 | if (e[names[i]]) { 246 | splice.push(e[names[i]]); 247 | } 248 | } else { 249 | for (key in e) if (e[has](key)) { 250 | splice.push(e[key]); 251 | } 252 | } 253 | cur.splice.apply(cur, splice); 254 | } 255 | } 256 | for (i = 0, ii = cur.length; i < ii; i++) { 257 | e = cur[i]; 258 | while (e.n) { 259 | if (f) { 260 | if (e.f) { 261 | for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) { 262 | e.f.splice(j, 1); 263 | break; 264 | } 265 | !e.f.length && delete e.f; 266 | } 267 | for (key in e.n) if (e.n[has](key) && e.n[key].f) { 268 | var funcs = e.n[key].f; 269 | for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) { 270 | funcs.splice(j, 1); 271 | break; 272 | } 273 | !funcs.length && delete e.n[key].f; 274 | } 275 | } else { 276 | delete e.f; 277 | for (key in e.n) if (e.n[has](key) && e.n[key].f) { 278 | delete e.n[key].f; 279 | } 280 | } 281 | e = e.n; 282 | } 283 | } 284 | }; 285 | /*\ 286 | * eve.once 287 | [ method ] 288 | ** 289 | * Binds given event handler with a given name to only run once then unbind itself. 290 | | eve.once("login", f); 291 | | eve("login"); // triggers f 292 | | eve("login"); // no listeners 293 | * Use @eve to trigger the listener. 294 | ** 295 | > Arguments 296 | ** 297 | - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards 298 | - f (function) event handler function 299 | ** 300 | = (function) same return function as @eve.on 301 | \*/ 302 | eve.once = function (name, f) { 303 | var f2 = function () { 304 | var res = f.apply(this, arguments); 305 | eve.unbind(name, f2); 306 | return res; 307 | }; 308 | return eve.on(name, f2); 309 | }; 310 | /*\ 311 | * eve.version 312 | [ property (string) ] 313 | ** 314 | * Current version of the library. 315 | \*/ 316 | eve.version = version; 317 | eve.toString = function () { 318 | return "You are running Eve " + version; 319 | }; 320 | (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define != "undefined" ? (define("eve", [], function() { return eve; })) : (glob.eve = eve)); 321 | })(this); 322 | -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2012 Adobe Systems Incorporated. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | * 22 | */ 23 | 24 | /*jslint vars: true, plusplus: true, devel: true, nomen: true, regexp: true, indent: 4, maxerr: 50 */ 25 | /*global define, $, brackets, window */ 26 | 27 | require.config({ 28 | paths: { 29 | "text" : "lib/text", 30 | "i18n" : "lib/i18n" 31 | }, 32 | locale: navigator.language 33 | }); 34 | 35 | var brackets_phonegap_linked_project_Id = 0; 36 | var brackets_phonegap_new_project = ""; 37 | var brackets_imbusy = 0; 38 | 39 | define(function (require, exports, module) { 40 | "use strict"; 41 | 42 | var Strings = require("js/strings"); 43 | var ProjectListLinkTemplate = require("text!templates/project-link-list.html"), 44 | PanelTemplate = require("text!templates/panel.html"), 45 | ProjectListPanelTemplate = require("text!templates/project-panel-list.html"), 46 | ProjectNewTemplate = require("text!templates/new-project.html"), 47 | ProjectDeleteTemplate = require("text!templates/delete-project.html"), 48 | LoginTemplate = require("text!templates/login.html"), 49 | NativeApp = brackets.getModule("utils/NativeApp"), 50 | AppInit = brackets.getModule("utils/AppInit"); 51 | 52 | 53 | //LoginTemplate = require("text!templates/hardcodedlogin.html") 54 | 55 | var CommandManager = brackets.getModule("command/CommandManager"), 56 | ProjectManager = brackets.getModule("project/ProjectManager"), 57 | EditorManager = brackets.getModule("editor/EditorManager"), 58 | Menus = brackets.getModule("command/Menus"), 59 | Dialogs = brackets.getModule("widgets/Dialogs"), 60 | FileUtils = brackets.getModule("file/FileUtils"), 61 | ExtensionUtils = brackets.getModule("utils/ExtensionUtils"), 62 | PanelManager = brackets.getModule("view/PanelManager"), 63 | eve, base64, 64 | format = (function () { 65 | var tokenRegex = /\{([^\}]+)\}/g, 66 | objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties 67 | replacer = function (all, key, obj) { 68 | var res = obj; 69 | key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) { 70 | name = name || quotedName; 71 | if (res) { 72 | if (name in res) { 73 | res = res[name]; 74 | } 75 | typeof res == "function" && isFunc && (res = res()); 76 | } 77 | }); 78 | //res = (res == null || res == obj ? all : res) + ""; 79 | return res + ""; 80 | }; 81 | return function (str, obj) { 82 | return String(str).replace(tokenRegex, function (all, key) { 83 | return replacer(all, key, obj); 84 | }); 85 | }; 86 | })(); 87 | 88 | require("js/widgets/bootstrap-alert"); 89 | require("js/qrcode"); 90 | require("js/jszip"); 91 | require("js/widgets/bootstrap-tooltip"); 92 | require("js/widgets/bootstrap-popover"); 93 | 94 | //This seems wrong 95 | require(["eve", "js/base64"], function (myeve, mybase64) { 96 | eve = myeve; 97 | base64 = mybase64; 98 | 99 | }); 100 | 101 | AppInit.appReady(function() { 102 | 103 | eve.f = function (event) { 104 | var attrs = [].slice.call(arguments, 1); 105 | return function () { 106 | eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0))); 107 | }; 108 | }; 109 | 110 | function zipProject(id) { 111 | var rootPath = ProjectManager.getProjectRoot().fullPath, 112 | files = [], 113 | txt = {txt: 1, html: 1, htm: 1, css: 1, svg: 1, xml: 1, js: 1}, 114 | count = { 115 | c: 0, 116 | on: function () { 117 | this.c++; 118 | }, 119 | off: function () { 120 | this.c--; 121 | !this.c && createZipFile(zip, id); 122 | } 123 | }, 124 | zip = new JSZip; 125 | function getRelPath(root, path) { 126 | root = root.split("/"); 127 | path = path.split("/"); 128 | while (path[0] == root[0]) { 129 | path.splice(0, 1); 130 | root.splice(0, 1); 131 | } 132 | return new Array(root.length + 1).join("../") + path.join("/"); 133 | } 134 | function processFile(path, data) { 135 | var ext = path.substring(path.lastIndexOf(".") + 1), 136 | isTxt = ext in txt; 137 | zip.file( 138 | path.substring(rootPath.length), 139 | data && (isTxt ? data : base64.encodeBinary(data)), 140 | {base64 : !isTxt} 141 | ); 142 | count.off(); 143 | } 144 | function readfile(path) { 145 | var xhr = new XMLHttpRequest; 146 | xhr.onload = function() { 147 | processFile(path, xhr.response); 148 | } 149 | xhr.overrideMimeType("text/plain; charset=x-user-defined"); 150 | xhr.open("get", getRelPath(FileUtils.getNativeBracketsDirectoryPath(), path), false); 151 | xhr.send(); 152 | } 153 | function readdir(path) { 154 | count.on(); 155 | brackets.fs.readdir(path, function (err, filelist) { 156 | for (var i = 0; i < filelist.length; i++) { 157 | (function (fullFilename) { 158 | // Ignore files that start with a dot. 159 | var filename = fullFilename.substring(fullFilename.lastIndexOf("/") + 1, fullFilename.length); 160 | if (filename.charAt(0) == ".") { 161 | return; 162 | } 163 | count.on(); 164 | brackets.fs.stat(fullFilename, function (statErr, statData) { 165 | if (!statErr) { 166 | if (statData.isDirectory()) { 167 | readdir(fullFilename + "/"); 168 | } else if (statData.isFile()) { 169 | count.on(); 170 | readfile(fullFilename); 171 | } 172 | } 173 | count.off(); 174 | }); 175 | })(path + filelist[i]); 176 | } 177 | count.off(); 178 | }); 179 | } 180 | readdir(rootPath); 181 | } 182 | 183 | function updateApp(id) { 184 | brackets_imbusy = 1; 185 | if (!id) id = brackets_phonegap_linked_project_Id; 186 | $("#pgb-progress-" + id).val(0).css("visibility", "visible"); 187 | zipProject(id); 188 | } 189 | 190 | function deleteApp(id) { 191 | var id = pendingDelete; 192 | var xhr = new XMLHttpRequest(); 193 | xhr.open("DELETE", "https://build.phonegap.com/api/v1/apps/" + id + "?auth_token=" + token, true); 194 | xhr.setRequestHeader("Cache-Control", "no-cache"); 195 | xhr.addEventListener("load", function() {eve("pgb.list"); pendingDelete = null;}, false); 196 | xhr.send(); 197 | } 198 | 199 | function createZipFile(zip, id) { 200 | var zipfile = zip.generate({"base64":false}); 201 | var byteArray = new Uint8Array(zipfile.length); 202 | for (var i = 0; i < zipfile.length; i++) { 203 | byteArray[i] = zipfile.charCodeAt(i) & 0xff; 204 | } 205 | var blob = new Blob([byteArray.buffer], {"type":"application/zip"}); 206 | putZipFile(blob, id); 207 | } 208 | 209 | function putZipFile(zipFile, id) { 210 | var xhr = new XMLHttpRequest(); 211 | xhr.upload.addEventListener("loadstart", function (ev) { 212 | // NO-OP 213 | }, false); 214 | xhr.upload.addEventListener("progress", function (ev) { 215 | if (ev.lengthComputable) { 216 | $("#pgb-progress-" + id).val( Math.round((ev.loaded / ev.total) * 100) ); 217 | } 218 | }, false); 219 | xhr.addEventListener("loadend", function (ev) { // Success or failure 220 | $("#pgb-progress-" + id).css("visibility", "hidden"); 221 | toggleRebuildLabels(id); 222 | brackets_imbusy = 0; 223 | eve("pgb.success.status", null, JSON.parse(this.responseText)); 224 | }, false); 225 | xhr.upload.addEventListener("error", function (ev) { 226 | console.log("Zip file upload error", this, ev); 227 | }, false); 228 | 229 | 230 | if (typeof(id) === "number") { 231 | xhr.open("PUT", "https://build.phonegap.com/api/v1/apps/" + id + "?auth_token=" + token, true); 232 | xhr.setRequestHeader("Cache-Control", "no-cache"); 233 | var form = new FormData(); 234 | form.append("file", zipFile, "file.zip"); 235 | xhr.send(form); 236 | } else { 237 | var urlToCall = "https://build.phonegap.com/api/v1/apps/?auth_token=" + token; 238 | xhr.open("POST", urlToCall , true); 239 | xhr.setRequestHeader("Cache-Control", "no-cache"); 240 | var form = new FormData(); 241 | form.append("file", zipFile, "file.zip"); 242 | form.append("data", '{"title":"'+ id +'","create_method":"file", "hydrates":"true"}'); 243 | xhr.send(form); 244 | } 245 | } 246 | 247 | var PGB_COMMAND_ID = "phonegap.build"; // package-style naming to avoid collisions 248 | CommandManager.register(Strings.COMMAND_NAME, PGB_COMMAND_ID, eve.f("pgb.button.click")); 249 | 250 | var button = $(""); 251 | ExtensionUtils.loadStyleSheet(module, "css/pgb.css"); 252 | 253 | var pgbhost = $('
'); 254 | 255 | button.attr({ 256 | title: Strings.COMMAND_NAME, 257 | id: "pgb-btn", 258 | href: "#", 259 | "class": "disabled" 260 | }).click(eve.f("pgb.button.click")); 261 | 262 | pgbhost.insertAfter("#toolbar-go-live"); 263 | pgbhost.html(button); 264 | 265 | 266 | var m_opts_panel = {Strings: Strings}; 267 | var $html_panel = $(Mustache.render(PanelTemplate, m_opts_panel)); 268 | 269 | $(".close", $html_panel).click(eve.f("pgb.panel.close")); 270 | 271 | var $panel = PanelManager.createBottomPanel("brackets-phonegap", $html_panel, 200); 272 | 273 | var anim = $("#pgb-anim", $panel); 274 | 275 | var $tableContainer = $(".table-container", $panel), 276 | $projectContainer = $("
").attr("id", "pgb-link-container"), 277 | $newContainer = $("
").attr("id", "pgb-new-item-dialog-container"), 278 | panelOpened, 279 | token, 280 | linkedProjectId, 281 | pendingDelete, 282 | projects, 283 | platforms = ["ios", "android", "winphone", "blackberry", "webos", "symbian"]; 284 | 285 | function ajax(url, name, type, username, password, showProgress) { 286 | if (showProgress) eve("pgb.status.progress"); 287 | var fullUrl = "https://build.phonegap.com/" + url + (token ? "?auth_token=" + token : ""); 288 | //console.log("full Url called:", fullUrl, "type:", type, "name:", name); 289 | $.ajax({ 290 | url: fullUrl, 291 | type: type || "get", 292 | error: eve.f("pgb.error." + name), 293 | success: eve.f("pgb.success." + name), 294 | username: username, 295 | password: password, 296 | dataType: "json", 297 | cache: false, 298 | crossDomain: true 299 | }); 300 | } 301 | 302 | /** 303 | * Displays an alert box between the menu bar and the editor. 304 | * 305 | * @message The text in the alert box. HTML tags are ok. 306 | * @showButtons Whether or not to show the OK and Cancel buttons. 307 | * @name The name which will be used to invoke your callbacks: pgb.alert..ok and pgb.alert..cancel. 308 | * @autoClose Whether or not to automatically close this alert in 5 seconds. 309 | */ 310 | function showAlert(message, showButtons, name, autoClose) { 311 | var options = { 312 | animation : true, 313 | html : true, 314 | placement : "left", 315 | trigger : "click", 316 | content : message 317 | }; 318 | 319 | $("#pgb-btn-holder").popover(options); 320 | $("#pgb-btn-holder").popover("show"); 321 | $("#pgb-btn-holder + .popover").css("top", "20px"); 322 | $("#pgb-btn-holder + .popover .arrow").css("top", "40%"); 323 | var doIt = function() { 324 | $("#pgb-btn-holder").popover("destroy"); 325 | } 326 | setTimeout(doIt, 5000); 327 | 328 | } 329 | 330 | function toggleRebuildLabels(id) { 331 | $("#rebuild-link-" + id).toggle(); 332 | $("#rebuilding-text-" + id).toggle(); 333 | } 334 | 335 | eve.on("pgb.status", function () { 336 | var type = eve.nt().split(/[\.\/]/)[2]; 337 | button[0].className = type; 338 | anim[type == "progress" ? "show" : "hide" ](); 339 | }); 340 | eve.on("pgb.login", function (login, password) { 341 | eve("pgb.anim"); 342 | ajax("token", "login", "post", login, password, true); 343 | }); 344 | eve.on("pgb.list", function () { 345 | ajax("api/v1/apps", "list", "get", null, null, true); 346 | }); 347 | eve.on("pgb.projectinfo", function (id) { 348 | ajax("api/v1/apps/" + id, "projectinfo", "get", null, null, true); 349 | }); 350 | eve.on("pgb.button.click", function () { 351 | if (!token) { 352 | eve("pgb.before.login"); 353 | } 354 | eve("pgb.panel.open"); 355 | }); 356 | 357 | eve.on("pgb.before.login", function () { 358 | 359 | var m_opts = {Strings: Strings}; 360 | var renderedTemplate = Mustache.render(LoginTemplate, m_opts); 361 | 362 | 363 | $(".pgb-table-container").append(renderedTemplate); 364 | var $form = $("#pgb-login-form"); 365 | var inputs = $("input", $form); 366 | $form.on("submit", function (e) { 367 | e.preventDefault(); 368 | eve("pgb.login", null, inputs[0].value, inputs[1].value); 369 | $("#pgb-login-form input[type=submit]").attr("disabled","disabled"); 370 | }); 371 | }); 372 | eve.on("pgb.panel.open", function () { 373 | $panel.show(); 374 | EditorManager.resizeEditor(); 375 | }); 376 | eve.on("pgb.panel.close", function () { 377 | $panel.hide(); 378 | EditorManager.resizeEditor(); 379 | }); 380 | eve.on("pgb.error", function (json) { 381 | Dialogs.showModalDialog(Dialogs.DIALOG_ID_ERROR, Strings.LOGIN_FAILED_DIALOG_TITLE, Strings.LOGIN_FAILED_DIALOG_MESSAGE); 382 | eve("pgb.status.error"); 383 | $("#pgb-login-form input[type=submit]").removeAttr("disabled"); 384 | }); 385 | eve.on("pgb.success", function (json) { 386 | eve("pgb.status.normal"); 387 | }); 388 | 389 | var PGB_MENU_ID = "phonegap.build.menu"; 390 | var PGB_OPEN_COMMAND_ID = "phonegap.build.open"; 391 | var PGB_LINK_COMMAND_ID = "phonegap.build.link"; 392 | var PGB_BUILD_COMMAND_ID = "phonegap.build.build"; 393 | var PGB_NEW_COMMAND_ID = "phonegap.build.new"; 394 | 395 | eve.on("pgb.success.login", function (json) { 396 | token = json.token; 397 | 398 | var pgMenu = Menus.addMenu(Strings.MENU_NAME, PGB_MENU_ID); 399 | 400 | CommandManager.register(Strings.OPEN_PANEL_MENU_ENTRY, PGB_OPEN_COMMAND_ID, eve.f("pgb.button.click")); 401 | pgMenu.addMenuItem(PGB_OPEN_COMMAND_ID); 402 | 403 | CommandManager.register(Strings.LINK_PROJECT_MENU_ITEM, PGB_LINK_COMMAND_ID, eve.f("pgb.link")); 404 | pgMenu.addMenuItem(PGB_LINK_COMMAND_ID); 405 | 406 | CommandManager.register(Strings.NEW_PROJECT_OPTION, PGB_NEW_COMMAND_ID, eve.f("pgb.new")); 407 | pgMenu.addMenuItem(PGB_NEW_COMMAND_ID); 408 | 409 | CommandManager.register(Strings.SEND_FILES_MENU_ENTRY, PGB_BUILD_COMMAND_ID, eve.f("pgb.update.confirm")); 410 | 411 | pgMenu.addMenuItem(Menus.DIVIDER); 412 | pgMenu.addMenuItem(PGB_BUILD_COMMAND_ID); 413 | 414 | showAlert(Strings.LOGIN_SUCCESS_MESSAGE + Strings.LINK_PROJECT_MENU_ITEM, false, false); 415 | 416 | eve("pgb.status.normal"); 417 | eve("pgb.list"); 418 | }); 419 | eve.on("pgb.success.list", function (json) { 420 | json.apps.sort(function (a,b) {if (a.title < b.title) return -1; if (a.title > b.title) return 1; return 0; }); 421 | projects = json.apps; 422 | var projectsMassaged = []; 423 | 424 | 425 | for (var i = 0; i < projects.length; i++) { 426 | var app = projects[i]; 427 | var appMassaged = {}; 428 | 429 | if (app.icon.filename === null) { 430 | appMassaged.iconlink = require.toUrl('./svg/icon-pg.svg'); 431 | } else { 432 | appMassaged.iconlink = "https://build.phonegap.com" + app.icon.link; 433 | } 434 | appMassaged.id = app.id; 435 | appMassaged.title = app.title; 436 | appMassaged.platforms = []; 437 | 438 | platforms.forEach(function(val, index) { 439 | var platform = {}; 440 | platform.os = val; 441 | platform.download = app.download[val]; 442 | if (app.status[val] === null) { 443 | platform.status = "error"; 444 | } else { 445 | platform.status = app.status[val]; 446 | } 447 | appMassaged.platforms.push(platform); 448 | }); 449 | projectsMassaged.push(appMassaged); 450 | 451 | } 452 | var m_opts_project_list = {Strings:Strings, projects: projectsMassaged}; 453 | var html_project_list = Mustache.render(ProjectListPanelTemplate, m_opts_project_list); 454 | $(".pgb-table-container").html(html_project_list); 455 | $(".pgb-table-container").click(eve.f("pgb.click")); 456 | 457 | $(".project-link").click(function (e) { 458 | eve("pgb.url.open", null, $(e.target).attr("data-url")); 459 | }); 460 | }); 461 | eve.on("pgb.click", function (e) { 462 | console.log("pgb.click"); 463 | console.log(e.target); 464 | var span = e.target; 465 | if (!String(span.id).indexOf("pgb-app")) { 466 | var data = $(span).attr("data-download"); 467 | if (data == {}) { 468 | return; 469 | } 470 | var qr = qrcode(5, "L"); 471 | qr.addData("https://build.phonegap.com" + data + "?qr_key=" + token); 472 | qr.make(); 473 | qr = qr.createSVGPath(4); 474 | var $qrcode = $(format('\ 475 | \ 476 | \ 477 | \ 478 | \ 479 | \ 480 | \ 481 | \ 482 | \ 483 | \ 484 | ', qr)); 485 | var spanOffset = $(span).offset(); 486 | $qrcode.css({ 487 | position: "absolute", 488 | zIndex: 2000, 489 | top: spanOffset.top - 240, 490 | left: spanOffset.left - 94 491 | }).appendTo(window.document.body); 492 | setTimeout(function () { 493 | $(window.document).one("click", function () { 494 | $qrcode.remove(); 495 | }); 496 | }); 497 | } 498 | if (span.className.indexOf("pgb-rebuild") > -1) { 499 | eve("pgb.rebuild", null, span.getAttribute("data-id")); 500 | } 501 | 502 | if (span.className.indexOf("pgb-delete") > -1) { 503 | eve("pgb.delete", null, span.getAttribute("data-id")); 504 | } 505 | }); 506 | eve.on("pgb.success.projectinfo", function (json) { 507 | console.warn(2, json); 508 | }); 509 | eve.on("pgb.rebuild", function (id) { 510 | console.log("Rebuild Called", "api/v1/apps/" + id, "rebuild"); 511 | toggleRebuildLabels(id); 512 | ajax("api/v1/apps/" + id, "rebuild", "put", null, null, false); 513 | }); 514 | eve.on("pgb.delete", function (id) { 515 | pendingDelete = id; 516 | var m_opts = {Strings: Strings, projects:projects, Dialogs: Dialogs}; 517 | var renderedTemplate = Mustache.render(ProjectDeleteTemplate, m_opts); 518 | Dialogs.showModalDialogUsingTemplate(renderedTemplate).done(eve.f("pgb.alert.delete")); 519 | }); 520 | eve.on("pgb.error.rebuild", function (error) { 521 | console.log("pgb.error.rebuild", error); 522 | }); 523 | eve.on("pgb.success.rebuild", function (json) { 524 | console.log("pgb.success.rebuild", json); 525 | eve("pgb.success.status", null, json); 526 | showAlert(Strings.REBUILDING_SUCCESS_MESSAGE, false, null, true); 527 | }); 528 | eve.on("pgb.failure.rebuild", function () { 529 | console.log("pgb.failure.rebuild"); 530 | }); 531 | eve.on("pgb.url.open", function(url) { 532 | NativeApp.openURLInDefaultBrowser(url); 533 | 534 | }); 535 | eve.on("pgb.link", function() { 536 | var m_opts = {Strings: Strings, projects:projects, Dialogs: Dialogs}; 537 | var renderedTemplate = Mustache.render(ProjectListLinkTemplate, m_opts); 538 | Dialogs.showModalDialogUsingTemplate(renderedTemplate).done(eve.f("pgb.close.link")); 539 | }); 540 | eve.on("pgb.new", function() { 541 | var m_opts_new_project = {Strings:Strings, Dialogs: Dialogs}; 542 | var html_new_project = Mustache.render(ProjectNewTemplate, m_opts_new_project); 543 | Dialogs.showModalDialogUsingTemplate(html_new_project).done(eve.f("pgb.close.new")); 544 | }); 545 | eve.on("pgb.close.link", function(action) { 546 | console.log(brackets_phonegap_linked_project_Id); 547 | if (action === Dialogs.DIALOG_BTN_OK) { 548 | showAlert(Strings.LINK_SUCCESSFUL_MESSAGE + Strings.SEND_FILES_MENU_ENTRY + ".", false, null, false); 549 | } 550 | }); 551 | eve.on("pgb.close.new", function(action) { 552 | console.log(brackets_phonegap_new_project, action); 553 | 554 | var val = brackets_phonegap_new_project; 555 | if (action === Dialogs.DIALOG_BTN_CANCEL) { 556 | // NO-OP. Probably don't have to do anything. 557 | } 558 | else if (action === Dialogs.DIALOG_BTN_OK) { 559 | showAlert(Strings.NEW_ALERT_MESSAGE + " " + val + ".", false, null, false); 560 | console.log("closed call on new, now doing updateApp"); 561 | updateApp(val); 562 | } 563 | }); 564 | eve.on("pgb.update.confirm", function() { 565 | if(brackets_imbusy) { 566 | showAlert("I'm currently doing a build", false, null, false); 567 | return; 568 | } 569 | if (!brackets_phonegap_linked_project_Id) { 570 | showAlert(Strings.PROJECT_NOT_LINKED_MESSAGE + Strings.LINK_PROJECT_MENU_ITEM, false, null, false); 571 | return; 572 | } 573 | var title = Strings.SEND_FILES_MENU_ENTRY; 574 | var message = Strings.UPLOAD_CONFIRMATION_MESSAGE; 575 | Dialogs.showModalDialog(Dialogs.DIALOG_ID_ERROR, title, message).done(eve.f("pgb.alert.bundle")); 576 | }); 577 | eve.on("pgb.alert.bundle", function(action) { 578 | if (action === Dialogs.DIALOG_BTN_CANCEL) { 579 | // NO-OP. Probably don't have to do anything. 580 | } 581 | else if (action === Dialogs.DIALOG_BTN_OK) { 582 | updateApp(); 583 | } 584 | }); 585 | eve.on("pgb.alert.delete", function(action) { 586 | if (action === Dialogs.DIALOG_BTN_CANCEL) { 587 | // NO-OP. Probably don't have to do anything. 588 | } 589 | else if (action === Dialogs.DIALOG_BTN_OK) { 590 | deleteApp(pendingDelete); 591 | } 592 | 593 | }); 594 | eve.on("pgb.success.status", function(json) { 595 | var finished = true, 596 | status; 597 | 598 | // Assoicate new projects with this folder 599 | if (json.build_count == null){ 600 | brackets_phonegap_linked_project_Id = json.id; 601 | } 602 | 603 | for (var os in json.status) { 604 | status = json.status[os]; 605 | if (status == "pending") finished = false; 606 | $("#pgb-app-" + os + "-" + json.id).attr("class", "icon " + os + "-" + status); 607 | } 608 | if (finished) { 609 | toggleRebuildLabels(json.id); 610 | showAlert(Strings.REBUILT_SUCCESS_MESSAGE, false, null, true); 611 | eve("pgb.list"); 612 | } else { 613 | 614 | var $rebuildingMsg = $("#rebuilding-text-" + json.id).html(); 615 | 616 | if ($rebuildingMsg) { 617 | if ($("#rebuilding-text-" + json.id).css("display") == "none"){ 618 | toggleRebuildLabels(json.id); 619 | } 620 | $("#rebuilding-text-" + json.id).html( 621 | ($rebuildingMsg.length == Strings.REBUILDING_MESSAGE.length + 3) ? Strings.REBUILDING_MESSAGE : $rebuildingMsg + "." 622 | ); 623 | } 624 | else{ 625 | eve("pgb.list"); 626 | } 627 | setTimeout(function() { 628 | ajax("api/v1/apps/" + json.id, "status", "get", null, null, false) 629 | }, 3000); 630 | } 631 | }); 632 | }); 633 | }); 634 | -------------------------------------------------------------------------------- /js/jszip.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | JSZip - A Javascript class for generating and reading zip files 4 | 5 | 6 | (c) 2009-2012 Stuart Knightley 7 | Dual licenced under the MIT license or GPLv3. See LICENSE.markdown. 8 | 9 | Usage: 10 | zip = new JSZip(); 11 | zip.file("hello.txt", "Hello, World!").add("tempfile", "nothing"); 12 | zip.folder("images").file("smile.gif", base64Data, {base64: true}); 13 | zip.file("Xmas.txt", "Ho ho ho !", {date : new Date("December 25, 2007 00:00:01")}); 14 | zip.remove("tempfile"); 15 | 16 | base64zip = zip.generate(); 17 | 18 | **/ 19 | 20 | /** 21 | * Representation a of zip file in js 22 | * @constructor 23 | * @param {String=} data the data to load, if any (optional). 24 | * @param {Object=} options the options for creating this objects (optional). 25 | */ 26 | var JSZip = function(data, options) { 27 | // object containing the files : 28 | // { 29 | // "folder/" : {...}, 30 | // "folder/data.txt" : {...} 31 | // } 32 | this.files = {}; 33 | 34 | // Where we are in the hierarchy 35 | this.root = ""; 36 | 37 | if(data) { 38 | this.load(data, options); 39 | } 40 | }; 41 | 42 | JSZip.signature = { 43 | LOCAL_FILE_HEADER : "\x50\x4b\x03\x04", 44 | CENTRAL_FILE_HEADER : "\x50\x4b\x01\x02", 45 | CENTRAL_DIRECTORY_END : "\x50\x4b\x05\x06", 46 | ZIP64_CENTRAL_DIRECTORY_LOCATOR : "\x50\x4b\x06\x07", 47 | ZIP64_CENTRAL_DIRECTORY_END : "\x50\x4b\x06\x06", 48 | DATA_DESCRIPTOR : "\x50\x4b\x07\x08" 49 | }; 50 | 51 | // Default properties for a new file 52 | JSZip.defaults = { 53 | base64: false, 54 | binary: false, 55 | dir: false, 56 | date: null 57 | }; 58 | 59 | 60 | JSZip.prototype = (function () 61 | { 62 | /** 63 | * A simple object representing a file in the zip file. 64 | * @constructor 65 | * @param {string} name the name of the file 66 | * @param {string} data the data 67 | * @param {Object} options the options of the file 68 | */ 69 | var ZipObject = function (name, data, options) { 70 | this.name = name; 71 | this.data = data; 72 | this.options = options; 73 | }; 74 | 75 | ZipObject.prototype = { 76 | /** 77 | * Return the content as UTF8 string. 78 | * @return {string} the UTF8 string. 79 | */ 80 | asText : function () 81 | { 82 | return this.options.binary ? JSZip.prototype.utf8decode(this.data) : this.data; 83 | }, 84 | /** 85 | * Returns the binary content. 86 | * @return {string} the content as binary. 87 | */ 88 | asBinary : function () 89 | { 90 | return this.options.binary ? this.data : JSZip.prototype.utf8encode(this.data); 91 | } 92 | }; 93 | 94 | /** 95 | * Transform an integer into a string in hexadecimal. 96 | * @private 97 | * @param {number} dec the number to convert. 98 | * @param {number} bytes the number of bytes to generate. 99 | * @returns {string} the result. 100 | */ 101 | var decToHex = function(dec, bytes) { 102 | var hex = "", i; 103 | for(i = 0; i < bytes; i++) 104 | { 105 | hex += String.fromCharCode(dec&0xff); 106 | dec=dec>>>8; 107 | } 108 | return hex; 109 | }; 110 | 111 | /** 112 | * Merge the objects passed as parameters into a new one. 113 | * @private 114 | * @param {...Object} var_args All objects to merge. 115 | * @return {Object} a new object with the data of the others. 116 | */ 117 | var extend = function () { 118 | var result = {}, i, attr; 119 | for (i = 0; i < arguments.length; i++) // arguments is not enumerable in some browsers 120 | { 121 | for (attr in arguments[i]) 122 | { 123 | if(typeof result[attr] === "undefined") 124 | { 125 | result[attr] = arguments[i][attr]; 126 | } 127 | } 128 | } 129 | return result; 130 | }; 131 | 132 | /** 133 | * Transforms the (incomplete) options from the user into the complete 134 | * set of options to create a file. 135 | * @private 136 | * @param {Object} o the options from the user. 137 | * @return {Object} the complete set of options. 138 | */ 139 | var prepareFileAttrs = function (o) { 140 | o = o || {}; 141 | if (o.base64 === true && o.binary == null) { 142 | o.binary = true; 143 | } 144 | o = extend(o, JSZip.defaults); 145 | o.date = o.date || new Date(); 146 | 147 | return o; 148 | }; 149 | 150 | /** 151 | * Add a file in the current folder. 152 | * @private 153 | * @param {string} name the name of the file 154 | * @param {string} data the data of the file 155 | * @param {Object} o the options of the file 156 | * @return {Object} the new file. 157 | */ 158 | var fileAdd = function (name, data, o) { 159 | // be sure sub folders exist 160 | var parent = parentFolder(name); 161 | if (parent) { 162 | folderAdd.call(this, parent); 163 | } 164 | 165 | o = prepareFileAttrs(o); 166 | 167 | return this.files[name] = {name: name, data: data, options:o}; 168 | }; 169 | 170 | 171 | /** 172 | * Find the parent folder of the path. 173 | * @private 174 | * @param {string} path the path to use 175 | * @return {string} the parent folder, or "" 176 | */ 177 | var parentFolder = function (path) { 178 | if (path.slice(-1) == '/') 179 | { 180 | path = path.substring(0, path.length - 1); 181 | } 182 | var lastSlash = path.lastIndexOf('/'); 183 | return (lastSlash > 0) ? path.substring(0, lastSlash) : ""; 184 | }; 185 | 186 | /** 187 | * Add a (sub) folder in the current folder. 188 | * @private 189 | * @param {string} name the folder's name 190 | * @return {Object} the new folder. 191 | */ 192 | var folderAdd = function (name) { 193 | // Check the name ends with a / 194 | if (name.slice(-1) != "/") { 195 | name += "/"; // IE doesn't like substr(-1) 196 | } 197 | 198 | // Does this folder already exist? 199 | if (!this.files[name]) 200 | { 201 | // be sure sub folders exist 202 | var parent = parentFolder(name); 203 | if (parent) { 204 | folderAdd.call(this, parent); 205 | } 206 | 207 | fileAdd.call(this, name, '', {dir:true}); 208 | } 209 | return this.files[name]; 210 | }; 211 | 212 | /** 213 | * Generate the data found in the local header of a zip file. 214 | * Do not create it now, as some parts are re-used later. 215 | * @private 216 | * @param {Object} file the file to use. 217 | * @param {string} utfEncodedFileName the file name, utf8 encoded. 218 | * @param {string} compressionType the compression to use. 219 | * @return {Object} an object containing header and compressedData. 220 | */ 221 | var prepareLocalHeaderData = function(file, utfEncodedFileName, compressionType) { 222 | var useUTF8 = utfEncodedFileName !== file.name, 223 | data = file.data, 224 | o = file.options, 225 | dosTime, 226 | dosDate; 227 | 228 | // date 229 | // @see http://www.delorie.com/djgpp/doc/rbinter/it/52/13.html 230 | // @see http://www.delorie.com/djgpp/doc/rbinter/it/65/16.html 231 | // @see http://www.delorie.com/djgpp/doc/rbinter/it/66/16.html 232 | 233 | dosTime = o.date.getHours(); 234 | dosTime = dosTime << 6; 235 | dosTime = dosTime | o.date.getMinutes(); 236 | dosTime = dosTime << 5; 237 | dosTime = dosTime | o.date.getSeconds() / 2; 238 | 239 | dosDate = o.date.getFullYear() - 1980; 240 | dosDate = dosDate << 4; 241 | dosDate = dosDate | (o.date.getMonth() + 1); 242 | dosDate = dosDate << 5; 243 | dosDate = dosDate | o.date.getDate(); 244 | 245 | if (o.base64 === true) { 246 | data = JSZipBase64.decode(data); 247 | } 248 | // decode UTF-8 strings if we are dealing with text data 249 | if(o.binary === false) { 250 | data = this.utf8encode(data); 251 | } 252 | 253 | 254 | var compression = JSZip.compressions[compressionType]; 255 | var compressedData = compression.compress(data); 256 | 257 | var header = ""; 258 | 259 | // version needed to extract 260 | header += "\x0A\x00"; 261 | // general purpose bit flag 262 | // set bit 11 if utf8 263 | header += useUTF8 ? "\x00\x08" : "\x00\x00"; 264 | // compression method 265 | header += compression.magic; 266 | // last mod file time 267 | header += decToHex(dosTime, 2); 268 | // last mod file date 269 | header += decToHex(dosDate, 2); 270 | // crc-32 271 | header += decToHex(this.crc32(data), 4); 272 | // compressed size 273 | header += decToHex(compressedData.length, 4); 274 | // uncompressed size 275 | header += decToHex(data.length, 4); 276 | // file name length 277 | header += decToHex(utfEncodedFileName.length, 2); 278 | // extra field length 279 | header += "\x00\x00"; 280 | 281 | return { 282 | header:header, 283 | compressedData:compressedData 284 | }; 285 | }; 286 | 287 | 288 | // return the actual prototype of JSZip 289 | return { 290 | /** 291 | * Read an existing zip and merge the data in the current JSZip object. 292 | * The implementation is in jszip-load.js, don't forget to include it. 293 | * @param {string} stream The stream to load 294 | * @param {Object} options Options for loading the stream. 295 | * options.base64 : is the stream in base64 ? default : false 296 | * @return {JSZip} the current JSZip object 297 | */ 298 | load : function (stream, options) 299 | { 300 | throw new Error("Load method is not defined. Is the file jszip-load.js included ?"); 301 | }, 302 | 303 | /** 304 | * Filter nested files/folders with the specified function. 305 | * @param {Function} search the predicate to use : 306 | * function (relativePath, file) {...} 307 | * It takes 2 arguments : the relative path and the file. 308 | * @return {Array} An array of matching elements. 309 | */ 310 | filter : function (search) 311 | { 312 | var result = [], filename, relativePath, file, fileClone; 313 | for (filename in this.files) 314 | { 315 | file = this.files[filename]; 316 | // return a new object, don't let the user mess with our internal objects :) 317 | fileClone = new ZipObject(file.name, file.data, extend(file.options)); 318 | relativePath = filename.slice(this.root.length, filename.length); 319 | if (filename.slice(0, this.root.length) === this.root && // the file is in the current root 320 | search(relativePath, fileClone)) // and the file matches the function 321 | { 322 | result.push(fileClone); 323 | } 324 | } 325 | return result; 326 | }, 327 | 328 | /** 329 | * Add a file to the zip file, or search a file. 330 | * @param {string|RegExp} name The name of the file to add (if data is defined), 331 | * the name of the file to find (if no data) or a regex to match files. 332 | * @param {string} data The file data, either raw or base64 encoded 333 | * @param {Object} o File options 334 | * @return {JSZip|Object|Array} this JSZip object (when adding a file), 335 | * a file (when searching by string) or an array of files (when searching by regex). 336 | */ 337 | file : function(name, data, o) 338 | { 339 | if (arguments.length === 1) 340 | { 341 | if (name instanceof RegExp) 342 | { 343 | var regexp = name; 344 | return this.filter(function(relativePath, file) { 345 | return !file.options.dir && regexp.test(relativePath); 346 | }); 347 | } 348 | else // text 349 | { 350 | return this.filter(function (relativePath, file) { 351 | return !file.options.dir && relativePath === name; 352 | })[0]||null; 353 | } 354 | } 355 | else // more than one argument : we have data ! 356 | { 357 | name = this.root+name; 358 | fileAdd.call(this, name, data, o); 359 | } 360 | return this; 361 | }, 362 | 363 | /** 364 | * Add a directory to the zip file, or search. 365 | * @param {String|RegExp} arg The name of the directory to add, or a regex to search folders. 366 | * @return {JSZip} an object with the new directory as the root, or an array containing matching folders. 367 | */ 368 | folder : function(arg) 369 | { 370 | if (!arg) 371 | { 372 | throw new Error("folder : wrong argument"); 373 | } 374 | 375 | if (arg instanceof RegExp) 376 | { 377 | return this.filter(function(relativePath, file) { 378 | return file.options.dir && arg.test(relativePath); 379 | }); 380 | } 381 | 382 | // else, name is a new folder 383 | var name = this.root + arg; 384 | var newFolder = folderAdd.call(this, name); 385 | 386 | // Allow chaining by returning a new object with this folder as the root 387 | var ret = this.clone(); 388 | ret.root = newFolder.name; 389 | return ret; 390 | }, 391 | 392 | /** 393 | * Delete a file, or a directory and all sub-files, from the zip 394 | * @param {string} name the name of the file to delete 395 | * @return {JSZip} this JSZip object 396 | */ 397 | remove : function(name) 398 | { 399 | name = this.root + name; 400 | var file = this.files[name]; 401 | if (!file) 402 | { 403 | // Look for any folders 404 | if (name.slice(-1) != "/") { 405 | name += "/"; 406 | } 407 | file = this.files[name]; 408 | } 409 | 410 | if (file) 411 | { 412 | if (!file.options.dir) 413 | { 414 | // file 415 | delete this.files[name]; 416 | } 417 | else 418 | { 419 | // folder 420 | var kids = this.filter(function (relativePath, file) { 421 | return file.name.slice(0, name.length) === name; 422 | }); 423 | for (var i = 0; i < kids.length; i++) 424 | { 425 | delete this.files[kids[i].name]; 426 | } 427 | } 428 | } 429 | 430 | return this; 431 | }, 432 | 433 | /** 434 | * Generate the complete zip file 435 | * @param {Object} options the options to generate the zip file : 436 | * - base64, true to generate base64. 437 | * - compression, "STORE" by default. 438 | * @return {string} the zip file 439 | */ 440 | generate : function(options) 441 | { 442 | options = extend(options || {}, { 443 | base64 : true, 444 | compression : "STORE" 445 | }); 446 | var compression = options.compression.toUpperCase(); 447 | 448 | // The central directory, and files data 449 | var directory = [], files = [], fileOffset = 0; 450 | 451 | if (!JSZip.compressions[compression]) { 452 | throw compression + " is not a valid compression method !"; 453 | } 454 | 455 | for (var name in this.files) 456 | { 457 | if( !this.files.hasOwnProperty(name) ) { continue; } 458 | 459 | var file = this.files[name]; 460 | 461 | var utfEncodedFileName = this.utf8encode(file.name); 462 | 463 | var fileRecord = "", 464 | dirRecord = "", 465 | data = prepareLocalHeaderData.call(this, file, utfEncodedFileName, compression); 466 | fileRecord = JSZip.signature.LOCAL_FILE_HEADER + data.header + utfEncodedFileName + data.compressedData; 467 | 468 | dirRecord = JSZip.signature.CENTRAL_FILE_HEADER + 469 | // version made by (00: DOS) 470 | "\x14\x00" + 471 | // file header (common to file and central directory) 472 | data.header + 473 | // file comment length 474 | "\x00\x00" + 475 | // disk number start 476 | "\x00\x00" + 477 | // internal file attributes TODO 478 | "\x00\x00" + 479 | // external file attributes 480 | (this.files[name].dir===true?"\x10\x00\x00\x00":"\x00\x00\x00\x00")+ 481 | // relative offset of local header 482 | decToHex(fileOffset, 4) + 483 | // file name 484 | utfEncodedFileName; 485 | 486 | fileOffset += fileRecord.length; 487 | 488 | files.push(fileRecord); 489 | directory.push(dirRecord); 490 | } 491 | 492 | var fileData = files.join(""); 493 | var dirData = directory.join(""); 494 | 495 | var dirEnd = ""; 496 | 497 | // end of central dir signature 498 | dirEnd = JSZip.signature.CENTRAL_DIRECTORY_END + 499 | // number of this disk 500 | "\x00\x00" + 501 | // number of the disk with the start of the central directory 502 | "\x00\x00" + 503 | // total number of entries in the central directory on this disk 504 | decToHex(files.length, 2) + 505 | // total number of entries in the central directory 506 | decToHex(files.length, 2) + 507 | // size of the central directory 4 bytes 508 | decToHex(dirData.length, 4) + 509 | // offset of start of central directory with respect to the starting disk number 510 | decToHex(fileData.length, 4) + 511 | // .ZIP file comment length 512 | "\x00\x00"; 513 | 514 | var zip = fileData + dirData + dirEnd; 515 | return (options.base64) ? JSZipBase64.encode(zip) : zip; 516 | }, 517 | 518 | /** 519 | * 520 | * Javascript crc32 521 | * http://www.webtoolkit.info/ 522 | * 523 | */ 524 | crc32 : function(str, crc) 525 | { 526 | 527 | if (str === "" || typeof str === "undefined") { 528 | return 0; 529 | } 530 | 531 | var table = "00000000 77073096 EE0E612C 990951BA 076DC419 706AF48F E963A535 9E6495A3 0EDB8832 79DCB8A4 E0D5E91E 97D2D988 09B64C2B 7EB17CBD E7B82D07 90BF1D91 1DB71064 6AB020F2 F3B97148 84BE41DE 1ADAD47D 6DDDE4EB F4D4B551 83D385C7 136C9856 646BA8C0 FD62F97A 8A65C9EC 14015C4F 63066CD9 FA0F3D63 8D080DF5 3B6E20C8 4C69105E D56041E4 A2677172 3C03E4D1 4B04D447 D20D85FD A50AB56B 35B5A8FA 42B2986C DBBBC9D6 ACBCF940 32D86CE3 45DF5C75 DCD60DCF ABD13D59 26D930AC 51DE003A C8D75180 BFD06116 21B4F4B5 56B3C423 CFBA9599 B8BDA50F 2802B89E 5F058808 C60CD9B2 B10BE924 2F6F7C87 58684C11 C1611DAB B6662D3D 76DC4190 01DB7106 98D220BC EFD5102A 71B18589 06B6B51F 9FBFE4A5 E8B8D433 7807C9A2 0F00F934 9609A88E E10E9818 7F6A0DBB 086D3D2D 91646C97 E6635C01 6B6B51F4 1C6C6162 856530D8 F262004E 6C0695ED 1B01A57B 8208F4C1 F50FC457 65B0D9C6 12B7E950 8BBEB8EA FCB9887C 62DD1DDF 15DA2D49 8CD37CF3 FBD44C65 4DB26158 3AB551CE A3BC0074 D4BB30E2 4ADFA541 3DD895D7 A4D1C46D D3D6F4FB 4369E96A 346ED9FC AD678846 DA60B8D0 44042D73 33031DE5 AA0A4C5F DD0D7CC9 5005713C 270241AA BE0B1010 C90C2086 5768B525 206F85B3 B966D409 CE61E49F 5EDEF90E 29D9C998 B0D09822 C7D7A8B4 59B33D17 2EB40D81 B7BD5C3B C0BA6CAD EDB88320 9ABFB3B6 03B6E20C 74B1D29A EAD54739 9DD277AF 04DB2615 73DC1683 E3630B12 94643B84 0D6D6A3E 7A6A5AA8 E40ECF0B 9309FF9D 0A00AE27 7D079EB1 F00F9344 8708A3D2 1E01F268 6906C2FE F762575D 806567CB 196C3671 6E6B06E7 FED41B76 89D32BE0 10DA7A5A 67DD4ACC F9B9DF6F 8EBEEFF9 17B7BE43 60B08ED5 D6D6A3E8 A1D1937E 38D8C2C4 4FDFF252 D1BB67F1 A6BC5767 3FB506DD 48B2364B D80D2BDA AF0A1B4C 36034AF6 41047A60 DF60EFC3 A867DF55 316E8EEF 4669BE79 CB61B38C BC66831A 256FD2A0 5268E236 CC0C7795 BB0B4703 220216B9 5505262F C5BA3BBE B2BD0B28 2BB45A92 5CB36A04 C2D7FFA7 B5D0CF31 2CD99E8B 5BDEAE1D 9B64C2B0 EC63F226 756AA39C 026D930A 9C0906A9 EB0E363F 72076785 05005713 95BF4A82 E2B87A14 7BB12BAE 0CB61B38 92D28E9B E5D5BE0D 7CDCEFB7 0BDBDF21 86D3D2D4 F1D4E242 68DDB3F8 1FDA836E 81BE16CD F6B9265B 6FB077E1 18B74777 88085AE6 FF0F6A70 66063BCA 11010B5C 8F659EFF F862AE69 616BFFD3 166CCF45 A00AE278 D70DD2EE 4E048354 3903B3C2 A7672661 D06016F7 4969474D 3E6E77DB AED16A4A D9D65ADC 40DF0B66 37D83BF0 A9BCAE53 DEBB9EC5 47B2CF7F 30B5FFE9 BDBDF21C CABAC28A 53B39330 24B4A3A6 BAD03605 CDD70693 54DE5729 23D967BF B3667A2E C4614AB8 5D681B02 2A6F2B94 B40BBE37 C30C8EA1 5A05DF1B 2D02EF8D"; 532 | 533 | if (typeof(crc) == "undefined") { crc = 0; } 534 | var x = 0; 535 | var y = 0; 536 | 537 | crc = crc ^ (-1); 538 | for( var i = 0, iTop = str.length; i < iTop; i++ ) { 539 | y = ( crc ^ str.charCodeAt( i ) ) & 0xFF; 540 | x = "0x" + table.substr( y * 9, 8 ); 541 | crc = ( crc >>> 8 ) ^ x; 542 | } 543 | 544 | return crc ^ (-1); 545 | 546 | }, 547 | 548 | // Inspired by http://my.opera.com/GreyWyvern/blog/show.dml/1725165 549 | clone : function() 550 | { 551 | var newObj = new JSZip(); 552 | for (var i in this) 553 | { 554 | if (typeof this[i] !== "function") 555 | { 556 | newObj[i] = this[i]; 557 | } 558 | } 559 | return newObj; 560 | }, 561 | 562 | 563 | /** 564 | * http://www.webtoolkit.info/javascript-utf8.html 565 | */ 566 | utf8encode : function (string) { 567 | string = string.replace(/\r\n/g,"\n"); 568 | var utftext = ""; 569 | 570 | for (var n = 0; n < string.length; n++) { 571 | 572 | var c = string.charCodeAt(n); 573 | 574 | if (c < 128) { 575 | utftext += String.fromCharCode(c); 576 | } 577 | else if((c > 127) && (c < 2048)) { 578 | utftext += String.fromCharCode((c >> 6) | 192); 579 | utftext += String.fromCharCode((c & 63) | 128); 580 | } 581 | else { 582 | utftext += String.fromCharCode((c >> 12) | 224); 583 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 584 | utftext += String.fromCharCode((c & 63) | 128); 585 | } 586 | 587 | } 588 | 589 | return utftext; 590 | }, 591 | 592 | /** 593 | * http://www.webtoolkit.info/javascript-utf8.html 594 | */ 595 | utf8decode : function (utftext) { 596 | var string = ""; 597 | var i = 0; 598 | var c = 0, c1 = 0, c2 = 0, c3 = 0; 599 | 600 | while ( i < utftext.length ) { 601 | 602 | c = utftext.charCodeAt(i); 603 | 604 | if (c < 128) { 605 | string += String.fromCharCode(c); 606 | i++; 607 | } 608 | else if((c > 191) && (c < 224)) { 609 | c2 = utftext.charCodeAt(i+1); 610 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 611 | i += 2; 612 | } 613 | else { 614 | c2 = utftext.charCodeAt(i+1); 615 | c3 = utftext.charCodeAt(i+2); 616 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 617 | i += 3; 618 | } 619 | 620 | } 621 | 622 | return string; 623 | } 624 | }; 625 | }()); 626 | 627 | /* 628 | * Compression methods 629 | * This object is filled in as follow : 630 | * name : { 631 | * magic // the 2 bytes indentifying the compression method 632 | * compress // function, take the uncompressed content and return it compressed. 633 | * uncompress // function, take the compressed content and return it uncompressed. 634 | * } 635 | * 636 | * STORE is the default compression method, so it's included in this file. 637 | * Other methods should go to separated files : the user wants modularity. 638 | */ 639 | JSZip.compressions = { 640 | "STORE" : { 641 | magic : "\x00\x00", 642 | compress : function (content) 643 | { 644 | return content; // no compression 645 | }, 646 | uncompress : function (content) 647 | { 648 | return content; // no compression 649 | } 650 | } 651 | }; 652 | 653 | /** 654 | * 655 | * Base64 encode / decode 656 | * http://www.webtoolkit.info/ 657 | * 658 | * Hacked so that it doesn't utf8 en/decode everything 659 | **/ 660 | var JSZipBase64 = (function() { 661 | // private property 662 | var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 663 | 664 | return { 665 | // public method for encoding 666 | encode : function(input, utf8) { 667 | var output = ""; 668 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 669 | var i = 0; 670 | 671 | while (i < input.length) { 672 | 673 | chr1 = input.charCodeAt(i++); 674 | chr2 = input.charCodeAt(i++); 675 | chr3 = input.charCodeAt(i++); 676 | 677 | enc1 = chr1 >> 2; 678 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 679 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 680 | enc4 = chr3 & 63; 681 | 682 | if (isNaN(chr2)) { 683 | enc3 = enc4 = 64; 684 | } else if (isNaN(chr3)) { 685 | enc4 = 64; 686 | } 687 | 688 | output = output + 689 | _keyStr.charAt(enc1) + _keyStr.charAt(enc2) + 690 | _keyStr.charAt(enc3) + _keyStr.charAt(enc4); 691 | 692 | } 693 | 694 | return output; 695 | }, 696 | 697 | // public method for decoding 698 | decode : function(input, utf8) { 699 | var output = ""; 700 | var chr1, chr2, chr3; 701 | var enc1, enc2, enc3, enc4; 702 | var i = 0; 703 | 704 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); 705 | 706 | while (i < input.length) { 707 | 708 | enc1 = _keyStr.indexOf(input.charAt(i++)); 709 | enc2 = _keyStr.indexOf(input.charAt(i++)); 710 | enc3 = _keyStr.indexOf(input.charAt(i++)); 711 | enc4 = _keyStr.indexOf(input.charAt(i++)); 712 | 713 | chr1 = (enc1 << 2) | (enc2 >> 4); 714 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 715 | chr3 = ((enc3 & 3) << 6) | enc4; 716 | 717 | output = output + String.fromCharCode(chr1); 718 | 719 | if (enc3 != 64) { 720 | output = output + String.fromCharCode(chr2); 721 | } 722 | if (enc4 != 64) { 723 | output = output + String.fromCharCode(chr3); 724 | } 725 | 726 | } 727 | 728 | return output; 729 | 730 | } 731 | }; 732 | }()); 733 | 734 | // enforcing Stuk's coding style 735 | // vim: set shiftwidth=3 softtabstop=3: 736 | -------------------------------------------------------------------------------- /js/qrcode.js: -------------------------------------------------------------------------------- 1 | //--------------------------------------------------------------------- 2 | // 3 | // QR Code Generator for JavaScript 4 | // 5 | // Copyright (c) 2009 Kazuhiko Arase 6 | // 7 | // URL: http://www.d-project.com/ 8 | // 9 | // Licensed under the MIT license: 10 | // http://www.opensource.org/licenses/mit-license.php 11 | // 12 | // The word 'QR Code' is registered trademark of 13 | // DENSO WAVE INCORPORATED 14 | // http://www.denso-wave.com/qrcode/faqpatent-e.html 15 | // 16 | //--------------------------------------------------------------------- 17 | 18 | var qrcode = function() { 19 | 20 | //--------------------------------------------------------------------- 21 | // qrcode 22 | //--------------------------------------------------------------------- 23 | 24 | /** 25 | * qrcode 26 | * @param typeNumber 1 to 10 27 | * @param errorCorrectLevel 'L','M','Q','H' 28 | */ 29 | var qrcode = function(typeNumber, errorCorrectLevel) { 30 | 31 | var PAD0 = 0xEC; 32 | var PAD1 = 0x11; 33 | 34 | var _typeNumber = typeNumber; 35 | var _errorCorrectLevel = QRErrorCorrectLevel[errorCorrectLevel]; 36 | var _modules = null; 37 | var _moduleCount = 0; 38 | var _dataCache = null; 39 | var _dataList = []; 40 | 41 | var _this = {}; 42 | 43 | var makeImpl = function(test, maskPattern) { 44 | 45 | _moduleCount = _typeNumber * 4 + 17; 46 | _modules = function(moduleCount) { 47 | var modules = []; 48 | for (var row = 0; row < moduleCount; row++) { 49 | modules[row] = []; 50 | for (var col = 0; col < moduleCount; col++) { 51 | modules[row][col] = null; 52 | } 53 | } 54 | return modules; 55 | }(_moduleCount); 56 | 57 | setupPositionProbePattern(0, 0); 58 | setupPositionProbePattern(_moduleCount - 7, 0); 59 | setupPositionProbePattern(0, _moduleCount - 7); 60 | setupPositionAdjustPattern(); 61 | setupTimingPattern(); 62 | setupTypeInfo(test, maskPattern); 63 | 64 | if (_typeNumber >= 7) { 65 | setupTypeNumber(test); 66 | } 67 | 68 | if (_dataCache == null) { 69 | _dataCache = createData(_typeNumber, _errorCorrectLevel, _dataList); 70 | } 71 | 72 | mapData(_dataCache, maskPattern); 73 | }; 74 | 75 | var setupPositionProbePattern = function(row, col) { 76 | 77 | for (var r = -1; r <= 7; r++) { 78 | 79 | if (row + r <= -1 || _moduleCount <= row + r) continue; 80 | 81 | for (var c = -1; c <= 7; c++) { 82 | 83 | if (col + c <= -1 || _moduleCount <= col + c) continue; 84 | 85 | if ( (0 <= r && r <= 6 && (c == 0 || c == 6) ) 86 | || (0 <= c && c <= 6 && (r == 0 || r == 6) ) 87 | || (2 <= r && r <= 4 && 2 <= c && c <= 4) ) { 88 | _modules[row + r][col + c] = true; 89 | } else { 90 | _modules[row + r][col + c] = false; 91 | } 92 | } 93 | } 94 | }; 95 | 96 | var getBestMaskPattern = function() { 97 | 98 | var minLostPoint = 0; 99 | var pattern = 0; 100 | 101 | for (var i = 0; i < 8; i++) { 102 | 103 | makeImpl(true, i); 104 | 105 | var lostPoint = QRUtil.getLostPoint(_this); 106 | 107 | if (i == 0 || minLostPoint > lostPoint) { 108 | minLostPoint = lostPoint; 109 | pattern = i; 110 | } 111 | } 112 | 113 | return pattern; 114 | }; 115 | 116 | var setupTimingPattern = function() { 117 | 118 | for (var r = 8; r < _moduleCount - 8; r++) { 119 | if (_modules[r][6] != null) { 120 | continue; 121 | } 122 | _modules[r][6] = (r % 2 == 0); 123 | } 124 | 125 | for (var c = 8; c < _moduleCount - 8; c++) { 126 | if (_modules[6][c] != null) { 127 | continue; 128 | } 129 | _modules[6][c] = (c % 2 == 0); 130 | } 131 | }; 132 | 133 | var setupPositionAdjustPattern = function() { 134 | 135 | var pos = QRUtil.getPatternPosition(_typeNumber); 136 | 137 | for (var i = 0; i < pos.length; i++) { 138 | 139 | for (var j = 0; j < pos.length; j++) { 140 | 141 | var row = pos[i]; 142 | var col = pos[j]; 143 | 144 | if (_modules[row][col] != null) { 145 | continue; 146 | } 147 | 148 | for (var r = -2; r <= 2; r++) { 149 | 150 | for (var c = -2; c <= 2; c++) { 151 | 152 | if (r == -2 || r == 2 || c == -2 || c == 2 153 | || (r == 0 && c == 0) ) { 154 | _modules[row + r][col + c] = true; 155 | } else { 156 | _modules[row + r][col + c] = false; 157 | } 158 | } 159 | } 160 | } 161 | } 162 | }; 163 | 164 | var setupTypeNumber = function(test) { 165 | 166 | var bits = QRUtil.getBCHTypeNumber(_typeNumber); 167 | 168 | for (var i = 0; i < 18; i++) { 169 | var mod = (!test && ( (bits >> i) & 1) == 1); 170 | _modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod; 171 | } 172 | 173 | for (var i = 0; i < 18; i++) { 174 | var mod = (!test && ( (bits >> i) & 1) == 1); 175 | _modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod; 176 | } 177 | }; 178 | 179 | var setupTypeInfo = function(test, maskPattern) { 180 | 181 | var data = (_errorCorrectLevel << 3) | maskPattern; 182 | var bits = QRUtil.getBCHTypeInfo(data); 183 | 184 | // vertical 185 | for (var i = 0; i < 15; i++) { 186 | 187 | var mod = (!test && ( (bits >> i) & 1) == 1); 188 | 189 | if (i < 6) { 190 | _modules[i][8] = mod; 191 | } else if (i < 8) { 192 | _modules[i + 1][8] = mod; 193 | } else { 194 | _modules[_moduleCount - 15 + i][8] = mod; 195 | } 196 | } 197 | 198 | // horizontal 199 | for (var i = 0; i < 15; i++) { 200 | 201 | var mod = (!test && ( (bits >> i) & 1) == 1); 202 | 203 | if (i < 8) { 204 | _modules[8][_moduleCount - i - 1] = mod; 205 | } else if (i < 9) { 206 | _modules[8][15 - i - 1 + 1] = mod; 207 | } else { 208 | _modules[8][15 - i - 1] = mod; 209 | } 210 | } 211 | 212 | // fixed module 213 | _modules[_moduleCount - 8][8] = (!test); 214 | }; 215 | 216 | var mapData = function(data, maskPattern) { 217 | 218 | var inc = -1; 219 | var row = _moduleCount - 1; 220 | var bitIndex = 7; 221 | var byteIndex = 0; 222 | var maskFunc = QRUtil.getMaskFunction(maskPattern); 223 | 224 | for (var col = _moduleCount - 1; col > 0; col -= 2) { 225 | 226 | if (col == 6) col -= 1; 227 | 228 | while (true) { 229 | 230 | for (var c = 0; c < 2; c++) { 231 | 232 | if (_modules[row][col - c] == null) { 233 | 234 | var dark = false; 235 | 236 | if (byteIndex < data.length) { 237 | dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1); 238 | } 239 | 240 | var mask = maskFunc(row, col - c); 241 | 242 | if (mask) { 243 | dark = !dark; 244 | } 245 | 246 | _modules[row][col - c] = dark; 247 | bitIndex -= 1; 248 | 249 | if (bitIndex == -1) { 250 | byteIndex++; 251 | bitIndex = 7; 252 | } 253 | } 254 | } 255 | 256 | row += inc; 257 | 258 | if (row < 0 || _moduleCount <= row) { 259 | row -= inc; 260 | inc = -inc; 261 | break; 262 | } 263 | } 264 | } 265 | }; 266 | 267 | var createBytes = function(buffer, rsBlocks) { 268 | 269 | var offset = 0; 270 | 271 | var maxDcCount = 0; 272 | var maxEcCount = 0; 273 | 274 | var dcdata = []; 275 | var ecdata = []; 276 | 277 | for (var r = 0; r < rsBlocks.length; r++) { 278 | 279 | var dcCount = rsBlocks[r].dataCount; 280 | var ecCount = rsBlocks[r].totalCount - dcCount; 281 | 282 | maxDcCount = Math.max(maxDcCount, dcCount); 283 | maxEcCount = Math.max(maxEcCount, ecCount); 284 | 285 | dcdata[r] = []; 286 | 287 | for (var i = 0; i < dcCount; i++) { 288 | dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset]; 289 | } 290 | offset += dcCount; 291 | 292 | var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount), 293 | length = rsPoly.getLength() - 1, 294 | rawPoly = qrPolynomial(dcdata[r], length), 295 | modPoly = rawPoly.mod(rsPoly); 296 | 297 | ecdata[r] = []; 298 | for (var i = 0; i < length; i++) { 299 | var modIndex = i + modPoly.getLength() - length; 300 | ecdata[r][i] = (modIndex >= 0)? modPoly.get(modIndex) : 0; 301 | } 302 | } 303 | 304 | var totalCodeCount = 0; 305 | for (var i = 0; i < rsBlocks.length; i++) { 306 | totalCodeCount += rsBlocks[i].totalCount; 307 | } 308 | 309 | var data = []; 310 | var index = 0; 311 | 312 | for (var i = 0; i < maxDcCount; i++) { 313 | for (var r = 0; r < rsBlocks.length; r++) { 314 | if (i < dcdata[r].length) { 315 | data[index] = dcdata[r][i]; 316 | index++; 317 | } 318 | } 319 | } 320 | 321 | for (var i = 0; i < maxEcCount; i++) { 322 | for (var r = 0; r < rsBlocks.length; r++) { 323 | if (i < ecdata[r].length) { 324 | data[index] = ecdata[r][i]; 325 | index++; 326 | } 327 | } 328 | } 329 | 330 | return data; 331 | }; 332 | 333 | var createData = function(typeNumber, errorCorrectLevel, dataList) { 334 | 335 | var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel); 336 | 337 | var buffer = qrBitBuffer(); 338 | 339 | for (var i = 0; i < dataList.length; i++) { 340 | var data = dataList[i]; 341 | buffer.put(data.getMode(), 4); 342 | buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) ); 343 | data.write(buffer); 344 | } 345 | 346 | // calc num max data. 347 | var totalDataCount = 0; 348 | for (var i = 0; i < rsBlocks.length; i++) { 349 | totalDataCount += rsBlocks[i].dataCount; 350 | } 351 | 352 | if (buffer.getLengthInBits() > totalDataCount * 8) { 353 | throw new Error('code length overflow. (' 354 | + buffer.getLengthInBits() 355 | + '>' 356 | + totalDataCount * 8 357 | + ')'); 358 | } 359 | 360 | // end code 361 | if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) { 362 | buffer.put(0, 4); 363 | } 364 | 365 | // padding 366 | while (buffer.getLengthInBits() % 8 != 0) { 367 | buffer.putBit(false); 368 | } 369 | 370 | // padding 371 | while (true) { 372 | 373 | if (buffer.getLengthInBits() >= totalDataCount * 8) { 374 | break; 375 | } 376 | buffer.put(PAD0, 8); 377 | 378 | if (buffer.getLengthInBits() >= totalDataCount * 8) { 379 | break; 380 | } 381 | buffer.put(PAD1, 8); 382 | } 383 | 384 | return createBytes(buffer, rsBlocks); 385 | }; 386 | 387 | _this.addData = function(data) { 388 | var newData = qr8BitByte(data); 389 | _dataList.push(newData); 390 | _dataCache = null; 391 | }; 392 | 393 | _this.isDark = function(row, col) { 394 | if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) { 395 | throw new Error(row + ',' + col); 396 | } 397 | return _modules[row][col]; 398 | }; 399 | 400 | _this.getModuleCount = function() { 401 | return _moduleCount; 402 | }; 403 | 404 | _this.make = function() { 405 | makeImpl(false, getBestMaskPattern() ); 406 | }; 407 | 408 | _this.createTableTag = function(cellSize, margin) { 409 | 410 | cellSize = cellSize || 2; 411 | margin = (typeof margin == 'undefined')? cellSize * 4 : margin; 412 | 413 | var qrHtml = ''; 414 | 415 | qrHtml += ''; 420 | qrHtml += ''; 421 | 422 | for (var r = 0; r < _this.getModuleCount(); r++) { 423 | 424 | qrHtml += ''; 425 | 426 | for (var c = 0; c < _this.getModuleCount(); c++) { 427 | qrHtml += ''; 440 | } 441 | 442 | qrHtml += ''; 443 | qrHtml += '
'; 437 | } 438 | 439 | qrHtml += '
'; 444 | 445 | return qrHtml; 446 | }; 447 | 448 | _this.createImgTag = function(cellSize, margin) { 449 | 450 | cellSize = cellSize || 2; 451 | margin = (typeof margin == 'undefined')? cellSize * 4 : margin; 452 | 453 | var size = _this.getModuleCount() * cellSize + margin * 2; 454 | var min = margin; 455 | var max = size - margin; 456 | 457 | return createImgTag(size, size, function(x, y) { 458 | if (min <= x && x < max && min <= y && y < max) { 459 | var c = Math.floor( (x - min) / cellSize); 460 | var r = Math.floor( (y - min) / cellSize); 461 | return _this.isDark(r, c)? 0 : 1; 462 | } else { 463 | return 1; 464 | } 465 | } ); 466 | }; 467 | 468 | // Brackets plugins addon: export as SVG Path. 469 | _this.createSVGPath = function (cellSize) { 470 | 471 | cellSize = cellSize || 2; 472 | 473 | var qrHtml = "", 474 | length = _this.getModuleCount(); 475 | 476 | for (var r = 0; r < length; r++) { 477 | 478 | for (var c = 0; c < length; c++) if (_this.isDark(r, c)) { 479 | qrHtml += "M" + c * cellSize + "," + r * cellSize + 480 | "h" + cellSize + "v" + cellSize + "h-" + cellSize + "z"; 481 | } 482 | } 483 | 484 | return { 485 | d: qrHtml, 486 | size: length * cellSize, 487 | toString: function () { 488 | return this.d; 489 | } 490 | }; 491 | }; 492 | 493 | return _this; 494 | }; 495 | 496 | //--------------------------------------------------------------------- 497 | // qrcode.stringToBytes 498 | //--------------------------------------------------------------------- 499 | 500 | qrcode.stringToBytes = function(s) { 501 | var bytes = []; 502 | for (var i = 0; i < s.length; i++) { 503 | var c = s.charCodeAt(i); 504 | bytes.push(c & 0xff); 505 | } 506 | return bytes; 507 | }; 508 | 509 | //--------------------------------------------------------------------- 510 | // qrcode.createStringToBytes 511 | //--------------------------------------------------------------------- 512 | 513 | /** 514 | * @param unicodeData base64 string of byte array. 515 | * [16bit Unicode],[16bit Bytes], ... 516 | * @param numChars 517 | */ 518 | qrcode.createStringToBytes = function(unicodeData, numChars) { 519 | 520 | // create conversion map. 521 | 522 | var unicodeMap = function() { 523 | 524 | var bin = base64DecodeInputStream(unicodeData); 525 | var read = function() { 526 | var b = bin.read(); 527 | if (b == -1) throw new Error(); 528 | return b; 529 | }; 530 | 531 | var count = 0; 532 | var unicodeMap = {}; 533 | while (true) { 534 | var b0 = bin.read(); 535 | if (b0 == -1) break; 536 | var b1 = read(); 537 | var b2 = read(); 538 | var b3 = read(); 539 | var k = String.fromCharCode( (b0 << 8) | b1); 540 | var v = (b2 << 8) | b3; 541 | unicodeMap[k] = v; 542 | count++; 543 | } 544 | if (count != numChars) { 545 | throw new Error(count + ' != ' + numChars); 546 | } 547 | 548 | return unicodeMap; 549 | }(); 550 | 551 | var unknownChar = '?'.charCodeAt(0); 552 | 553 | return function(s) { 554 | var bytes = []; 555 | for (var i = 0; i < s.length; i++) { 556 | var c = s.charCodeAt(i); 557 | if (c < 128) { 558 | bytes.push(c); 559 | } else { 560 | var b = unicodeMap[s.charAt(i)]; 561 | if (typeof b == 'number') { 562 | if ( (b & 0xff) == b) { 563 | // 1byte 564 | bytes.push(b); 565 | } else { 566 | // 2bytes 567 | bytes.push(b >>> 8); 568 | bytes.push(b & 0xff); 569 | } 570 | } else { 571 | bytes.push(unknownChar); 572 | } 573 | } 574 | } 575 | return bytes; 576 | }; 577 | }; 578 | 579 | //--------------------------------------------------------------------- 580 | // QRMode 581 | //--------------------------------------------------------------------- 582 | 583 | var QRMode = { 584 | MODE_NUMBER : 1 << 0, 585 | MODE_ALPHA_NUM : 1 << 1, 586 | MODE_8BIT_BYTE : 1 << 2, 587 | MODE_KANJI : 1 << 3 588 | }; 589 | 590 | //--------------------------------------------------------------------- 591 | // QRErrorCorrectLevel 592 | //--------------------------------------------------------------------- 593 | 594 | var QRErrorCorrectLevel = { 595 | L : 1, 596 | M : 0, 597 | Q : 3, 598 | H : 2 599 | }; 600 | 601 | //--------------------------------------------------------------------- 602 | // QRMaskPattern 603 | //--------------------------------------------------------------------- 604 | 605 | var QRMaskPattern = { 606 | PATTERN000 : 0, 607 | PATTERN001 : 1, 608 | PATTERN010 : 2, 609 | PATTERN011 : 3, 610 | PATTERN100 : 4, 611 | PATTERN101 : 5, 612 | PATTERN110 : 6, 613 | PATTERN111 : 7 614 | }; 615 | 616 | //--------------------------------------------------------------------- 617 | // QRUtil 618 | //--------------------------------------------------------------------- 619 | 620 | var QRUtil = function() { 621 | 622 | var PATTERN_POSITION_TABLE = [ 623 | [], 624 | [6, 18], 625 | [6, 22], 626 | [6, 26], 627 | [6, 30], 628 | [6, 34], 629 | [6, 22, 38], 630 | [6, 24, 42], 631 | [6, 26, 46], 632 | [6, 28, 50], 633 | [6, 30, 54], 634 | [6, 32, 58], 635 | [6, 34, 62], 636 | [6, 26, 46, 66], 637 | [6, 26, 48, 70], 638 | [6, 26, 50, 74], 639 | [6, 30, 54, 78], 640 | [6, 30, 56, 82], 641 | [6, 30, 58, 86], 642 | [6, 34, 62, 90], 643 | [6, 28, 50, 72, 94], 644 | [6, 26, 50, 74, 98], 645 | [6, 30, 54, 78, 102], 646 | [6, 28, 54, 80, 106], 647 | [6, 32, 58, 84, 110], 648 | [6, 30, 58, 86, 114], 649 | [6, 34, 62, 90, 118], 650 | [6, 26, 50, 74, 98, 122], 651 | [6, 30, 54, 78, 102, 126], 652 | [6, 26, 52, 78, 104, 130], 653 | [6, 30, 56, 82, 108, 134], 654 | [6, 34, 60, 86, 112, 138], 655 | [6, 30, 58, 86, 114, 142], 656 | [6, 34, 62, 90, 118, 146], 657 | [6, 30, 54, 78, 102, 126, 150], 658 | [6, 24, 50, 76, 102, 128, 154], 659 | [6, 28, 54, 80, 106, 132, 158], 660 | [6, 32, 58, 84, 110, 136, 162], 661 | [6, 26, 54, 82, 110, 138, 166], 662 | [6, 30, 58, 86, 114, 142, 170] 663 | ]; 664 | var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0); 665 | var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0); 666 | var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1); 667 | 668 | var _this = {}; 669 | 670 | var getBCHDigit = function(data) { 671 | var digit = 0; 672 | while (data != 0) { 673 | digit++; 674 | data >>>= 1; 675 | } 676 | return digit; 677 | }; 678 | 679 | _this.getBCHTypeInfo = function(data) { 680 | var d = data << 10; 681 | while (getBCHDigit(d) - getBCHDigit(G15) >= 0) { 682 | d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) ); 683 | } 684 | return ( (data << 10) | d) ^ G15_MASK; 685 | }; 686 | 687 | _this.getBCHTypeNumber = function(data) { 688 | var d = data << 12; 689 | while (getBCHDigit(d) - getBCHDigit(G18) >= 0) { 690 | d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) ); 691 | } 692 | return (data << 12) | d; 693 | }; 694 | 695 | _this.getPatternPosition = function(typeNumber) { 696 | return PATTERN_POSITION_TABLE[typeNumber - 1]; 697 | }; 698 | 699 | _this.getMaskFunction = function(maskPattern) { 700 | 701 | switch (maskPattern) { 702 | 703 | case QRMaskPattern.PATTERN000 : 704 | return function(i, j) { return (i + j) % 2 == 0; }; 705 | case QRMaskPattern.PATTERN001 : 706 | return function(i, j) { return i % 2 == 0; }; 707 | case QRMaskPattern.PATTERN010 : 708 | return function(i, j) { return j % 3 == 0; }; 709 | case QRMaskPattern.PATTERN011 : 710 | return function(i, j) { return (i + j) % 3 == 0; }; 711 | case QRMaskPattern.PATTERN100 : 712 | return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; }; 713 | case QRMaskPattern.PATTERN101 : 714 | return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; }; 715 | case QRMaskPattern.PATTERN110 : 716 | return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; }; 717 | case QRMaskPattern.PATTERN111 : 718 | return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; }; 719 | 720 | default : 721 | throw new Error('bad maskPattern:' + maskPattern); 722 | } 723 | }; 724 | 725 | _this.getErrorCorrectPolynomial = function(errorCorrectLength) { 726 | var a = qrPolynomial([1], 0); 727 | for (var i = 0; i < errorCorrectLength; i++) { 728 | a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) ); 729 | } 730 | return a; 731 | }; 732 | 733 | _this.getLengthInBits = function(mode, type) { 734 | 735 | if (1 <= type && type < 10) { 736 | 737 | // 1 - 9 738 | 739 | switch(mode) { 740 | case QRMode.MODE_NUMBER : return 10; 741 | case QRMode.MODE_ALPHA_NUM : return 9; 742 | case QRMode.MODE_8BIT_BYTE : return 8; 743 | case QRMode.MODE_KANJI : return 8; 744 | default : 745 | throw new Error('mode:' + mode); 746 | } 747 | 748 | } else if (type < 27) { 749 | 750 | // 10 - 26 751 | 752 | switch(mode) { 753 | case QRMode.MODE_NUMBER : return 12; 754 | case QRMode.MODE_ALPHA_NUM : return 11; 755 | case QRMode.MODE_8BIT_BYTE : return 16; 756 | case QRMode.MODE_KANJI : return 10; 757 | default : 758 | throw new Error('mode:' + mode); 759 | } 760 | 761 | } else if (type < 41) { 762 | 763 | // 27 - 40 764 | 765 | switch(mode) { 766 | case QRMode.MODE_NUMBER : return 14; 767 | case QRMode.MODE_ALPHA_NUM : return 13; 768 | case QRMode.MODE_8BIT_BYTE : return 16; 769 | case QRMode.MODE_KANJI : return 12; 770 | default : 771 | throw new Error('mode:' + mode); 772 | } 773 | 774 | } else { 775 | throw new Error('type:' + type); 776 | } 777 | }; 778 | 779 | _this.getLostPoint = function(qrcode) { 780 | 781 | var moduleCount = qrcode.getModuleCount(); 782 | 783 | var lostPoint = 0; 784 | 785 | // LEVEL1 786 | 787 | for (var row = 0; row < moduleCount; row++) { 788 | for (var col = 0; col < moduleCount; col++) { 789 | 790 | var sameCount = 0; 791 | var dark = qrcode.isDark(row, col); 792 | 793 | for (var r = -1; r <= 1; r++) { 794 | 795 | if (row + r < 0 || moduleCount <= row + r) { 796 | continue; 797 | } 798 | 799 | for (var c = -1; c <= 1; c++) { 800 | 801 | if (col + c < 0 || moduleCount <= col + c) { 802 | continue; 803 | } 804 | 805 | if (r == 0 && c == 0) { 806 | continue; 807 | } 808 | 809 | if (dark == qrcode.isDark(row + r, col + c) ) { 810 | sameCount++; 811 | } 812 | } 813 | } 814 | 815 | if (sameCount > 5) { 816 | lostPoint += (3 + sameCount - 5); 817 | } 818 | } 819 | }; 820 | 821 | // LEVEL2 822 | 823 | for (var row = 0; row < moduleCount - 1; row++) { 824 | for (var col = 0; col < moduleCount - 1; col++) { 825 | var count = 0; 826 | if (qrcode.isDark(row, col) ) count++; 827 | if (qrcode.isDark(row + 1, col) ) count++; 828 | if (qrcode.isDark(row, col + 1) ) count++; 829 | if (qrcode.isDark(row + 1, col + 1) ) count++; 830 | if (count == 0 || count == 4) { 831 | lostPoint += 3; 832 | } 833 | } 834 | } 835 | 836 | // LEVEL3 837 | 838 | for (var row = 0; row < moduleCount; row++) { 839 | for (var col = 0; col < moduleCount - 6; col++) { 840 | if (qrcode.isDark(row, col) 841 | && !qrcode.isDark(row, col + 1) 842 | && qrcode.isDark(row, col + 2) 843 | && qrcode.isDark(row, col + 3) 844 | && qrcode.isDark(row, col + 4) 845 | && !qrcode.isDark(row, col + 5) 846 | && qrcode.isDark(row, col + 6) ) { 847 | lostPoint += 40; 848 | } 849 | } 850 | } 851 | 852 | for (var col = 0; col < moduleCount; col++) { 853 | for (var row = 0; row < moduleCount - 6; row++) { 854 | if (qrcode.isDark(row, col) 855 | && !qrcode.isDark(row + 1, col) 856 | && qrcode.isDark(row + 2, col) 857 | && qrcode.isDark(row + 3, col) 858 | && qrcode.isDark(row + 4, col) 859 | && !qrcode.isDark(row + 5, col) 860 | && qrcode.isDark(row + 6, col) ) { 861 | lostPoint += 40; 862 | } 863 | } 864 | } 865 | 866 | // LEVEL4 867 | 868 | var darkCount = 0; 869 | 870 | for (var col = 0; col < moduleCount; col++) { 871 | for (var row = 0; row < moduleCount; row++) { 872 | if (qrcode.isDark(row, col) ) { 873 | darkCount++; 874 | } 875 | } 876 | } 877 | 878 | var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5; 879 | lostPoint += ratio * 10; 880 | 881 | return lostPoint; 882 | }; 883 | 884 | return _this; 885 | }(); 886 | 887 | //--------------------------------------------------------------------- 888 | // QRMath 889 | //--------------------------------------------------------------------- 890 | 891 | var QRMath = function() { 892 | 893 | var EXP_TABLE = []; 894 | var LOG_TABLE = []; 895 | 896 | // initialize tables 897 | for (var i = 0; i < 8; i++) { 898 | EXP_TABLE[i] = 1 << i; 899 | } 900 | for (var i = 8; i < 256; i++) { 901 | EXP_TABLE[i] = EXP_TABLE[i - 4] 902 | ^ EXP_TABLE[i - 5] 903 | ^ EXP_TABLE[i - 6] 904 | ^ EXP_TABLE[i - 8]; 905 | } 906 | for (var i = 0; i < 255; i++) { 907 | LOG_TABLE[EXP_TABLE[i] ] = i; 908 | } 909 | 910 | var _this = {}; 911 | 912 | _this.glog = function(n) { 913 | 914 | if (n < 1) { 915 | throw new Error('glog(' + n + ')'); 916 | } 917 | 918 | return LOG_TABLE[n]; 919 | }; 920 | 921 | _this.gexp = function(n) { 922 | 923 | while (n < 0) { 924 | n += 255; 925 | } 926 | 927 | while (n >= 256) { 928 | n -= 255; 929 | } 930 | 931 | return EXP_TABLE[n]; 932 | }; 933 | 934 | return _this; 935 | }(); 936 | 937 | //--------------------------------------------------------------------- 938 | // qrPolynomial 939 | //--------------------------------------------------------------------- 940 | 941 | function qrPolynomial(num, shift) { 942 | 943 | if (typeof num.length == 'undefined') { 944 | throw new Error(num.length + '/' + shift); 945 | } 946 | 947 | var _num = function() { 948 | var offset = 0; 949 | while (offset < num.length && num[offset] == 0) { 950 | offset++; 951 | } 952 | var _num = new Array(num.length - offset + shift); 953 | for (var i = 0; i < num.length - offset; i++) { 954 | _num[i] = num[i + offset]; 955 | } 956 | return _num; 957 | }(); 958 | 959 | var _this = {}; 960 | 961 | _this.get = function(index) { 962 | return _num[index]; 963 | }; 964 | 965 | _this.getLength = function() { 966 | return _num.length; 967 | }; 968 | 969 | _this.multiply = function(e) { 970 | 971 | var num = []; 972 | 973 | for (var i = 0; i < _this.getLength(); i++) { 974 | for (var j = 0; j < e.getLength(); j++) { 975 | num[i + j] ^= QRMath.gexp(QRMath.glog(_this.get(i) ) + QRMath.glog(e.get(j) ) ); 976 | } 977 | } 978 | 979 | return qrPolynomial(num, 0); 980 | }; 981 | 982 | _this.mod = function(e) { 983 | 984 | if (_this.getLength() - e.getLength() < 0) { 985 | return _this; 986 | } 987 | 988 | var ratio = QRMath.glog(_this.get(0) ) - QRMath.glog(e.get(0) ); 989 | 990 | var num = []; 991 | for (var i = 0; i < _this.getLength(); i++) { 992 | num[i] = _this.get(i); 993 | } 994 | 995 | for (var i = 0; i < e.getLength(); i++) { 996 | num[i] ^= QRMath.gexp(QRMath.glog(e.get(i) ) + ratio); 997 | } 998 | 999 | // recursive call 1000 | return qrPolynomial(num, 0).mod(e); 1001 | }; 1002 | 1003 | return _this; 1004 | }; 1005 | 1006 | //--------------------------------------------------------------------- 1007 | // QRRSBlock 1008 | //--------------------------------------------------------------------- 1009 | 1010 | var QRRSBlock = function() { 1011 | 1012 | var RS_BLOCK_TABLE = [ 1013 | 1014 | // L 1015 | // M 1016 | // Q 1017 | // H 1018 | 1019 | // 1 1020 | [1, 26, 19], 1021 | [1, 26, 16], 1022 | [1, 26, 13], 1023 | [1, 26, 9], 1024 | 1025 | // 2 1026 | [1, 44, 34], 1027 | [1, 44, 28], 1028 | [1, 44, 22], 1029 | [1, 44, 16], 1030 | 1031 | // 3 1032 | [1, 70, 55], 1033 | [1, 70, 44], 1034 | [2, 35, 17], 1035 | [2, 35, 13], 1036 | 1037 | // 4 1038 | [1, 100, 80], 1039 | [2, 50, 32], 1040 | [2, 50, 24], 1041 | [4, 25, 9], 1042 | 1043 | // 5 1044 | [1, 134, 108], 1045 | [2, 67, 43], 1046 | [2, 33, 15, 2, 34, 16], 1047 | [2, 33, 11, 2, 34, 12], 1048 | 1049 | // 6 1050 | [2, 86, 68], 1051 | [4, 43, 27], 1052 | [4, 43, 19], 1053 | [4, 43, 15], 1054 | 1055 | // 7 1056 | [2, 98, 78], 1057 | [4, 49, 31], 1058 | [2, 32, 14, 4, 33, 15], 1059 | [4, 39, 13, 1, 40, 14], 1060 | 1061 | // 8 1062 | [2, 121, 97], 1063 | [2, 60, 38, 2, 61, 39], 1064 | [4, 40, 18, 2, 41, 19], 1065 | [4, 40, 14, 2, 41, 15], 1066 | 1067 | // 9 1068 | [2, 146, 116], 1069 | [3, 58, 36, 2, 59, 37], 1070 | [4, 36, 16, 4, 37, 17], 1071 | [4, 36, 12, 4, 37, 13], 1072 | 1073 | // 10 1074 | [2, 86, 68, 2, 87, 69], 1075 | [4, 69, 43, 1, 70, 44], 1076 | [6, 43, 19, 2, 44, 20], 1077 | [6, 43, 15, 2, 44, 16] 1078 | ]; 1079 | 1080 | var qrRSBlock = function(totalCount, dataCount) { 1081 | var _this = {}; 1082 | _this.totalCount = totalCount; 1083 | _this.dataCount = dataCount; 1084 | return _this; 1085 | }; 1086 | 1087 | var _this = {}; 1088 | 1089 | var getRsBlockTable = function(typeNumber, errorCorrectLevel) { 1090 | 1091 | switch(errorCorrectLevel) { 1092 | case QRErrorCorrectLevel.L : 1093 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0]; 1094 | case QRErrorCorrectLevel.M : 1095 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1]; 1096 | case QRErrorCorrectLevel.Q : 1097 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2]; 1098 | case QRErrorCorrectLevel.H : 1099 | return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3]; 1100 | default : 1101 | return undefined; 1102 | } 1103 | }; 1104 | 1105 | _this.getRSBlocks = function(typeNumber, errorCorrectLevel) { 1106 | 1107 | var rsBlock = getRsBlockTable(typeNumber, errorCorrectLevel); 1108 | 1109 | if (typeof rsBlock == 'undefined') { 1110 | throw new Error('bad rs block @ typeNumber:' + typeNumber + 1111 | '/errorCorrectLevel:' + errorCorrectLevel); 1112 | } 1113 | 1114 | var length = rsBlock.length / 3; 1115 | 1116 | var list = []; 1117 | 1118 | for (var i = 0; i < length; i++) { 1119 | 1120 | var count = rsBlock[i * 3 + 0]; 1121 | var totalCount = rsBlock[i * 3 + 1]; 1122 | var dataCount = rsBlock[i * 3 + 2]; 1123 | 1124 | for (var j = 0; j < count; j++) { 1125 | list.push(qrRSBlock(totalCount, dataCount) ); 1126 | } 1127 | } 1128 | 1129 | return list; 1130 | }; 1131 | 1132 | return _this; 1133 | }(); 1134 | 1135 | //--------------------------------------------------------------------- 1136 | // qrBitBuffer 1137 | //--------------------------------------------------------------------- 1138 | 1139 | var qrBitBuffer = function() { 1140 | 1141 | var _buffer = []; 1142 | var _length = 0; 1143 | 1144 | var _this = {}; 1145 | 1146 | _this.getBuffer = function() { 1147 | return _buffer; 1148 | }; 1149 | 1150 | _this.get = function(index) { 1151 | var bufIndex = Math.floor(index / 8); 1152 | return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1; 1153 | }; 1154 | 1155 | _this.put = function(num, length) { 1156 | for (var i = 0; i < length; i++) { 1157 | _this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1); 1158 | } 1159 | }; 1160 | 1161 | _this.getLengthInBits = function() { 1162 | return _length; 1163 | }; 1164 | 1165 | _this.putBit = function(bit) { 1166 | 1167 | var bufIndex = Math.floor(_length / 8); 1168 | if (_buffer.length <= bufIndex) { 1169 | _buffer.push(0); 1170 | } 1171 | 1172 | if (bit) { 1173 | _buffer[bufIndex] |= (0x80 >>> (_length % 8) ); 1174 | } 1175 | 1176 | _length++; 1177 | }; 1178 | 1179 | return _this; 1180 | }; 1181 | 1182 | //--------------------------------------------------------------------- 1183 | // qr8BitByte 1184 | //--------------------------------------------------------------------- 1185 | 1186 | var qr8BitByte = function(data) { 1187 | 1188 | var _mode = QRMode.MODE_8BIT_BYTE; 1189 | var _data = data; 1190 | var _bytes = qrcode.stringToBytes(data); 1191 | 1192 | var _this = {}; 1193 | 1194 | _this.getMode = function() { 1195 | return _mode; 1196 | }; 1197 | 1198 | _this.getLength = function(buffer) { 1199 | return _bytes.length; 1200 | }; 1201 | 1202 | _this.write = function(buffer) { 1203 | for (var i = 0; i < _bytes.length; i++) { 1204 | buffer.put(_bytes[i], 8); 1205 | } 1206 | }; 1207 | 1208 | return _this; 1209 | }; 1210 | 1211 | //===================================================================== 1212 | // GIF Support etc. 1213 | // 1214 | 1215 | //--------------------------------------------------------------------- 1216 | // byteArrayOutputStream 1217 | //--------------------------------------------------------------------- 1218 | 1219 | var byteArrayOutputStream = function() { 1220 | 1221 | var _bytes = []; 1222 | 1223 | var _this = {}; 1224 | 1225 | _this.writeByte = function(b) { 1226 | _bytes.push(b & 0xff); 1227 | }; 1228 | 1229 | _this.writeShort = function(i) { 1230 | _this.writeByte(i); 1231 | _this.writeByte(i >>> 8); 1232 | }; 1233 | 1234 | _this.writeBytes = function(b, off, len) { 1235 | off = off || 0; 1236 | len = len || b.length; 1237 | for (var i = 0; i < len; i++) { 1238 | _this.writeByte(b[i + off]); 1239 | } 1240 | }; 1241 | 1242 | _this.writeString = function(s) { 1243 | for (var i = 0; i < s.length; i++) { 1244 | _this.writeByte(s.charCodeAt(i) ); 1245 | } 1246 | }; 1247 | 1248 | _this.toByteArray = function() { 1249 | return _bytes; 1250 | }; 1251 | 1252 | _this.toString = function() { 1253 | var s = ''; 1254 | s += '['; 1255 | for (var i = 0; i < _bytes.length; i++) { 1256 | if (i > 0) { 1257 | s += ','; 1258 | } 1259 | s += _bytes[i]; 1260 | } 1261 | s += ']'; 1262 | return s; 1263 | }; 1264 | 1265 | return _this; 1266 | }; 1267 | 1268 | //--------------------------------------------------------------------- 1269 | // base64EncodeOutputStream 1270 | //--------------------------------------------------------------------- 1271 | 1272 | var base64EncodeOutputStream = function() { 1273 | 1274 | var _buffer = 0; 1275 | var _buflen = 0; 1276 | var _length = 0; 1277 | var _base64 = ''; 1278 | 1279 | var _this = {}; 1280 | 1281 | var writeEncoded = function(b) { 1282 | _base64 += String.fromCharCode(encode(b & 0x3f) ); 1283 | }; 1284 | 1285 | var encode = function(n) { 1286 | if (n < 0) { 1287 | // error. 1288 | } else if (n < 26) { 1289 | return 0x41 + n; 1290 | } else if (n < 52) { 1291 | return 0x61 + (n - 26); 1292 | } else if (n < 62) { 1293 | return 0x30 + (n - 52); 1294 | } else if (n == 62) { 1295 | return 0x2b; 1296 | } else if (n == 63) { 1297 | return 0x2f; 1298 | } 1299 | throw new Error('n:' + n); 1300 | }; 1301 | 1302 | _this.writeByte = function(n) { 1303 | 1304 | _buffer = (_buffer << 8) | (n & 0xff); 1305 | _buflen += 8; 1306 | _length++; 1307 | 1308 | while (_buflen >= 6) { 1309 | writeEncoded(_buffer >>> (_buflen - 6) ); 1310 | _buflen -= 6; 1311 | } 1312 | }; 1313 | 1314 | _this.flush = function() { 1315 | 1316 | if (_buflen > 0) { 1317 | writeEncoded(_buffer << (6 - _buflen) ); 1318 | _buffer = 0; 1319 | _buflen = 0; 1320 | } 1321 | 1322 | if (_length % 3 != 0) { 1323 | // padding 1324 | var padlen = 3 - _length % 3; 1325 | for (var i = 0; i < padlen; i++) { 1326 | _base64 += '='; 1327 | } 1328 | } 1329 | }; 1330 | 1331 | _this.toString = function() { 1332 | return _base64; 1333 | }; 1334 | 1335 | return _this; 1336 | }; 1337 | 1338 | //--------------------------------------------------------------------- 1339 | // base64DecodeInputStream 1340 | //--------------------------------------------------------------------- 1341 | 1342 | var base64DecodeInputStream = function(str) { 1343 | 1344 | var _str = str; 1345 | var _pos = 0; 1346 | var _buffer = 0; 1347 | var _buflen = 0; 1348 | 1349 | var _this = {}; 1350 | 1351 | _this.read = function() { 1352 | 1353 | while (_buflen < 8) { 1354 | 1355 | if (_pos >= _str.length) { 1356 | if (_buflen == 0) { 1357 | return -1; 1358 | } 1359 | throw new Error('unexpected end of file./' + _buflen); 1360 | } 1361 | 1362 | var c = _str.charAt(_pos); 1363 | _pos++; 1364 | 1365 | if (c == '=') { 1366 | _buflen = 0; 1367 | return -1; 1368 | } else if (c.match(/^\s$/) ) { 1369 | // ignore if whitespace. 1370 | continue; 1371 | } 1372 | 1373 | _buffer = (_buffer << 6) | decode(c.charCodeAt(0) ); 1374 | _buflen += 6; 1375 | } 1376 | 1377 | var n = (_buffer >>> (_buflen - 8) ) & 0xff; 1378 | _buflen -= 8; 1379 | return n; 1380 | }; 1381 | 1382 | var decode = function(c) { 1383 | if (0x41 <= c && c <= 0x5a) { 1384 | return c - 0x41; 1385 | } else if (0x61 <= c && c <= 0x7a) { 1386 | return c - 0x61 + 26; 1387 | } else if (0x30 <= c && c <= 0x39) { 1388 | return c - 0x30 + 52; 1389 | } else if (c == 0x2b) { 1390 | return 62; 1391 | } else if (c == 0x2f) { 1392 | return 63; 1393 | } else { 1394 | throw new Error('c:' + c); 1395 | } 1396 | }; 1397 | 1398 | return _this; 1399 | }; 1400 | 1401 | //--------------------------------------------------------------------- 1402 | // gifImage (B/W) 1403 | //--------------------------------------------------------------------- 1404 | 1405 | var gifImage = function(width, height) { 1406 | 1407 | var _width = width; 1408 | var _height = height; 1409 | var _data = []; 1410 | 1411 | var _this = {}; 1412 | 1413 | _this.setPixel = function(x, y, pixel) { 1414 | _data[y * _width + x] = pixel; 1415 | }; 1416 | 1417 | _this.write = function(out) { 1418 | 1419 | //--------------------------------- 1420 | // GIF Signature 1421 | 1422 | out.writeString('GIF87a'); 1423 | 1424 | //--------------------------------- 1425 | // Screen Descriptor 1426 | 1427 | out.writeShort(_width); 1428 | out.writeShort(_height); 1429 | 1430 | out.writeByte(0x80); // 2bit 1431 | out.writeByte(0); 1432 | out.writeByte(0); 1433 | 1434 | //--------------------------------- 1435 | // Global Color Map 1436 | 1437 | // black 1438 | out.writeByte(0x00); 1439 | out.writeByte(0x00); 1440 | out.writeByte(0x00); 1441 | 1442 | // white 1443 | out.writeByte(0xff); 1444 | out.writeByte(0xff); 1445 | out.writeByte(0xff); 1446 | 1447 | //--------------------------------- 1448 | // Image Descriptor 1449 | 1450 | out.writeString(','); 1451 | out.writeShort(0); 1452 | out.writeShort(0); 1453 | out.writeShort(_width); 1454 | out.writeShort(_height); 1455 | out.writeByte(0); 1456 | 1457 | //--------------------------------- 1458 | // Local Color Map 1459 | 1460 | //--------------------------------- 1461 | // Raster Data 1462 | 1463 | var lzwMinCodeSize = 2; 1464 | var raster = getLZWRaster(lzwMinCodeSize); 1465 | 1466 | out.writeByte(lzwMinCodeSize); 1467 | 1468 | var offset = 0; 1469 | 1470 | while (raster.length - offset > 255) { 1471 | out.writeByte(255); 1472 | out.writeBytes(raster, offset, 255); 1473 | offset += 255; 1474 | } 1475 | 1476 | out.writeByte(raster.length - offset); 1477 | out.writeBytes(raster, offset, raster.length - offset); 1478 | out.writeByte(0x00); 1479 | 1480 | //--------------------------------- 1481 | // GIF Terminator 1482 | out.writeString(';'); 1483 | }; 1484 | 1485 | var bitOutputStream = function(out) { 1486 | 1487 | var _out = out; 1488 | var _bitLength = 0; 1489 | var _bitBuffer = 0; 1490 | 1491 | var _this = {}; 1492 | 1493 | _this.write = function(data, length) { 1494 | 1495 | if ( (data >>> length) != 0) { 1496 | throw new Error('length over'); 1497 | } 1498 | 1499 | while (_bitLength + length >= 8) { 1500 | _out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) ); 1501 | length -= (8 - _bitLength); 1502 | data >>>= (8 - _bitLength); 1503 | _bitBuffer = 0; 1504 | _bitLength = 0; 1505 | } 1506 | 1507 | _bitBuffer = (data << _bitLength) | _bitBuffer; 1508 | _bitLength = _bitLength + length; 1509 | }; 1510 | 1511 | _this.flush = function() { 1512 | if (_bitLength > 0) { 1513 | _out.writeByte(_bitBuffer); 1514 | } 1515 | }; 1516 | 1517 | return _this; 1518 | }; 1519 | 1520 | var getLZWRaster = function(lzwMinCodeSize) { 1521 | 1522 | var clearCode = 1 << lzwMinCodeSize; 1523 | var endCode = (1 << lzwMinCodeSize) + 1; 1524 | var bitLength = lzwMinCodeSize + 1; 1525 | 1526 | // Setup LZWTable 1527 | var table = lzwTable(); 1528 | 1529 | for (var i = 0; i < clearCode; i++) { 1530 | table.add(String.fromCharCode(i) ); 1531 | } 1532 | table.add(String.fromCharCode(clearCode) ); 1533 | table.add(String.fromCharCode(endCode) ); 1534 | 1535 | var byteOut = byteArrayOutputStream(); 1536 | var bitOut = bitOutputStream(byteOut); 1537 | 1538 | // clear code 1539 | bitOut.write(clearCode, bitLength); 1540 | 1541 | var dataIndex = 0; 1542 | 1543 | var s = String.fromCharCode(_data[dataIndex]); 1544 | dataIndex++; 1545 | 1546 | while (dataIndex < _data.length) { 1547 | 1548 | var c = String.fromCharCode(_data[dataIndex]); 1549 | dataIndex++; 1550 | 1551 | if (table.contains(s + c) ) { 1552 | 1553 | s = s + c; 1554 | 1555 | } else { 1556 | 1557 | bitOut.write(table.indexOf(s), bitLength); 1558 | 1559 | if (table.size() < 0xfff) { 1560 | 1561 | if (table.size() == (1 << bitLength) ) { 1562 | bitLength++; 1563 | } 1564 | 1565 | table.add(s + c); 1566 | } 1567 | 1568 | s = c; 1569 | } 1570 | } 1571 | 1572 | bitOut.write(table.indexOf(s), bitLength); 1573 | 1574 | // end code 1575 | bitOut.write(endCode, bitLength); 1576 | 1577 | bitOut.flush(); 1578 | 1579 | return byteOut.toByteArray(); 1580 | }; 1581 | 1582 | var lzwTable = function() { 1583 | 1584 | var _map = {}; 1585 | var _size = 0; 1586 | 1587 | var _this = {}; 1588 | 1589 | _this.add = function(key) { 1590 | if (_this.contains(key) ) { 1591 | throw new Error('dup key:' + key); 1592 | } 1593 | _map[key] = _size; 1594 | _size++; 1595 | }; 1596 | 1597 | _this.size = function() { 1598 | return _size; 1599 | }; 1600 | 1601 | _this.indexOf = function(key) { 1602 | return _map[key]; 1603 | }; 1604 | 1605 | _this.contains = function(key) { 1606 | return typeof _map[key] != 'undefined'; 1607 | }; 1608 | 1609 | return _this; 1610 | }; 1611 | 1612 | return _this; 1613 | }; 1614 | 1615 | var createImgTag = function(width, height, getPixel, alt) { 1616 | 1617 | var gif = gifImage(width, height); 1618 | for (var y = 0; y < height; y++) { 1619 | for (var x = 0; x < width; x++) { 1620 | gif.setPixel(x, y, getPixel(x, y) ); 1621 | } 1622 | } 1623 | 1624 | var b = byteArrayOutputStream(); 1625 | gif.write(b); 1626 | 1627 | var base64 = base64EncodeOutputStream(); 1628 | var bytes = b.toByteArray(); 1629 | for (var i = 0; i < bytes.length; i++) { 1630 | base64.writeByte(bytes[i]); 1631 | } 1632 | base64.flush(); 1633 | 1634 | var img = ''; 1635 | img += '