瀏覽代碼

Initial commit v.1.0.0

Javier Jiménez Villar 14 年之前
當前提交
de5f4b86b0
共有 100 個文件被更改,包括 16204 次插入0 次删除
  1. 265 0
      LICENSE.txt
  2. 41 0
      README.md
  3. 12 0
      examples/test_src/app/app.js
  4. 17 0
      examples/test_src/app/data.js
  5. 51 0
      examples/test_src/app/events.js
  6. 35 0
      examples/test_src/app/services.js
  7. 28 0
      examples/test_src/app/view.js
  8. 二進制
      examples/test_src/assets/images/default.png
  9. 二進制
      examples/test_src/assets/images/icon-72.png
  10. 二進制
      examples/test_src/assets/images/icon.png
  11. 二進制
      examples/test_src/assets/images/icon@2x.png
  12. 44 0
      examples/test_src/assets/stylesheets/app.css
  13. 326 0
      examples/test_src/index.html
  14. 14 0
      examples/todo.js/app/app.js
  15. 76 0
      examples/todo.js/app/data.js
  16. 70 0
      examples/todo.js/app/events.js
  17. 7 0
      examples/todo.js/app/services.js
  18. 53 0
      examples/todo.js/app/view.js
  19. 二進制
      examples/todo.js/assets/images/default.png
  20. 二進制
      examples/todo.js/assets/images/icon-72.png
  21. 二進制
      examples/todo.js/assets/images/icon.png
  22. 二進制
      examples/todo.js/assets/images/icon@2x.png
  23. 24 0
      examples/todo.js/assets/sugars/lungo.sugar.growl.css
  24. 171 0
      examples/todo.js/assets/sugars/lungo.sugar.growl.js
  25. 143 0
      examples/todo.js/index.html
  26. 9 0
      examples/todo.js/todo.appcache
  27. 18 0
      package.json
  28. 1880 0
      release/lungo-1.0.0.js
  29. 24 0
      release/lungo-1.0.0.min.css
  30. 24 0
      release/lungo-1.0.0.min.js
  31. 113 0
      release/lungo-1.0.0.packed.js
  32. 217 0
      release/lungo.theme.default.css
  33. 9 0
      spec/Lungo.Core.Spec.js
  34. 11 0
      spec/Lungo.Spec.js
  35. 57 0
      spec/SpecRunner.html
  36. 44 0
      src/Lungo.App.js
  37. 147 0
      src/Lungo.Core.js
  38. 72 0
      src/Lungo.Environment.js
  39. 53 0
      src/Lungo.Events.js
  40. 72 0
      src/Lungo.Service.js
  41. 31 0
      src/Lungo.js
  42. 32 0
      src/attributes/Lungo.Attributes.Data.js
  43. 20 0
      src/attributes/Lungo.Attributes.Section.js
  44. 54 0
      src/boot/Lungo.Boot.Article.js
  45. 45 0
      src/boot/Lungo.Boot.Data.js
  46. 68 0
      src/boot/Lungo.Boot.Events.js
  47. 78 0
      src/boot/Lungo.Boot.Section.js
  48. 22 0
      src/boot/Lungo.Boot.js
  49. 83 0
      src/data/Lungo.Data.Cache.js
  50. 203 0
      src/data/Lungo.Data.Sql.js
  51. 17 0
      src/data/Lungo.Data.Storage.js
  52. 150 0
      src/dom/Lungo.Dom.Event.js
  53. 29 0
      src/dom/Lungo.Dom.js
  54. 1044 0
      src/lib/iscroll.js
  55. 1193 0
      src/lib/zepto.js
  56. 54 0
      src/router/Lungo.Router.History.js
  57. 69 0
      src/router/Lungo.Router.js
  58. 6 0
      src/stylesheets/Lungo.icon.css
  59. 二進制
      src/stylesheets/Lungo.icon.otf
  60. 131 0
      src/stylesheets/base.css
  61. 257 0
      src/stylesheets/layout.css
  62. 195 0
      src/stylesheets/lungo.theme.default.css
  63. 402 0
      src/stylesheets/widgets.css
  64. 56 0
      src/view/Lungo.View.Article.js
  65. 87 0
      src/view/Lungo.View.Resize.js
  66. 136 0
      src/view/Lungo.View.Scroll.js
  67. 92 0
      src/view/Lungo.View.Template.Binding.js
  68. 83 0
      src/view/Lungo.View.Template.List.js
  69. 58 0
      src/view/Lungo.View.Template.js
  70. 137 0
      vendor/build.sh
  71. 202 0
      vendor/google-compiler/COPYING
  72. 289 0
      vendor/google-compiler/README
  73. 二進制
      vendor/google-compiler/compiler.jar
  74. 20 0
      vendor/jasmine-1.1.0/MIT.LICENSE
  75. 190 0
      vendor/jasmine-1.1.0/jasmine-html.js
  76. 166 0
      vendor/jasmine-1.1.0/jasmine.css
  77. 2476 0
      vendor/jasmine-1.1.0/jasmine.js
  78. 二進制
      vendor/jasmine-1.1.0/jasmine_favicon.png
  79. 131 0
      vendor/node-jasmine-dom/README.md
  80. 186 0
      vendor/node-jasmine-dom/bin/jasmine-dom
  81. 7 0
      vendor/node-jasmine-dom/examples/config.yaml
  82. 36 0
      vendor/node-jasmine-dom/examples/runner.html
  83. 28 0
      vendor/node-jasmine-dom/examples/runner2.html
  84. 7 0
      vendor/node-jasmine-dom/examples/src/example-dom.js
  85. 5 0
      vendor/node-jasmine-dom/examples/src/example-functions-2.js
  86. 5 0
      vendor/node-jasmine-dom/examples/src/example-functions.js
  87. 18 0
      vendor/node-jasmine-dom/examples/tests/helper/jquery.js
  88. 183 0
      vendor/node-jasmine-dom/examples/tests/lib/jasmine-html.js
  89. 167 0
      vendor/node-jasmine-dom/examples/tests/lib/jasmine.css
  90. 2421 0
      vendor/node-jasmine-dom/examples/tests/lib/jasmine.js
  91. 9 0
      vendor/node-jasmine-dom/examples/tests/spec/example-dom_spec.js
  92. 12 0
      vendor/node-jasmine-dom/examples/tests/spec/example-functions-2_spec.js
  93. 12 0
      vendor/node-jasmine-dom/examples/tests/spec/example-functions_spec.js
  94. 174 0
      vendor/node-jasmine-dom/lib/jasmine-dom/index.js
  95. 110 0
      vendor/node-jasmine-dom/lib/jasmine-dom/reporter-agregator.js
  96. 45 0
      vendor/node-jasmine-dom/lib/jasmine-dom/reporter-html.js
  97. 72 0
      vendor/node-jasmine-dom/lib/jasmine-dom/reporter-junit.js
  98. 91 0
      vendor/node-jasmine-dom/lib/jasmine-dom/reporter-simple.js
  99. 183 0
      vendor/node-jasmine-dom/lib/jasmine-dom/resources/jasmine-html.js
  100. 0 0
      vendor/node-jasmine-dom/lib/jasmine-dom/resources/jasmine.css

+ 265 - 0
LICENSE.txt

@@ -0,0 +1,265 @@
+Copyright (c) 2011 TapQuo Inc (Javier Jimenez Villar, Guillermo Pascual)
+
+===============================================================================
+===============================================================================
+
+OPEN SOURCE LICENSE FOR LUNGOJS
+Version 1.0
+
+LungoJS is an avid supporter of open source software. This is the appropriate 
+option if you are creating an open source application with a license compatible 
+with the GNU GPL license v3 (http://www.gnu.org/copyleft/gpl.html). Although 
+the GPLv3 has many terms, the most important is that you must provide the 
+source code of your application to your users so they can be free to modify 
+your application for their own needs.
+
+-------------------------------------------------------------------------------
+
+The MIT License
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+
+===============================================================================
+===============================================================================
+
+COMMERCIAL SOFTWARE LICENSE FOR LUNGOJS
+Version 1.0
+
+THIS DOCUMENT IS A LEGAL AGREEMENT (the “License Agreement”) BETWEEN TAPQUO INC. 
+(“We,” “Us”) AND YOU OR THE ORGANIZATION ON WHOSE BEHALF YOU ARE UNDERTAKING 
+THE LICENSE DESCRIBED BELOW (“You”) IN RELATION TO THE LUNGOJS SOFTWARE 
+LIBRARY (THE “Software”), AND/OR ALL RELATED MATERIALS. BY DOWNLOADING, 
+INSTALLING, COPYING OR OTHERWISE USING THE SOFTWARE, YOU ACCEPT THE FOLLOWING
+TERMS AND CONDITIONS. IF YOU DO NOT AGREE WITH ANY OF THE TERMS OR CONDITIONS 
+OF THIS LICENSE AGREEMENT, DO NOT PROCEED WITH THE DOWNLOADING, COPYING, 
+INSTALLATION OR ANY OTHER USE OF THE SOFTWARE OR ANY PORTION THEREOF. THE 
+SOFTWARE IS PROTECTED BY UNITED STATES COPYRIGHT LAWS AND INTERNATIONAL 
+COPYRIGHT LAWS, AS WELL AS OTHER INTELLECTUAL PROPERTY LAWS AND TREATIES. 
+THE SOFTWARE IS LICENSED, NOT SOLD.
+
+THIS LICENSE AGREEMENT DESCRIBES YOUR RIGHTS WITH RESPECT TO THE SOFTWARE 
+AND ITS COMPONENTS.
+
+1. OWNERSHIP, LICENSE GRANT
+
+This is a license agreement and not an agreement for sale. We reserve ownership
+of all intellectual property rights inherent in or relating to the Software, 
+which include, but are not limited to, all copyright, patent rights, all rights
+in relation to registered and unregistered trademarks (including service marks),
+confidential information (including trade secrets and know-how) and all
+rights other than those expressly granted by this Agreement.
+
+Subject to the terms and conditions of this License Agreement, We grant to You 
+a revocable, non-transferable, non-exclusive license (i) for a Designated 
+User(s) (as defined below) within Your organization to install and use the 
+Software on any workstations used exclusively by such Designated User(s) and 
+(ii) for You to install and use the Software in connection with unlimited 
+domains and sub-domains on unlimited servers, solely in connection with 
+distribution of the Software in accordance with sections 3 and 4 below. This 
+license is not sublicensable except as explicitly set forth herein. 
+“Designated User(s)” shall mean Your employee(s) acting within the scope of 
+their employment or Your consultant(s) or contractor(s) acting within the scope
+of the services they provide for You or on Your behalf.
+
+2. PERMITTED USES, SOURCE CODE, MODIFICATIONS
+
+We provide You with source code so that You can create Modifications of the 
+original Software, where Modification means: a) any addition to or deletion 
+from the contents of a file included in the original Software or previous 
+Modifications created by You, or b) any new file that contains any part of the
+original Software or previous Modifications. While You retain all rights to any
+original work authored by You as part of the Modifications, We continue to own 
+all copyright and other intellectual property rights in the Software.
+
+3. DISTRIBUTION
+
+You may distribute the Software in any applications, frameworks, or elements (collectively referred
+to as “Applications”) that you develop using the Software in accordance with this License Agreement,
+provided that such distribution does not violate the restrictions set forth in section 4 of this
+agreement. You must not remove, obscure or interfere with any copyright, acknowledgment,
+attribution, trademark, warning or disclaimer statement affixed to, incorporated in or otherwise
+applied in connection with the Software.
+
+You are required to ensure that the Software is not reused by or with any applications other than
+those with which You distribute it as permitted herein. For example, if You install the Software on
+a customer’s server, that customer is not permitted to use the Software independently of Your
+application, and must be informed as such.
+
+You will not owe Us any royalties for Your distribution of the Software in accordance with this
+License Agreement.
+
+4. PROHIBITED USES
+
+You may not, without Our prior written consent, redistribute the Software or Modifications other
+than by including the Software or a portion thereof within Your own product, which must have
+substantially different functionality than the Software or Modifications and must not allow any
+third party to use the Software or Modifications, or any portions thereof, for software development
+or application development purposes. You are explicitly not allowed to redistribute the Software or
+Modifications as part of any product that can be described as a development toolkit or library or is
+intended for use by software developers or application developers and not end-users. You are not
+allowed to redistribute any part of the Software documentation.
+
+You may not: a) use any part of the Software or Modifications or Your knowledge of the Software (or
+any information that You learn as a result of Your use of the Software) to create a product with the
+same or substantially the same functionality as the Software; or b) change or remove the copyright
+notice from any of the files included in the Software or Modifications.
+
+UNDER NO CIRCUMSTANCES MAY YOU USE THE SOFTWARE AS THE BASIS FOR OR IN 
+CONNECTION WITH A PRODUCT THAT CONTAINS THE SAME,OR SUBSTANTIALLY THE SAME 
+FUNCTIONALITY AS THE SOFTWARE.
+
+The Open Source version of the Software (“GPL Version”) is licensed under the terms of the GNU
+General Public License versions 3.0 (“GPL”) and not under this Agreement. If You, or another third
+party, has, at any time, developed all (or any portions of) the Application(s) using the GPL
+Version, You may not combine such development work with the Software and must license such
+Application(s) (or any portions derived there from) under the terms of the GNU General Public
+License version 3, a copy of which is located at http://www.gnu.org/copyleft/gpl.html.
+
+5. TERMINATION
+
+This License Agreement and Your right to use the Software and Modifications will terminate
+immediately without notice if You fail to comply with the terms and conditions of this License
+Agreement. Upon termination, You agree to immediately cease using and destroy the Software or
+Modifications, including all accompanying documents. The provisions of sections 4, 5, 6, 7, 8, 9, 10
+and 12 will survive any termination of this License Agreement.
+
+6. DISCLAIMER OF WARRANTIES
+
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, WE AND OUR SUPPLIERS 
+DISCLAIM ALL WARRANTIES AND CONDITIONS, EITHER EXPRESS OR IMPLIED, INCLUDING, 
+BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
+PARTICULAR PURPOSE, AND TITLE AND NON-INFRINGEMENT, WITH REGARD TO THE 
+SOFTWARE. WE DO NOT GUARANTEE THAT THE OPERATION OF THE SOFTWARE WILL 
+BE UNINTERRUPTED OR ERROR-FREE, AND YOU ACKNOWLEDGE THAT IT IS NOT 
+TECHNICALLY PRACTICABLE FOR US TO DO SO.
+
+7. LIMITATION OF LIABILITIES
+
+TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL WE OR 
+OUR SUPPLIERS BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL 
+DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF BUSINESS 
+PROFITS, BUSINESS INTERRUPTION, LOSS OF BUSINESS INFORMATION OR ANY OTHER 
+PECUNIARY LAW) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN 
+IF WE HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. IN ANY CASE, OUR ENTIRE 
+LIABILITY UNDER ANY PROVISION OF THIS LICENSE AGREEMENT SHALL BE LIMITED TO THE 
+AMOUNT ACTUALLY PAID BY YOU FOR THE SOFTWARE.
+
+8. VERIFICATION
+
+We or a certified auditor acting on Our behalf, may, upon its reasonable request and at its expense,
+audit You with respect to the use of the Software. Such audit may be conducted by mail, electronic
+means or through an in-person visit to Your place of business. Any such in-person audit shall be
+conducted during regular business hours at Your facilities and shall not unreasonably interfere with
+Your business activities. We shall not remove, copy, or redistribute any electronic material during
+the course of an audit. If an audit reveals that You are using the Software in a way that is in
+material violation of the terms of the Agreement, then You shall pay Our reasonable costs of
+conducting the audit. In the case of a material violation, You agree to pay Us any amounts owing
+that are attributable to the unauthorized use. In the alternative, We reserve the right, at Our
+sole option, to terminate the licenses for the Software.
+
+9. THIRD PARTY SOFTWARE
+
+Examples included in Software may provide links to third party libraries or code (collectively
+“Third Party Software”) to implement various functions. Third Party Software does not comprise part
+of the Software. In some cases, access to Third Party Software may be included along with the
+Software delivery as a convenience for demonstration purposes. Such source code and libraries may be
+included in the “…/examples” source tree delivered with the Software and do not comprise the
+Software. Licensee acknowledges (1) that some part of Third Party Software may require additional
+licensing of copyright and patents from the owners of such, and (2) that distribution of any of the
+Software referencing or including any portion of a Third Party Software may require appropriate
+licensing from such third parties.
+
+10. PAYMENT AND TAXES
+
+If credit has been extended to You by Us, all payments under this Agreement are due within thirty
+(30) days of the date We mail an invoice to You. If We have not extended credit to You, You shall
+be required to make payment concurrent with the delivery of the Software by Us. All amounts payable
+are gross amounts but exclusive of any value added tax, use tax, sales tax or similar tax. You shall
+be entitled to withhold from payments any applicable withholding taxes and comply with all
+applicable tax and employment legislation. Each party shall pay all taxes (including, but not
+limited to, taxes based upon its income) or levies imposed on it under applicable laws, regulations
+and tax treaties as a result of this Agreement and any payments made hereunder (including those
+required to be withheld or deducted from payments). Each party shall furnish evidence of such paid
+taxes as is sufficient to enable the other party to obtain any credits available to it, including
+original withholding tax certificates.
+
+11. SUPPORT AND UPDATES
+
+You are not entitled to any support for the Software under this Agreement. All support must be
+purchased separately and will be subject to the terms and conditions contained in the Tapquo support
+agreement. You are entitled to receive minor version updates to the Software (i.e. versions
+identified as follows (X.Y, X.Y+1). You are not entitled to receive major version updates (i.e.
+X.Y, X+1.Y) or bug fix updates to the Software (X.Y.Z, X.Y.Z+1). Major version updates and bug fix
+updates to the Software are available separately for purchase.
+
+12 MISCELLANEOUS
+
+The license granted herein applies only to the version of the Software available when purchased in
+connection with the terms of this Agreement. Any previous or subsequent license granted to You for
+use of the Software shall be governed by the terms and conditions of the agreement entered in
+connection with purchase of that version of the Software. You agree that you will comply with all
+applicable laws and regulations with respect to the Software, including without limitation all
+export and re-export control laws and regulations.
+
+While redistributing the Software or Modifications thereof, You may choose to offer acceptance of
+support, warranty, indemnity, or other liability obligations and/or rights consistent with this
+Agreement. However, in accepting such obligations, You may act only on Your own behalf and on Your
+sole responsibility, not on our behalf. You agree to indemnify, defend, and hold Us harmless from
+and against any liability incurred by, or claims asserted against, Us (i) by reason of Your
+accepting any such support, warranty, indemnity or additional liability; or (ii) arising out of the
+use, reproduction or distribution of Your Application, except to the extent such claim is solely
+based on the inclusion of the Software therein.
+
+You agree to be identified as a customer of ours and You agree that We may refer to You by name,
+trade name and trademark, if applicable, and may briefly describe Your business in our marketing
+materials and web site.
+
+You may not assign this License Agreement without Our prior written consent, which will not be
+unreasonably withheld. This License Agreement will inure to the benefit of Our successors and
+assigns.
+
+You acknowledge that this License Agreement is complete and is the exclusive representation of our
+agreement. No oral or written information given by Us or on our behalf shall create a warranty or
+collateral contract, or in any way increase the scope of this License Agreement in any way, and You
+may not rely on any such oral or written information. No term or condition contained in any purchase
+order shall apply unless expressly accepted by Us in writing,
+
+There are no implied licenses or other implied rights granted under this Agreement, and all rights,
+save for those expressly granted hereunder, shall remain with Us and our licensors. In addition, no
+licenses or immunities are granted to the combination of the Software and/or Modifications, as
+applicable, with any other software or hardware not delivered by Us to You under this Agreement.
+
+If any provision in this License Agreement shall be determined to be invalid, such provision shall
+be deemed omitted; the remainder of this License Agreement shall continue in full force and effect.
+If any remedy provided is determined to have failed for its essential purpose, all limitations of
+liability and exclusions of damages set forth in this Agreement shall remain in effect.
+
+This License Agreement may be modified only by a written instrument signed by an authorized
+representative of each party.
+
+This Agreement is governed by the law of the State of California, United States (notwithstanding
+conflicts of laws provisions), and all parties irrevocably submit to the jurisdiction of the courts
+of the State of California and further agree to commence any litigation which may arise hereunder in
+the state or federal courts located in the judicial district of Santa Clara County, California, US.
+
+If the Software or any related documentation is licensed to the U.S. government or any agency
+thereof, it will be deemed to be “commercial computer software” or “commercial computer software
+documentation,” pursuant to SFAR Section 227.7202 and FAR Section 12.212. Any use of the Software or
+related documentation by the U.S. government will be governed solely by the terms of this License
+Agreement.

+ 41 - 0
README.md

@@ -0,0 +1,41 @@
+# LungoJS
+### HTML5 Mobile Framework, and stuff.
+
+## Create powerful mobile apps with just HTML5, CSS3, and JavaScript.
+Meet the first Mobile Framework that uses the actual features of #HTML5, #CSS3 & #JavaScript
+
+- Design and make applications for iOS, Android, Blackberry and Windows Phone 7.
+- Designed to take advantage of the features of current mobile devices.
+- Capture events like Swipe, Tap, Double-Tap...
+- Distribute your app in "Mobile Stores" or in websites.
+- No need images, everything is vector.
+- Implement HTML5 features like WebSQL, Geolocation, History, Device Orientation and more.
+- No webserver required.
+- Implement native looking with (on iPad & iPhone).
+- Fully customizable.
+- You can use Lungo Sugars to sweeten your apps.
+- Build apps, games, catalogues and everything else you can imagine.
+
+
+[Source code](https://github.com/TapQuo/Lungo.js) and [issue tracking](http://github.com/TapQuo/Lungo.js/issues) are available on Github.
+
+## Learn more about the project
+The idea of LungoJS arose earlier this year, the author [Javi Jiménez Villar](http://twitter.com/soyjavi) saw hot existing Mobile Frameworks at that time were not powerful and not using the features of HTML5. If you want to know more about the project you have some interesting links:
+
+- Visit LungoJS [Site](http://www.lungojs.com/).
+- [How to use](http://www.lungojs.com/how-to-use/)
+- [JavaScript API](http://www.lungojs.com/api/) 
+- [About](http://www.lungojs.com/about/) the author and contributors.
+- [Watch Screencasts video preview](http://www.vimeo.com/) to see it in action.
+
+## Credits
+Created by [Javier Jiménez](http://twitter.com/soyjavi).
+Maintained by [Javier Jiménez](http://twitter.com/soyjavi) & [Guillermo Pascual](http://twitter.com/pasku1).
+
+Copyright (c) 2011 by Tapquo Inc.
+
+## Licensing Options
+LungoJS is licensed under free commercial and open source licenses for 
+application development, and a paid commercial license for OEM uses.
+
+See LICENSE.txt for license.

+ 12 - 0
examples/test_src/app/app.js

@@ -0,0 +1,12 @@
+var App = (function(lng, undefined) {
+
+    lng.App.init({
+        name: 'LungoJS Test Source',
+        version: '1.0.0'
+    });
+
+    return {
+
+    };
+
+})(LUNGO);

+ 17 - 0
examples/test_src/app/data.js

@@ -0,0 +1,17 @@
+App.Data = (function(lng, App, undefined) {
+
+    lng.Data.Sql.init({
+        name: 'lungo.js',
+        version: '1.0',
+        schema: [
+            { name: 'demo', drop: true, fields: {  
+                id: 'INTEGER PRIMARY KEY',
+                name: 'TEXT',
+                done: 'INTEGER DEFAULT 0',
+                created_at: 'DATETIME' 
+                }
+            }
+        ]
+    });
+               
+})(LUNGO, App);

+ 51 - 0
examples/test_src/app/events.js

@@ -0,0 +1,51 @@
+App.Events = (function(lng, $, undefined) {
+
+    lng.Dom.Event.live('#event_touchstart', 'TOUCH_START', function(event) {
+        alert("TOUCH_START!");
+    });
+    
+    lng.Dom.Event.live('#event_touchend', 'TOUCH_END', function(event) {
+        alert("TOUCH_START!");
+    });
+    
+    lng.Dom.Event.live('#event_touchmove', 'TOUCH_MOVE', function(event) {
+        alert("TOUCH_MOVE!");
+    });
+    
+    lng.Dom.Event.live('#event_tap', 'TAP', function(event) {
+        alert("TAP!");
+    });
+    
+    lng.Dom.Event.live('#event_doubletap', 'DOUBLE_TAP', function(event) {
+        alert("DOUBLE_TAP!");
+    });
+    
+    lng.Dom.Event.live('#swipe', 'SWIPE', function(event) {
+        alert("SWIPE!");
+    });
+    
+    lng.Dom.Event.live('#swipe_left', 'SWIPE_LEFT', function(event) {
+        alert("SWIPE_LEFT!");
+    });
+    
+    lng.Dom.Event.live('#swipe_right', 'SWIPE_RIGHT', function(event) {
+        alert("SWIPE_RIGHT!");
+    });
+    
+    lng.Dom.Event.live('#swipe_up', 'SWIPE_UP', function(event) {
+        alert("SWIPE_UP!");
+    });
+    
+    lng.Dom.Event.live('#swipe_down', 'SWIPE_DOWN', function(event) {
+        alert("SWIPE_DOWN!");
+    });
+
+    lng.Dom.Event.delegate('body', '#load_list', 'TAP', function(event) {
+        App.Services.getMockList();
+    });
+
+    lng.Dom.Event.live('#load_scroll_mocks', 'TAP', function(event) {
+        App.View.scroll_mockup();
+    });
+
+})(LUNGO, Zepto);

+ 35 - 0
examples/test_src/app/services.js

@@ -0,0 +1,35 @@
+App.Services = (function(lng, App, undefined) {
+    
+    
+    var getMockList = function(){
+        var mock = new Array(); 
+        for (var i=1; i <= 6; i++) {
+            mock.push({id:i, name:'name nº'+i, description:'description nº'+i, avatar:'resources/images/avatar.jpg'});
+        }
+        for (var i=7; i <= 12; i++) {
+            mock.push({id:i, name:'surname nº'+i, description:'description nº'+i});
+        }
+        for (var i=13; i <= 32; i++) {
+            mock.push({id:i, name:'name nº'+i, description:'description nº'+i});
+        }
+
+        lng.View.Template.List.create({
+            container_id: 'list_sample',
+            template_id: 'list-tmp',
+            data: mock
+        });
+
+        lng.View.Template.List.create({
+            container_id: 'list_grouped_sample',
+            template_id: 'list-tmp',
+            data: mock,
+            order_field: 'name',
+            order_type: 'desc'
+        });
+    }
+
+    return {
+        getMockList: getMockList
+    }
+
+})(LUNGO, App);

+ 28 - 0
examples/test_src/app/view.js

@@ -0,0 +1,28 @@
+App.View = (function(lng, App, undefined) {
+
+    lng.View.Template.create(
+        'list-tmp', 
+        '<li class="{{anchor}}">\
+            <a href="#">\
+                <div class="bubble articblue onright">{{id}} €</div>\
+                <strong>{{name}}{{anchor_name}}</strong>\
+                <small>{{description}}</small>\
+            </a>\
+        </li>'
+    );
+
+    var scroll_mockup = function(){
+        var _markup = '';
+        for (var i=0; i < 32; i++) {
+            _markup += '<li>'+i+'</li>';
+        }
+
+        lng.View.Scroll.update('scroll_vertical', _markup);
+        lng.View.Scroll.update('scroll_horizontal', _markup);
+    }
+
+    return{
+        scroll_mockup: scroll_mockup
+    }
+
+})(LUNGO, App);

二進制
examples/test_src/assets/images/default.png


二進制
examples/test_src/assets/images/icon-72.png


二進制
examples/test_src/assets/images/icon.png


二進制
examples/test_src/assets/images/icon@2x.png


+ 44 - 0
examples/test_src/assets/stylesheets/app.css

@@ -0,0 +1,44 @@
+/* @group <section> #demo_events */
+section#demo_events div.container{
+	width: 100%;
+	height: 128px;
+	background: #f00;
+	font-size: 2.0em;
+	text-align: center;
+	line-height: 128px;
+	color: #fff;
+	text-shadow: rgba(0,0,0,0.5) 0 2px 1px;
+}
+
+section#demo_events div.output{
+	width: 100%;
+	padding: 4px;
+	min-height: 14px;
+	background: #ccc;
+	color: rgba(0,0,0,0.75);
+}
+/* @end */
+
+/* @group <section> #demo_scroll */
+#scroll_vertical, #scroll_horizontal{margin-left: 3px;}
+#scroll_vertical{
+	height: 256px;
+}
+
+#scroll_horizontal {
+	height: 80px;
+}
+
+section#demo_scroll li{
+	height: 76px;
+	width: 76px;
+	background: #ccc;
+	font-size: 24px;
+	line-height: 76px;
+	list-style: none;
+	text-align: center;
+	float:left;
+	margin-right: 3px;
+	margin-bottom: 3px;
+}
+/* @end */

+ 326 - 0
examples/test_src/index.html

@@ -0,0 +1,326 @@
+<!doctype html>
+<html>
+<head>
+    <meta charset="utf-8">
+    <title>LungoJS - Test Source</title>
+    <meta name="description" content="">
+    <meta name="author" content="Javier Jiménez Villar (@soyjavi)">
+    <!-- Mobile viewport optimization http://goo.gl/b9SaQ -->
+    <meta name="HandheldFriendly" content="True">
+    <meta name="MobileOptimized" content="320">
+    <meta http-equiv="cleartype" content="on">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black">
+    <!-- For iPhone 4 with high-resolution Retina display: -->
+    <link rel="apple-touch-icon-precomposed" sizes="114x114" href="assets/images/icon@2x.png">
+    <link rel="apple-touch-icon-precomposed" sizes="72x72" href="assets/images/icon-72.png">
+    <link rel="apple-touch-icon-precomposed" href="assets/images/icon.png">
+    <link rel="apple-touch-startup-image" href="assets/images/default.png">
+    <!-- Main Stylesheet -->
+    <link rel="stylesheet" href="../../src/stylesheets/base.css">
+    <link rel="stylesheet" href="../../src/stylesheets/layout.css">
+    <link rel="stylesheet" href="../../src/stylesheets/widgets.css">
+    <link rel="stylesheet" href="../../src/stylesheets/lungo.theme.default.css">
+    <!-- App Stylesheet -->
+    <link rel="stylesheet" href="assets/stylesheets/app.css?v=1">    
+</head> 
+ 
+<body class="app">   
+    <section id="login" class="splash">
+		<article>
+			<h1>Lungo<span class="semi-opacity">js</span></h1>
+			<div class="form">
+				<input type="text" placeholder="Type your user"/>
+				<input type="password" placeholder="Type your password"/>
+				<a href="#demo" class="section button big blue" data-icon="play">Go to demo!</a>
+			</div>
+			<div class="copyright">Copyright TapQuo Inc, 2011</div>
+		</article>
+	</section>   
+
+	<section id="demo" class="_static">
+		<header data-title="LungoJS Framework">
+			<nav class="onright">
+				<a href="#"><span class="counter">8</span></a>
+				<a href="#main" class="article current" data-title="LungoJS Framework" data-icon="config"></a>
+				<a href="#author" class="article" data-title="Authors" data-icon="user"></a>
+			</nav>
+		</header>
+		<article id="main" class="list">
+            <scroll id="main_scroll">
+                <ul>
+                    <li class="info contrast">
+                        <img src="assets/images/icon.png" class="rounded">
+                        <strong>LungoJS Framework</strong>
+                        <small class="margin-top-4">version 1.0.0</small>
+                        <small>by TapQuo Inc.</small>
+                    </li>
+    				<li><a href="#demo_header_footer" class="section arrow" data-icon="wifi">
+    					<strong>Header + Footer + Articles</strong><small>paodapod</small></a></li>
+    				<li><a href="#demo_toolbar" class="section arrow" data-icon="download">
+    					<strong>Toolbar</strong><small>Toolbar</small></a></li>	
+    				<li><a href="#demo_list" class="section arrow" id="load_list" data-icon="note">
+                        <div class="bubble highlight">3 types</div>
+    					<strong>Lists</strong>
+    					<small>paodapod</small></a></li>
+    				<li><a href="#demo_events" class="section arrow" data-icon="user">
+    						<strong>Events handler</strong><small>paodapod</small></a></li>
+    				<li><a href="#demo_scroll" class="section arrow" id="load_scroll_mocks" data-icon="points">
+    						<strong>Scroll</strong><small>paodapod</strong></small></a></li>
+    				<li><a href="#demo_buttons" class="section arrow" data-icon="plus">
+    						<strong>Buttons</strong><small>paodapod</small></a></li>
+    				<li><a href="#demo_forms" class="section arrow" data-icon="write">
+                        <strong>Forms</strong><small>paodapod</small></a></li>
+    				<li class="anchor" data-icon="key">Tapquo Sugars</li>
+    				<li><a href="#demo_sugars" class="section" data-icon="box">
+    					<strong>Sugars</strong><small>paodapod</small></a></li>
+    			</ul>
+			</scroll>
+        </article>
+
+		<article id="author">
+		    <div class="list" data-search="contrast">
+		        <ul>
+		            <li>
+		                <img src="assets/images/soyjavi.jpg" class="size48 framed">
+		                <strong>Javi Jiménez Villar</strong>
+		                <small>@soyjavi</small>
+		                </li>
+		                
+	                <li>
+		                <img src="assets/images/pasku.png" class="size48 rounded">
+		                <strong>Guillermo Pascual</strong>
+		                <small>@pasku1</small></li>
+		        </ul>
+		    </div>
+		</article>
+    </section> 
+
+	<section id="demo_header_footer"  class="popup">
+		<header data-title="Summary" data-back="home blue">
+            <nav class="onright">
+                <a href="#summary" class="article current" data-title="Summary" data-icon="user"></a>
+                <a href="#messages" class="article" data-title="Message" data-icon="chat"><abbr>Messages</abbr></a>
+            </nav>
+        </header>
+
+		<article id="summary">
+		    Summary
+		    <a href="mailto:javi@tapquo.com">Prueba</a>
+		</article>
+		
+		<article id="messages">
+		    Messages
+		</article>
+		
+		<footer>
+		    <a href="#" class="button articblue onright" data-icon="key">Sign In</a>
+            <nav class="onleft">
+                <a href="#summary" class="article" alt="Test Article Nº1"><span class="icon user"></span></a>
+                <a href="#messages" class="article" alt="Test Article Nº2"><span class="icon chat"></span></a>
+            </nav>
+        </footer>
+	</section>
+	
+	<section id="demo_events">
+	    <header data-title="Events demo" data-back="home blue"></header>
+	    <article class="list">
+	        <li class="info contrast" data-icon="user">
+                <strong>Test events</strong>
+                <small class="margin-top-4">... in each row.</small>
+            </li>
+	        <li id="event_touchstart"><a href="#">Touchstart me!</a></li>
+	        <li id="event_tap">Tap me!</li>
+	        <li id="event_doubletap">Double-Tap me!</li>
+	        <li class="anchor" data-icon="shuffle">Special Mobile Events</li>
+            <li id="swipe">Swipe me!</li>
+            <li id="swipe_left">Swipe-Left me!</li>
+            <li id="swipe_right">Swipe-Right me!</li>
+            <li id="swipe_up">Swipe-Up me!</li>
+            <li id="swipe_down">Swipe-Down me!</li>
+	    </article>
+	</section>
+
+	<section id="demo_scroll">
+		<header data-title="Scroll Demo" data-back="home blue"></header>
+		<article>
+			<h1>Scroll: vertical</h1>
+			<scroll id="scroll_vertical" class="vertical"></scroll>
+				
+			<h1>Scroll: horizontal</h1>
+			<scroll id="scroll_horizontal" class="horizontal"></scroll>
+		</article>
+    </section>
+
+	<section id="demo_list">
+		<header data-back="home articblue" data-title="LISTS">
+			<nav class="onright">
+				<a href="#list_sample" class="article current" data-icon="note"><abbr>Simple List</abbr></a>
+				<a href="#list_grouped_sample" class="article" data-icon="folder"><abbr>Grouped List</abbr></a>
+				<a href="#list_info_sample" class="article" data-icon="cloudup"><abbr>Special .class</abbr></a>
+			</nav>
+		</header>
+		<article id="list_sample" class="list"></article>
+		<article id="list_grouped_sample" class="list"></article>
+		<article id="list_info_sample" class="list">
+		    <ul>
+		         <li class="info">
+                     <img src="assets/images/icon.png" class="framed">
+                     <strong>LungoJS Framework</strong>
+                     <small class="margin-top-4">version 0.2</small>
+                     <small>by @soyjavi & @pasku1</small>
+                 </li> 
+                 
+                 <li class="info contrast">
+                     <img src="assets/images/icon.png" class="framed">
+                     <strong>LungoJS Framework</strong>
+                     <small class="margin-top-4">version 0.2</small>
+                     <small>by @soyjavi & @pasku1</small>
+                 </li>
+                 
+                <li class="toolbar contrast">
+                    <nav>
+                        <a href="#list_sample" class="article current" data-icon="note"></a>
+        				<a href="#list_grouped_sample" class="article" data-icon="folder"></a>
+        				<a href="#list_info_sample" class="article" data-icon="cloudup"></a>
+                    </nav>
+                </li>
+                <li class="info contrast highlight">
+                    <img src="assets/images/icon.png" class="framed">
+                    <strong>LungoJS Framework</strong>
+                    <small class="margin-top-4">version 0.2</small>
+                    <small>by @soyjavi & @pasku1</small>
+                </li>
+                <li class="toolbar highlight">
+                    <nav>
+                        <a href="#list_sample" class="article current" data-icon="note"></a>
+        				<a href="#list_grouped_sample" class="article" data-icon="folder"></a>
+        				<a href="#list_info_sample" class="article" data-icon="cloudup"></a>
+                    </nav>
+                </li>
+		    </ul>
+		</article>
+		<footer data-title="COUNT:*"></footer>
+	</section>  
+
+	<section id="demo_toolbar">
+		<header data-title="Toolbar Demo" data-back="articblue home"></header>
+		<article id="toolbar_1"></article>
+		<article id="toolbar_2"></article>
+		<article id="toolbar_3"></article>
+		<article id="toolbar_4"></article>
+		<footer class="toolbar">
+            <nav>
+                <a href="#back" class="section" alt="Test Article Nº1" data-icon="left"></a>
+                <a href="#toolbar_1" class="article current" alt="Test Article Nº2" data-icon="chat"></a>
+				<a href="#toolbar_2" class="article" alt="Test Article Nº1" data-icon="user"></a>
+                <a href="#toolbar_3" class="article" alt="Test Article Nº2" data-icon="chat"></a>
+				<a href="#toolbar_4" class="article" alt="Test Article Nº1" data-icon="user"></a>
+            </nav>
+        </footer>
+	</section> 
+
+	<section id="demo_buttons">
+		<header data-back="home blue" data-title="Buttons">
+		</header>
+		<article>
+			<scroll id="scroll_buttons">
+				<p class="buttons" id="scroll_buttons_scroll" style="height:730px;">
+					<a href="#" class="button big">default</a>
+					<a href="#" class="button big disabled">disabled</a>
+			
+					<a href="#" class="button big red">red</a>
+					<a href="#" class="button big lightgreen">lightgreen</a>
+					<a href="#" class="button big green">green</a>
+					<a href="#" class="button big blue">blue</a>
+					<a href="#" class="button big articblue">articblue</a>
+					<a href="#" class="button big orange">orange</a>
+					<a href="#" class="button big magenta">magenta</a>
+					<a href="#" class="button big pink">pink</a>
+					<a href="#" class="button big yellow">yellow</a>
+					<a href="#" class="button big twitter" data-icon="twitter">twitter</a>
+					<a href="#" class="button big facebook" data-icon="facebook">facebook</a>
+				</p>
+			</scroll>
+		</article>
+		<footer>
+		    <a href="#back" class="section back onleft button articblue" data-icon="home">Home</a>
+			<a href="#" class="button red onleft" data-icon="picture">Picture</a>
+			<a href="#" class="button articblue onleft" data-icon="wifi"></a>
+			<a href="#" class="button yellow onleft" data-icon="home">Home</a>
+		</footer>
+	</section>
+
+	<section id="demo_forms">
+		<header data-back="home blue"></header>
+		<article>
+			<p class="form">
+				<input type="text" placeholder="Input text"/>
+				<input type="password" placeholder="Input password"/>
+				<input type="checkbox">Save credentials</input>
+				<textarea placeholder="Textarea sample"></textarea>
+				<label class="select">
+					<select class="custom">
+						<option value="1">One</option>
+						<option value="2">Two</option>
+						<option value="3">Three</option>
+					</select>
+				</label>
+				<br/>
+				
+				<a href="#" class="button big green" data-icon="check">Accept</a>
+				<a href="#" class="button big red" data-icon="cancel">Cancel</a>
+			</p>
+		</article>
+	</section>
+	
+	<section id="demo_sugars">
+		<header data-back="home articblue"></header>
+		<article>
+		</article>
+	</section>
+
+	<section id="demo_next">
+		<header data-back="home articblue"></header>
+		<article><h1>Section demo_next - Article id=messages</h1></article>
+	</section>  
+
+    <!-- Third parties dependencies -->
+    <script src="../../src/lib/zepto.js"></script>
+    <script src="../../src/lib/iscroll.js"></script>
+    <!-- LungoJS Libraries (Development mode)-->
+    <script src="../../src/Lungo.js"></script>
+    <script src="../../src/Lungo.App.js" ></script>
+    <script src="../../src/Lungo.Environment.js" ></script>
+    <script src="../../src/Lungo.Core.js"></script>  
+    <script src="../../src/Lungo.Events.js"></script>
+    <script src="../../src/Lungo.Service.js"></script>
+    <script src="../../src/router/Lungo.Router.js"></script>
+    <script src="../../src/router/Lungo.Router.History.js" ></script>  
+    <script src="../../src/view/Lungo.View.Resize.js"></script>
+    <script src="../../src/view/Lungo.View.Article.js" ></script>
+    <script src="../../src/view/Lungo.View.Scroll.js"></script>
+    <script src="../../src/view/Lungo.View.Template.js"></script>
+    <script src="../../src/view/Lungo.View.Template.Binding.js"></script>
+    <script src="../../src/view/Lungo.View.Template.List.js"></script>
+    <script src="../../src/dom/Lungo.Dom.js"></script>
+    <script src="../../src/dom/Lungo.Dom.Event.js"></script>
+    <script src="../../src/data/Lungo.Data.Cache.js"></script>   
+    <script src="../../src/data/Lungo.Data.Sql.js"></script>
+    <script src="../../src/data/Lungo.Data.Storage.js"></script>
+    <script src="../../src/attributes/Lungo.Attributes.Data.js"></script>
+    <script src="../../src/attributes/Lungo.Attributes.Section.js"></script>
+    <script src="../../src/boot/Lungo.Boot.js"></script>
+    <script src="../../src/boot/Lungo.Boot.Events.js"></script>
+    <script src="../../src/boot/Lungo.Boot.Data.js"></script>
+    <script src="../../src/boot/Lungo.Boot.Section.js"></script>
+    <script src="../../src/boot/Lungo.Boot.Article.js"></script>
+    <!-- LungoJS - Sandbox App -->    
+    <script src="app/app.js"></script>
+    <script src="app/data.js"></script>
+    <script src="app/events.js"></script>
+    <script src="app/services.js"></script>
+    <script src="app/view.js"></script>
+</body> 
+</html>

+ 14 - 0
examples/todo.js/app/app.js

@@ -0,0 +1,14 @@
+var App = (function(lng, undefined) {
+
+    lng.App.init({
+        name: 'ToDo.JS',
+        version: '0.1'
+    });
+
+    
+
+    return {
+
+    };
+
+})(LUNGO);

+ 76 - 0
examples/todo.js/app/data.js

@@ -0,0 +1,76 @@
+App.Data = (function(lng, App, undefined) {
+
+    //CONFIG: Data.Sql
+    lng.Data.Sql.init({
+        name: 'todo.js',
+        version: '1.0',
+        schema: [
+            { name: 'todo', drop: false, fields: {  
+                id: 'INTEGER PRIMARY KEY',
+                name: 'TEXT',
+                description: 'TEXT',
+                type: 'STRING',
+                done: 'INTEGER DEFAULT 0',
+                created_at: 'DATETIME' 
+                }
+            },
+            { name: 'types', drop: false, fields: {
+                id: 'INTEGER PRIMARY KEY',
+                name: 'TEXT'
+                }
+            },
+        ]
+    });
+
+    var refresh = function() {
+        _pendingTodos();
+        _doneTodos();
+    };
+
+    var insertTodo = function(data) {
+        lng.Data.Sql.insert('todo', data);
+    };
+
+    var removeTodo = function(id) {
+        lng.Data.Sql.drop('todo', {id:id});
+    };
+
+    var updateTodo = function(id, data) {
+        lng.Data.Sql.update('todo', data, {id:id});
+    };
+
+    var doneTodo = function(id) {
+        lng.Data.Sql.update('todo', {done:1}, {id:id});
+    };
+
+    var _pendingTodos = function() {
+        lng.Data.Sql.select('todo', {done:0}, function(result){
+            lng.View.Template.List.create({
+                container_id: 'pending',
+                template_id: 'pending-tmp',
+                data: result
+            });
+        });
+    };
+
+    var _doneTodos = function() {
+        lng.Data.Sql.select('todo', {done:1}, function(result){
+            lng.View.Template.List.create({
+                container_id: 'done',
+                template_id: 'list-tmp',
+                data: result
+            });
+        });
+    };
+
+    refresh();
+
+    return {
+        refresh: refresh,
+        insertTodo: insertTodo,
+        removeTodo: removeTodo,
+        updateTodo: updateTodo,
+        doneTodo: doneTodo
+    }
+    
+})(LUNGO, App);

+ 70 - 0
examples/todo.js/app/events.js

@@ -0,0 +1,70 @@
+App.Events = (function(lng, undefined) {
+
+    //Create new todo
+    lng.Dom.Event.live('#btnNewTodo', 'TAP', function(event) {
+
+        var name = lng.Dom.query('#txtNewName');
+        var description = lng.Dom.query('#txtNewDescription');
+        var type = lng.Dom.query('#txtNewType');
+
+        App.Data.insertTodo({
+            name: name.val(),
+            description: description.val(),
+            done: 0,
+            created_at: Date('now')
+        });
+
+        name.val('');
+        description.val('');
+
+        App.View.returnToMain('ToDo created', 'check');
+    });
+    
+    //View ToDo
+    lng.Dom.Event.live('#done li, #pending li', 'TAP', function(event) {
+        var todo_id = lng.Dom.query(this).attr('id');
+        App.View.todo(todo_id)
+    });
+
+    //Done ToDo
+    lng.Dom.Event.live('#btnDoneTodo', 'TAP', function(event) {
+        var current_todo = lng.Data.Cache.get('current_todo');
+
+        App.Data.doneTodo(current_todo.id);
+        App.View.returnToMain('ToDo done', 'check');
+    });
+
+    //Update ToDo
+    lng.Dom.Event.live('#btnUpdateTodo', 'TAP', function(event) {
+        var current_todo = lng.Data.Cache.get('current_todo');
+        var name = lng.Dom.query('#txtEditName');
+        var description = lng.Dom.query('#txtEditDescription');
+        var type = lng.Dom.query('#txtNewType');
+
+        App.Data.updateTodo(current_todo.id, {
+            name: name.val(),
+            description: description.val()
+        });
+
+        App.View.returnToMain('ToDo updated', 'write');
+    });
+
+    //Delete ToDo
+    lng.Dom.Event.live('#btnDeleteTodo', 'TAP', function(event) {
+        var current_todo = lng.Data.Cache.get('current_todo');
+
+        var options = [
+            {
+                name: '...Yes, delete it!',
+                icon: 'check',
+                color: 'green',
+                callback: function(){
+                    App.Data.removeTodo(current_todo.id);
+                    App.View.returnToMain('ToDo deleted', 'trash');
+                }
+            }
+        ];
+        lng.Sugar.Growl.option('Are you sure?', options);
+    });
+
+})(LUNGO);

+ 7 - 0
examples/todo.js/app/services.js

@@ -0,0 +1,7 @@
+App.Services = (function(lng, App, undefined) {
+
+    return {
+
+    }
+
+})(LUNGO, App);

+ 53 - 0
examples/todo.js/app/view.js

@@ -0,0 +1,53 @@
+App.View = (function(lng, App, undefined) {
+
+    lng.View.Template.create('pending-tmp', 
+        '<li id="{{id}}">\
+            <a href="#">\
+                <span class="icon check"></span>\
+                <strong>{{name}}</strong>\
+                <small>{{description}}</small>\
+            </a>\
+        </li>'
+    );
+
+    lng.View.Template.create('list-tmp', 
+        '<li id="{{id}}">\
+            <a href="#">\
+                <span class="icon folder"></span>\
+                <strong>{{name}}</strong>\
+                <small>{{description}}</small>\
+            </a>\
+        </li>'
+    );
+
+    var todo = function(id) {
+        lng.Data.Sql.select('todo', {id:id}, function(result){
+            if (result.length > 0) {
+                var data = result[0];
+                lng.Data.Cache.set('current_todo', data);
+
+                $('#txtEditName').val(data.name);
+                $('#txtEditDescription').val(data.description);
+                $('#txtEditName').val(data.name);
+
+                lng.Router.section('view');
+            }
+        });
+    };
+
+    var returnToMain = function(message, icon) {
+        lng.Sugar.Growl.show(message, icon, true);
+        App.Data.refresh();
+
+        setTimeout(function() {
+            lng.Router.back();
+            lng.Sugar.Growl.hide();
+        }, 500);
+    };
+
+    return{
+        todo: todo,
+        returnToMain: returnToMain
+    }
+
+})(LUNGO, App);

二進制
examples/todo.js/assets/images/default.png


二進制
examples/todo.js/assets/images/icon-72.png


二進制
examples/todo.js/assets/images/icon.png


二進制
examples/todo.js/assets/images/icon@2x.png


文件差異過大導致無法顯示
+ 24 - 0
examples/todo.js/assets/sugars/lungo.sugar.growl.css


+ 171 - 0
examples/todo.js/assets/sugars/lungo.sugar.growl.js

@@ -0,0 +1,171 @@
+/** 
+ * Description or Responsability 
+ * 
+ * @namespace LUNGO.Sugar
+ * @class Growl
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ */
+
+LUNGO.Sugar.Growl = (function(lng, undefined) {
+    var _options = [];
+
+    var SELECTOR = {
+        BODY: 'body',
+        GROWL: '.growl',
+        MODAL: '.growl .modal',
+        NOTIFY: '.growl .notify',
+        MODAL_HREF: '.growl .modal a'
+    }
+
+    var CSS_CLASS = {
+        VISIBLE: 'visible',
+        SHOW: 'show',
+        HIDE: 'hide',
+        WORKING: 'working',
+        INPUT: 'input'
+    }
+
+    var CALLBACK_HIDE = 'LUNGO.Sugar.Growl.hide()';
+    var MARKUP_GROWL = '<div class="growl"><div class="modal"></div><div class="notify"></div></div>';
+
+    var show = function(title, icon, animate, seconds, callback) {
+        _showGrowlModal(true);
+        
+        var modal = $(SELECTOR.MODAL);
+        modal.removeClass(CSS_CLASS.SHOW);
+        modal.removeClass(CSS_CLASS.INPUT);
+        modal.html('<span class="big icon ' + icon + '"></span><strong>' + title + '<strong>');
+        _animate(modal, animate);
+        modal.show().addClass(CSS_CLASS.SHOW);
+
+        _auto_hide(seconds, callback);
+    };
+
+    var hide = function() {
+        _hide_children();
+
+        setTimeout(function() {
+            $(SELECTOR.GROWL).hide();
+        }, 100);
+    };
+
+    var notify = function(title, description, icon, type, seconds, callback) {
+        _showGrowlModal(false);
+
+        var notify = $(SELECTOR.NOTIFY);
+        if (type) {
+            notify.addClass(type);
+        }
+        notify.html('<span class="icon ' + icon + '"></span><strong>' + title + '</strong><br/><em>' + description + '</strong>');
+
+        setTimeout(function() {
+            notify.addClass(CSS_CLASS.SHOW);
+        }, 300);
+
+        _auto_hide(seconds, callback);
+    };
+
+    var option = function(title, options) {
+        _showGrowlModal(true);
+
+        _options = options;
+
+        $(SELECTOR.MODAL).removeClass(CSS_CLASS.WORKING).removeClass(CSS_CLASS.SHOW);
+        
+        var buttons = _createButtons(options);
+        $(SELECTOR.MODAL).addClass('input').html('<span class="title">' + title + '</span>' + buttons);
+        $('.growl .modal').show().addClass('show');
+    };
+
+    var _init = function() {
+        //TODO: Append stylesheet (.less)
+        $(SELECTOR.BODY).append(MARKUP_GROWL);
+        _subscribeEvents();
+    };
+
+    var _showGrowlModal = function(modal) {
+        var growl = $(SELECTOR.GROWL);
+        if (!growl.is(CSS_CLASS.VISIBLE)) {
+            growl.show();
+        }
+        if (modal) {
+            growl.addClass('modal');
+        }
+    };
+
+    var _animate = function(element, animate) {
+        if (animate) {
+            element.addClass(CSS_CLASS.WORKING);
+        }
+        else {
+            element.removeClass(CSS_CLASS.WORKING);
+        }
+    };
+
+    var _createButtons = function(options) {
+        var buttons = '';
+        for (var i = 0, len = options.length; i < len; i++) {
+            buttons += _option_button(options[i].color, 'growl_option_' + i, options[i].icon, options[i].name);
+        };
+        buttons += _option_button('red', undefined, 'cancel', 'Cancel');
+
+        return buttons;
+    };
+
+    var _option_button = function(color, id, icon, label) {
+        id = (id !== undefined) ? 'id="' + id + '"' : '';
+        return '<a href="#" ' + id + ' class="button ' + color + '"><span class="icon ' + icon + '"></span>' + label + '</a>';
+    };
+
+    var _auto_hide = function(seconds, callback) {
+        if (seconds != undefined && seconds != 0) {
+            if (callback === undefined) {
+                callback = CALLBACK_HIDE;
+            }
+            setTimeout(callback, seconds * 1000);
+        }
+    };
+
+    var _hide_children = function() {
+        _hide_child(SELECTOR.MODAL);
+        _hide_child(SELECTOR.NOTIFY);
+    };
+
+    var _hide_child = function(selector) {
+        var child = $(selector);
+        if (child.hasClass(CSS_CLASS.SHOW)) {
+            child.removeClass(CSS_CLASS.SHOW);
+            //child.removeClass(CSS_CLASS.INPUT).html('');
+        }
+    };
+
+    var _subscribeEvents = function() {
+        //BINDING EVENT
+        //CF.app.EVENTS.touch
+        $(SELECTOR.NOTIFY).bind('click', function() {
+            $(SELECTOR.NOTIFY).removeClass(CSS_CLASS.SHOW);
+        });
+
+
+        $('.growl .modal a').live('click', function(event) {
+            if ($(this).attr('id') !== ''){
+                id = $(this).attr('id').replace(/growl_option_/g, '');
+                setTimeout(_options[id].callback, 100);
+            } else {
+                event.preventDefault();
+                hide();
+                return false;
+            }
+        });
+    };
+
+    _init();
+
+    return {
+        show: show,
+        hide: hide,
+        notify: notify,
+        option: option
+    }
+})(LUNGO);

+ 143 - 0
examples/todo.js/index.html

@@ -0,0 +1,143 @@
+<!doctype html>
+<html manifest="APP.appcache">
+<head>
+    <meta charset="utf-8">
+    <title>ToDo.js with LungoJS</title>
+    <meta name="description" content="">
+    <meta name="author" content="Javier Jiménez Villar (@soyjavi)">
+    <!-- Mobile viewport optimization http://goo.gl/b9SaQ -->
+    <meta name="HandheldFriendly" content="True">
+    <meta name="MobileOptimized" content="320">
+    <meta http-equiv="cleartype" content="on">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0;">
+    <meta name="apple-mobile-web-app-capable" content="yes">
+    <meta name="apple-mobile-web-app-status-bar-style" content="black">
+    <!-- For iPhone 4 with high-resolution Retina display: -->
+    <link rel="apple-touch-icon-precomposed" sizes="114x114" href="assets/images/icon@2x.png">
+    <link rel="apple-touch-icon-precomposed" sizes="72x72" href="assets/images/icon-72.png">
+    <link rel="apple-touch-icon-precomposed" href="assets/images/icon.png">
+    <link rel="apple-touch-startup-image" href="assets/images/default.png">
+    <!-- Main Stylesheet -->
+    <link rel="stylesheet" href="../../release/lungo-1.0.0.min.css">
+    <link rel="stylesheet" href="../../release/lungo.theme.default.css">
+    <!-- Sugars :) -->
+    <link rel="stylesheet" href="assets/sugars/lungo.sugar.growl.css">
+</head> 
+ 
+<body class="app">   
+    <section id="login" class="splash">
+		<article>
+			<h1>ToDo<span class="semi-opacity">.js</span></h1>
+			<div class="form">
+				<input type="text" placeholder="Type your user" disabled/>
+				<input type="password" placeholder="Type your password" disabled/>
+				<a href="#main" class="section button big blue" data-icon="key">Go to app!</a>
+			</div>
+			<div class="copyright">Copyright TapQuo Inc, 2011</div>
+		</article>
+	</section>   
+
+	<section id="main">
+		<header data-title="ToDo.js">
+			<a href="#new" class="section button onright articblue" data-icon="add">&nbsp;New</a>
+		</header>
+
+        <article id="pending" class="list"></article>
+        <article id="done" class="list"><!--  data-search="contrast" --></article>
+
+        <footer class="toolbar">
+            <nav>
+                <a href="#pending" class="article current" data-title="Pending ToDo's" data-icon="inbox"></a>
+                <a href="#done" class="article" data-title="Done ToDo's" data-icon="folder"></a>
+                <a href="#settings" class="section" data-icon="config"></a>
+            </nav>
+        </footer>
+    </section>
+    
+    <section id="view">
+        <header data-back="home articblue" data-title="View ToDo's" >
+            <a href="#" id="btnDoneTodo" class="button onright red" data-icon="check">&nbsp;Done</a>
+        </header>
+        <article>
+            <p class="form">
+			    <label>Name</label>
+				<input type="text" id="txtEditName" placeholder="Input text"/>
+				<label>Description</label>
+				<textarea id="txtEditDescription" placeholder="Textarea sample"></textarea>
+				<label>Type</label>
+				<label class="select">
+					<select id="txtEditType">
+						<option value="1">One</option>
+						<option value="2">Two</option>
+						<option value="3">Three</option>
+					</select>
+				</label>
+			</p>
+        </article>
+        <footer class="toolbar">
+            <nav>
+                <a href="#" id="btnUpdateTodo" data-icon="write"></a>
+                <a href="#" id="btnDeleteTodo" data-icon="trash"></a>
+            </nav>
+        </footer>
+    </section>
+
+    <section id="new">
+		<header data-back="home articblue" data-title="New ToDo"></header>
+		<article>
+			<p class="form">
+			    <label>Name</label>
+				<input type="text" id="txtNewName" placeholder="Input text"/>
+				<label>Description</label>
+				<textarea id="txtNewDescription" placeholder="Textarea sample"></textarea>
+				<label>Type</label>
+				<label class="select">
+					<select id="txtNewType">
+						<option value="1">One</option>
+						<option value="2">Two</option>
+						<option value="3">Three</option>
+					</select>
+				</label>
+				<br/>
+				
+				<a href="#" id="btnNewTodo" class="button big articblue" data-icon="check">Add ToDo</a>
+				<a href="#back" class="section button big red" data-icon="cancel">Cancel</a>
+			</p>
+		</article>
+	</section> 
+
+	<section id="settings">
+	    <header data-back="home articblue" data-title="Settings"></header>
+	    <article>
+	        <p class="form">
+    	        <label>Your name:</label>
+    			<input type="text" id="txtName" placeholder="Type your name"/>
+			
+    	        <label>Available task types:</label>
+    	        <input type="text" id="txtType" placeholder="Type new type of task"/>
+    			<label class="select">
+    				<select id="txtTypes">
+    					<option value="1">One</option>
+    					<option value="2">Two</option>
+    					<option value="3">Three</option>
+    				</select>
+    			</label>
+    			<a href="#" id="btnNewType" class="button big articblue" data-icon="check">Add new type</a>
+			</p>
+	    </article>
+	</section>
+
+    <!-- Third parties dependencies -->
+    <script src="../../src/lib/zepto.js"></script>
+    <script src="../../src/lib/iscroll.js"></script>
+    <!-- LungoJS (Production mode) -->
+    <script src="../../release/lungo-1.0.0.min.js"></script>
+    <script src="assets/sugars/lungo.sugar.growl.js"></script>
+    <!-- LungoJS - Sandbox App -->    
+    <script src="app/app.js"></script>
+    <script src="app/data.js"></script>
+    <script src="app/events.js"></script>
+    <script src="app/services.js"></script>
+    <script src="app/view.js"></script>
+</body> 
+</html>

+ 9 - 0
examples/todo.js/todo.appcache

@@ -0,0 +1,9 @@
+CACHE MANIFEST
+CACHE:
+index.html
+/assets/images/default.png
+/assets/images/icon-72.png
+/assets/images/icon.png
+/assets/images/icon@2x.png
+/assets/sugars/lungo.sugar.growl.css
+/assets/sugars/lungo.sugar.growl.js

+ 18 - 0
package.json

@@ -0,0 +1,18 @@
+{
+    "name": "LungoJS"
+  , "description": ""
+  , "version": ""
+  , "homepage": "https://github.com/TapQuo/Lungo.js"
+  , "author": "Javier Jimenez Villar <javier@tapquo.com> (http://twitter.com/soyjavi ) "
+  , "keywords": []
+  , "main": "./"
+  , "repository": {
+      "type": "git"
+    , "url": "https://github.com/TapQuo/Lungo.js"
+  }
+  , "devDependencies": {
+      "": ">= "
+      "": ">= "
+      "": ">= "
+  }
+}

文件差異過大導致無法顯示
+ 1880 - 0
release/lungo-1.0.0.js


文件差異過大導致無法顯示
+ 24 - 0
release/lungo-1.0.0.min.css


文件差異過大導致無法顯示
+ 24 - 0
release/lungo-1.0.0.min.js


文件差異過大導致無法顯示
+ 113 - 0
release/lungo-1.0.0.packed.js


+ 217 - 0
release/lungo.theme.default.css

@@ -0,0 +1,217 @@
+/**
+ *
+ *    /$$
+ *   | $$
+ *   | $$       /$$   /$$ /$$$$$$$   /$$$$$$   /$$$$$$
+ *   | $$      | $$  | $$| $$__  $$ /$$__  $$ /$$__  $$
+ *   | $$      | $$  | $$| $$  \ $$| $$  \ $$| $$  \ $$
+ *   | $$      | $$  | $$| $$  | $$| $$  | $$| $$  | $$
+ *   | $$$$$$$$|  $$$$$$/| $$  | $$|  $$$$$$$|  $$$$$$/
+ *   |________/ \______/ |__/  |__/ \____  $$ \______/
+ *                                  /$$  \ $$
+ *                                 |  $$$$$$/
+ *                                  \______/
+ *
+ * @copyright 2011 TapQuo Inc (c)
+ * @license   http://www.github.com/tapquo/lungo/blob/master/LICENSE.txt
+ * @version   1.0.0
+ * @link      https://github.com/TapQuo/Lungo.js
+ *
+ * @author   Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ */
+
+/* @group layout */
+.app {
+	font-size:13px;
+    background: #000;
+	}
+
+article{
+	background: #fff;
+	}
+	
+/* @group <header> & <footer> */
+header{
+	background: #42474d -webkit-gradient(linear, left top, left bottom, color-stop(0.25, #42474d), color-stop(1, #2c3137));
+	-webkit-box-shadow: 
+		inset 0px 1px 0px rgba(255,255,255,0.25), 
+		inset 0px -1px rgba(0,0,0,0.5), 
+		0px 2px 2px rgba(0,0,0,0.2);}
+	
+footer{
+	background: #2c2c2d -webkit-gradient(linear, left top, left bottom, color-stop(0.25, #2c2c2d), color-stop(1, #1c1d1d));
+	
+	-webkit-box-shadow: 
+		inset 0px 1px rgba(0,0,0,0.5), 
+		0px -1px 2px rgba(0,0,0,0.4);}
+
+.title{
+	color: #fff;
+	text-shadow: rgba(0,0,0,0.5) 0 -1px 1px; 
+	text-transform: uppercase;}
+/* @end */
+
+/* @group <nav> */
+nav a {
+	background: none;
+	color: rgba(0,0,0,0.5);
+	text-shadow: rgba(255,255,255,0.35) 0 1px 1px;}
+	
+header:not(.toolbar) nav a, footer:not(.toolbar) nav a{
+	text-shadow: none;
+	
+	-webkit-box-shadow: 
+	inset 0px 1px rgba(255,255,255,0.15), 
+	inset 1px 0px rgba(0,0,0,0.75), 
+	inset 2px 0px rgba(255,255,255,0.25);
+}
+
+nav a.current{
+	background: rgba(255,255,255,0.15);
+	color: #fff;
+	text-shadow: rgba(0,0,0,1) 0 1px 1px;
+	text-shadow: none;
+	}	
+
+//@ToDo: Change the colour for not .toolbar classed elements
+not(.toolbar) nav a.current{
+	
+}
+
+.toolbar a:first-child{	-webkit-box-shadow: none; }
+	
+/* @end */
+
+/* @group .list */
+.list {
+	background: #c5c5c5;
+	}
+
+.list li:not(.info){
+	background: #fff;
+	border-bottom: 1px inset #c3c4c6;
+	color: #333;
+	}
+	
+.list li.toolbar{
+	border-bottom-color: rgba(0,0,0,0.2);
+	}
+
+.list li:not(.toolbar) a { 
+	color: #333;
+	text-shadow: #fff 0px 1px 1px; 
+	-webkit-tap-highlight-color: #242525;
+	}
+
+.list strong {
+	color: #2343be;
+	}
+	
+.list small{
+	opacity: 0.75;
+	}
+	
+.list .bubble{
+	border: 1px solid rgba(0,0,0,0.15);
+	-webkit-border-radius: 2px;
+	color: #fff; 
+	text-shadow: 0 -1px 0 rgba(0,0,0,.3);
+	}	
+.list .button{
+	color: #fff;
+	text-shadow: 0 -1px 0 rgba(0,0,0,.3); 
+}
+
+.list .arrow:before{
+	color: #a9a9a9;}
+
+
+
+/* @group .anchor */
+.list li.anchor {
+	background: #e1e1e1;
+	color: #222;
+	}
+/* @end */
+
+
+/* @group .anchor */
+.list li.search {
+	border: none;
+	-webkit-box-shadow: 
+		inset 0px 1px 0px rgba(255,255,255,0.1), 
+		inset 0px -1px 0px rgba(0,0,0,0.25);
+	}
+/* @end */
+
+/* @group .info */
+.list li.info {	
+	border: none;
+	-webkit-box-shadow: 
+		inset 0px 1px 0px rgba(255,255,255,0.1), 
+		inset 0px -1px 0px rgba(0,0,0,0.25);
+	background: #cbcbcb;
+	}
+	
+.list .info strong{	
+	color: #333;
+	}
+
+.list .info.contrast strong{	
+	color: #fff;
+	}
+/* @end */
+/* @end */
+.list .contrast{
+	background: #2c2c2d -webkit-gradient(linear, left top, left bottom, color-stop(0.25, #2c2c2d), color-stop(1, #1c1d1d)) !important;
+	text-shadow: 0px 1px 1px rgba(0,0,0,0.25);
+	color: #fff;
+}
+
+.list .highlight{
+	background: #499dc4 -webkit-gradient(linear, left top, left bottom, color-stop(0, #499dc4), color-stop(1, #0e8eb6)) !important;
+	}
+	
+/* @end */
+
+/* @group .classes */
+/* @end */
+
+/* @group widgets */
+
+/* @group <section>.splash */
+.splash article { 
+	background: #2c2c2d -webkit-gradient(linear, left top, left bottom, color-stop(0.25, #2c2c2d), color-stop(1, #1c1d1d)) !important;
+	color: #fff; 
+	text-shadow: rgba(0,0,0,0.5) 0 1px 1px;}
+/* @end */
+
+/* @group <inputs> */
+input, textarea, select {
+	border: 1px solid #ccc;	
+	-webkit-border-radius: 2px;
+	background: #fff -webkit-gradient(linear, left top, left bottom, color-stop(0, #ddd), color-stop(0.15, #fff));}
+	
+input:focus, textarea:focus, select:focus { 
+	border: 1px solid #058cf5;
+	-webkit-box-shadow: 0 0 4px 1px #058cf5; 
+	background: #e8fefe;}
+	
+.select:after {
+	background: #ccc;
+	color: white;
+	-webkit-border-radius: 0 2px 2px 0;}
+/* @end */
+	
+
+
+/* @group .counter (BETA) */
+.counter{
+	background: #ee2c23;
+	-webkit-border-radius: 2px; 
+	-moz-border-radius: 2px; 
+	
+	color: #fff;
+}
+/* @end */
+/* @end */

+ 9 - 0
spec/Lungo.Core.Spec.js

@@ -0,0 +1,9 @@
+describe('Lungo Core', function() {
+    it('', function() {
+
+    });
+    
+    it('', function() {
+        
+    });
+});

+ 11 - 0
spec/Lungo.Spec.js

@@ -0,0 +1,11 @@
+describe('Lungo', function() {
+
+    it('Lungo namespace exists', function() {
+        expect(LUNGO).toBeDefined();
+    });
+    
+    it('Lungo version exists', function() {
+        expect(LUNGO.VERSION).toBeDefined();
+    });
+
+});            

+ 57 - 0
spec/SpecRunner.html

@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta name="author" content="Guillermo Pascual" />
+  <meta charset="UTF-8" />
+  <title>Lungo Spec Runner</title>
+  
+  <link rel="shortcut icon" type="image/png" href="../vendor/jasmine-1.1.0/jasmine_favicon.png">
+  <link rel="stylesheet" type="text/css" href="../vendor/jasmine-1.1.0/jasmine.css">
+
+  <!-- lib source files here... -->
+    <script type="text/javascript" src="../vendor/jasmine-1.1.0/jasmine.js"></script>
+    <script type="text/javascript" src="../vendor/jasmine-1.1.0/jasmine-html.js"></script>
+  <!-- end lib source files -->
+
+  <!-- Lungo source files here... -->
+    <script type="text/javascript" src="../src/Lungo/Lungo.js"></script>
+    <script type="text/javascript" src="../src/Lungo/Lungo.Core.js"></script>
+  <!-- end Lungo source files -->
+
+  <!-- Lungo spec files here... -->
+    <script type="text/javascript" src="Lungo.Spec.js"></script>
+    <script type="text/javascript" src="Lungo.Core.Spec.js"></script>
+  <!-- Lungo spec files -->
+
+  <script type="text/javascript">
+    (function() {
+        var jasmineEnv = jasmine.getEnv();
+        jasmineEnv.updateInterval = 1000;
+
+        var trivialReporter = new jasmine.TrivialReporter();
+
+        jasmineEnv.addReporter(trivialReporter);
+
+        jasmineEnv.specFilter = function(spec) {
+            return trivialReporter.specFilter(spec);
+        };
+
+        var currentWindowOnload = window.onload;
+
+        window.onload = function() {
+            if (currentWindowOnload) {
+                currentWindowOnload();
+            }
+            execJasmine();
+        };
+
+        function execJasmine() {
+            jasmineEnv.execute();
+        }
+    })();
+  </script>
+</head>
+<body>
+    
+</body>
+</html>

+ 44 - 0
src/Lungo.App.js

@@ -0,0 +1,44 @@
+/** 
+ * Lungo sandbox APP initialization
+ * 
+ * @namespace LUNGO
+ * @class App
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.App = (function(lng, undefined) {
+
+     var default_config = {
+         id: 1,
+         name: 'lungo_app',
+         version: 1.0,
+         icon: ''
+     };
+
+    /**
+     * Initializes all LungoJS system: setting properties for the application,
+     * subscribing to automatic events, initializing sections & articles
+     * and stating the title.
+     *
+     * @method init
+     *
+     * @param {object} Application configuration properties
+     */
+    var init = function(app_config) {
+        default_config = lng.Core.mix(default_config, app_config);
+
+        lng.Boot();
+    };
+
+    var get = function(property) {
+        return default_config[property];
+    };
+
+    return {
+        init: init,
+        get: get
+    };
+
+})(LUNGO);

+ 147 - 0
src/Lungo.Core.js

@@ -0,0 +1,147 @@
+/** 
+ * Contains all the common functions used in Lungo.
+ * 
+ * @namespace LUNGO
+ * @class Core
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Core = (function(lng, $, undefined) {
+
+    var ARRAY_PROTO = Array.prototype;
+    var OBJ_PROTO   = Object.prototype;
+    var SUPPORTED_OS = ['ios', 'android', 'blackberry', 'webos'];
+
+    /**
+     * Console system to display messages when you are in debug mode.
+     *
+     * @method log
+     *
+     * @param {number} Severity based in (1)Log, (2)Warn, (>2)Error
+     * @param {string} Message to show in console
+     */
+    var log = function(severity, message) {
+        if (lng.Environment.isDesktop()) {
+            console[(severity === 1) ? 'log' : (severity === 2) ? 'warn' : 'error'](message);
+        } else {
+            // @todo : send to the server
+        }
+    };
+
+    /**
+     * Executes callbacks based on the parameters received.
+     *
+     * @method execute
+     *
+     * @param {Function} callback to execute
+     */
+    var execute = function() {
+        var args = toArray(arguments);
+        var callback = args.shift();
+
+        if (toType(callback) === 'function') {
+            callback.apply(null, args);
+        }
+    };
+
+    /**
+     * Creates a new function that, when called, itself calls this function in
+     * the context of the provided this value, with a given sequence of arguments
+     * preceding any provided when the new function was called.
+     *
+     * @method bind
+     *
+     * @param {object} object to which the 'this' can refer in the new function when the new function is called.
+     * @param {Function} method A function object.
+     */
+    var bind = function(object, method) {
+        return function() {
+            return method.apply(object, toArray(arguments));
+        };
+    };
+        
+    /**
+     * Copy from any number of objects and mix them all into a new object.
+     * The implementation is simple; just loop through arguments and
+     * copy every property of every object passed to the function.
+     *
+     * @method mix
+     *  
+     * @param {object} arguments to mix them all into a new object.
+     * @return {object} child a new object with all the objects from the arguments mixed.
+     */
+     var mix = function() {
+        var child = child || {};
+        for (var arg = 0, len = arguments.length; arg < len; arg++) {
+            var argument = arguments[arg];
+            for (var prop in argument) {
+                if (isOwnProperty(argument, prop)) {
+                    child[prop] = argument[prop];
+                }
+            }
+        }
+        return child;
+    };
+    
+    /**
+     * Every object descended from Object inherits the hasOwnProperty method.
+     * This method can be used to determine whether an object has the specified property
+     * as a direct property of that object.
+     *
+     * @param {object} object to test for a property's existence inside itself.
+     * @param {string} property the name of the property to test.
+     * @return {boolean} indicating whether the object has the specified property.
+     */
+    var isOwnProperty = function(object, property) {
+        return OBJ_PROTO.hasOwnProperty.call(object, property);
+    };
+
+    /**
+     * Determine the internal JavaScript [[Class]] of an object.
+     *
+     * @param {object} obj to get the real type of itself.
+     * @return {string} with the internal JavaScript [[Class]] of itself.
+     */
+    var toType = function(obj) {
+        return OBJ_PROTO.toString.call(obj).match(/\s([a-z|A-Z]+)/)[1].toLowerCase();
+    };
+
+    /**
+     * Convert an array-like object into a true JavaScript array.
+     *
+     * @param {object} obj Any object to turn into a native Array.
+     * @return {object} The object is now a plain array.
+     */
+    var toArray = function(obj) {
+        return ARRAY_PROTO.slice.call(obj, 0);
+    };
+
+    /**
+     * 
+     *
+     */
+    var isMobile = function() {
+        var result = false;
+
+        for (var i = 0, len = SUPPORTED_OS.length; i < len && !result; i++) {
+            var mobile_os = SUPPORTED_OS[i];
+            $.os[mobile_os] && (result = true);
+        }
+
+        return result;
+    };
+
+    return {
+        log: log,
+        execute: execute,
+        bind: bind,
+        mix: mix,
+        isOwnProperty: isOwnProperty,
+        toType: toType,
+        toArray: toArray,
+        isMobile: isMobile
+    };
+
+})(LUNGO, Zepto);

+ 72 - 0
src/Lungo.Environment.js

@@ -0,0 +1,72 @@
+/** 
+ * Set environment (Desktop or Mobile) automatically, depending on the
+ * environment the subscribed events wil be different.
+ * 
+ * @namespace LUNGO
+ * @class Environment
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Environment = (function(lng, undefined) {
+
+    var MOBILE_ENVIRONMENT  = 'mobile';
+    var DESKTOP_ENVIRONMENT = 'desktop';
+
+    var _environment = DESKTOP_ENVIRONMENT;
+
+    /**
+     * Analizing if it's run in Mobile Phone and changing the type of event to subscribe.
+     *
+     * @method start
+     */
+    var start = function() {
+        if (lng.Core.isMobile()) {
+            _environment = MOBILE_ENVIRONMENT;
+            _saveStatsInLungoJS();
+        }
+    };
+
+    /**
+     * Gets the current environment for LungoJS
+     *
+     * @method init
+     *
+     * @return {String} Current environment enumerator
+     */
+    var current = function() {
+        return _environment;
+    };
+
+    /**
+     * Returns whether the development environment is in desktop mode
+     *
+     * @method isDesktop
+     *
+     * @return {Boolean} True if is in DESKTOP_ENVIRONMENT
+     */
+    var isDesktop = function() {
+        return (_environment === DESKTOP_ENVIRONMENT) ? true : false;
+    };
+
+    /**
+     * Save in LungoJS.com the use of the service for further ranking
+     *
+     * @method _saveStatsInLungoJS
+     */    
+    var _saveStatsInLungoJS = function() {
+        lng.Service.post( 'http://www.lungojs.com/stats/', {
+            name: lng.App.get('name'),
+            version: lng.App.get('version'),
+            icon: lng.App.get('icon')
+        });
+    }
+
+    return {
+        start: start,
+        current: current,
+        isDesktop: isDesktop
+    };
+
+})(LUNGO);

+ 53 - 0
src/Lungo.Events.js

@@ -0,0 +1,53 @@
+/** 
+ * Lungo UI events Manager
+ * 
+ * @namespace LUNGO
+ * @class Event
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Events = (function(lng, undefined) {
+
+    var EVENTS = {
+        mobile: {
+            TOUCH_START: 'touchstart',
+            TOUCH_MOVE: 'touchmove',
+            TOUCH_END: 'touchend',
+            TAP: 'tap',
+            DOUBLE_TAP: 'doubletap',
+            ORIENTATION_CHANGE: 'orientationchange'
+        },
+        desktop: {
+            TOUCH_START: 'click',
+            TOUCH_MOVE: 'click',
+            TOUCH_END: 'click',
+            TAP: 'click',
+            DOUBLE_TAP: 'dblclick',
+            ORIENTATION_CHANGE: 'orientationchange'
+        }
+    };
+
+    var current_environment = lng.Environment.current();
+    var current_events = EVENTS[current_environment];
+
+    /**
+     * Returns the touch event based on an enumeration of LungoJS
+     * and the current environment
+     *
+     * @method get
+     *
+     * @param  {string} Touch enumerator of LungoJS
+     * @return {string} Touch event based on the current environment
+     */
+    var get = function(eventName) {
+        return current_events[eventName];
+    };
+
+    return {
+        get: get
+    };
+
+})(LUNGO);

+ 72 - 0
src/Lungo.Service.js

@@ -0,0 +1,72 @@
+/** 
+ * External Data & Services Manager
+ * 
+ * @namespace LUNGO
+ * @class Service
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Service = (function(lng, $, undefined) {
+
+    /**
+     * Load data from the server using a HTTP GET request.
+     *
+     * @method get
+     *
+     * @param  {string} Containing the URL to which the request is sent
+     * @param  {object} A map or string that is sent to the server with the request
+     * @param  {Function} [OPTIONAL] Callback function after the request
+     */
+    var get = function(url, data, callback) {
+        var parameters = '?';
+        for (var parameter in data) {
+            if (lng.Core.isOwnProperty(data, parameter)) {
+                if (parameters !== '?') parameters += '&';
+                parameters += parameter + '=' + data[parameter];
+            }
+        }
+        url = url + parameters;
+
+        _ajax('GET', url, null, callback);
+    };
+
+    /**
+     * Load data from the server using a HTTP POST request.
+     *
+     * @method post
+     *
+     * @param  {string} Containing the URL to which the request is sent
+     * @param  {object} A map or string that is sent to the server with the request
+     * @param  {Function} [OPTIONAL] Callback function after the request
+     */
+    var post = function(url, data, callback) {
+        _ajax('POST', url, data, callback);
+    };
+
+    var _ajax = function(type, url, data, callback, error) {
+        $.ajax({
+            type: type,
+            url: url,
+            dataType: 'json',
+            success: function(response) {
+                if (lng.Core.toType(callback) === 'function') {
+                    setTimeout(callback, 100, response);
+                }
+            },
+            error: function(xhr, type) {
+                if (error) {
+                    setTimeout(error, 100, result);
+                }
+            }
+        });
+    };
+
+    return {
+        get: get,
+        post: post
+    };
+
+})(LUNGO, Zepto);

+ 31 - 0
src/Lungo.js

@@ -0,0 +1,31 @@
+/**
+ *
+ *    /$$
+ *   | $$
+ *   | $$       /$$   /$$ /$$$$$$$   /$$$$$$   /$$$$$$
+ *   | $$      | $$  | $$| $$__  $$ /$$__  $$ /$$__  $$
+ *   | $$      | $$  | $$| $$  \ $$| $$  \ $$| $$  \ $$
+ *   | $$      | $$  | $$| $$  | $$| $$  | $$| $$  | $$
+ *   | $$$$$$$$|  $$$$$$/| $$  | $$|  $$$$$$$|  $$$$$$/
+ *   |________/ \______/ |__/  |__/ \____  $$ \______/
+ *                                  /$$  \ $$
+ *                                 |  $$$$$$/
+ *                                  \______/
+ *
+ * @copyright 2011 TapQuo Inc (c)
+ * @license   http://www.github.com/tapquo/lungo/blob/master/LICENSE.txt
+ * @version   1.0.0
+ * @link      https://github.com/TapQuo/Lungo.js
+ *
+ * @author   Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author   Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+var LUNGO = LUNGO || {};
+
+LUNGO.VERSION = '1.0';
+
+LUNGO.Attributes || (LUNGO.Attributes = {});
+LUNGO.Data || (LUNGO.Data = {});
+LUNGO.Sugar || (LUNGO.Sugar = {});
+LUNGO.View || (LUNGO.View = {});

+ 32 - 0
src/attributes/Lungo.Attributes.Data.js

@@ -0,0 +1,32 @@
+/** 
+ * Object with data-attributes (HTML5) with a special <markup>
+ * 
+ * @namespace LUNGO.Attributes
+ * @class Data
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Attributes.Data = {
+     search: {
+         tag: 'search',
+         selector: '.list',
+         html: '<li class="search {{value}}"><input type="search" placeholder="Search..."><a href="#" class="button" data-icon="search"></a></li>'
+     },
+     icon: {
+         tag: 'icon',
+         selector: '*',
+         html: '<span class="icon {{value}}"></span>'
+     },
+     title: {
+         tag: 'title',
+         selector: 'header, footer',
+         html: '<h1 class="title">{{value}}</h1>'
+     },
+     back: {
+         tag: 'back',
+         selector: 'header, footer',
+         html: '<a href="#back" class="section back onleft button icon {{value}}"></a>'
+     }
+};

+ 20 - 0
src/attributes/Lungo.Attributes.Section.js

@@ -0,0 +1,20 @@
+/** 
+ * Make an analysis of <elements> in a <section>.
+ * 
+ * @namespace LUNGO.Attributes
+ * @class Section
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Attributes.Section = {
+     header: {
+         name: 'header',
+         bind: 'top'
+     },
+     footer: {
+         name: 'footer',
+         bind: 'bottom'
+     }
+};

+ 54 - 0
src/boot/Lungo.Boot.Article.js

@@ -0,0 +1,54 @@
+/** 
+ * Initialize the <article> element
+ * 
+ * @namespace LUNGO.Boot
+ * @class Article
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Boot.Article = (function(lng, undefined) {
+
+    var SELECTORS = {
+        LIST_IN_ARTICLE: 'article.list',
+        SCROLL_IN_ARTICLE: 'scroll'
+    };
+
+    /**
+     * Initializes the markup elements of an article
+     *
+     * @method init
+     */
+    var start = function() {
+        _initElement(SELECTORS.LIST_IN_ARTICLE, _createListElement);
+        _initElement(SELECTORS.SCROLL_IN_ARTICLE, _createScrollElement);
+    };
+
+    var _initElement = function(selector, callback) {
+        var found_elements = lng.Dom.query(selector);
+
+        for (var i = 0, len = found_elements.length; i < len; i++) {
+            var element = lng.Dom.query(found_elements[i]);
+            lng.Core.execute(callback, element);
+        }
+    };
+
+    var _createListElement = function(article) {
+        if (article.children().length === 0) {
+            var article_id = article.attr('id');
+            article.append('<ul id="' + article_id + '_list"></ul>');
+        }
+    };
+
+    var _createScrollElement = function(scroll) {
+        var scroll_id = scroll.attr('id');
+        lng.View.Scroll.create(scroll_id);
+    };
+
+    return {
+        start: start
+    };
+
+})(LUNGO);

+ 45 - 0
src/boot/Lungo.Boot.Data.js

@@ -0,0 +1,45 @@
+/** 
+ * Make an analysis of Data attributes in HTML elements and creates a <markup>
+ * based on each data type.
+ * 
+ * @namespace LUNGO.Boot
+ * @class Data
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Boot.Data = (function(lng, undefined) {
+
+    /**
+     * Initialize the <markup> data-attributes analisys
+     *
+     * @method init
+     *
+     * 
+     */
+    var start = function() {
+        var attributes = lng.Attributes.Data;
+
+        for (var attribute in attributes) {
+            if (lng.Core.isOwnProperty(attributes, attribute)) {
+                _findElements(attributes[attribute]);
+            }
+        }
+    };
+
+    var _findElements = function(attribute) {
+        var elements = lng.Dom.query(attribute.selector);
+
+        for (var i = 0, len = elements.length; i < len; i++) {
+            var element = lng.Dom.query(elements[i]);
+            lng.View.Template.Binding.dataAttribute(element, attribute);
+        }
+    };
+
+    return {
+        start: start
+    };
+
+})(LUNGO);

+ 68 - 0
src/boot/Lungo.Boot.Events.js

@@ -0,0 +1,68 @@
+/** 
+ * Initialize the automatic DOM UI events
+ * 
+ * @namespace LUNGO.Boot
+ * @class Events
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Boot.Events = (function(lng, undefined) {
+
+    /**
+     * Initializes the automatic subscription events by markup of the project.
+     *
+     * @method init
+     *
+     */
+    var start = function() {
+        var touch_move_event  = 'TOUCH_MOVE';
+        var touch_start_event = 'TOUCH_START';
+        var orientation_change = 'ORIENTATION_CHANGE';
+        var events_selector   = 'a[href].section, a[href].article';
+
+        lng.Dom.Event.bind(document, touch_move_event, _iScroll);
+        lng.Dom.Event.bind(window, orientation_change, _changeOrientation);
+        lng.Dom.Event.live(events_selector, touch_start_event, _loadLayout);
+    };
+
+    var _iScroll = function(event) {
+        event.preventDefault();
+    };
+
+    var _changeOrientation = function(event) {
+        lng.View.Resize.toolbars();
+    };
+
+    var _loadLayout = function(event) {
+        event.preventDefault();
+
+        var link = lng.Dom.query(this);
+        var destination_id = link.attr('href');
+
+        if (destination_id.length > 0) {
+            (link.hasClass('section')) ? _goSection(destination_id) : _goArticle(link);
+        }
+    };
+
+    var _goSection = function(id) {
+        if (id === '#back') {
+            lng.Router.back();
+        } else {
+            lng.Router.section(id);
+        }
+    };
+
+    var _goArticle = function(element) {
+        var section_id =  '#' + element.parents('section').attr('id');
+        var article_id =  element.attr('href');
+
+        lng.Router.article(section_id, article_id);
+    };
+
+    return {
+        start: start
+    };
+
+})(LUNGO);

+ 78 - 0
src/boot/Lungo.Boot.Section.js

@@ -0,0 +1,78 @@
+/** 
+ * Initialize the <section> element
+ * 
+ * @namespace LUNGO.Boot
+ * @class Section
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Boot.Section = (function(lng, undefined) {
+
+    var SELECTORS = {
+        ARTICLE: 'article',
+        SECTION: 'section'
+    };
+
+    var ACTIVE_CLASS = 'current';
+
+    /**
+     * Initializes all <section>s of the project
+     *
+     * @method init
+     */
+    var start = function() {
+        var sections = lng.Dom.query(SELECTORS.SECTION);
+        var easing_transition = '-webkit-transform 0.3s ease-in-out';
+
+        _initFirstSection(sections);
+        _initAllSections(sections);
+
+        lng.View.Resize.toolbars();
+        _allocateEasingTransition(sections, easing_transition);
+    };
+
+    var _initFirstSection = function(sections) {
+        var first_section = sections.first();
+        var first_section_id = '#' + first_section.attr('id');
+
+        first_section.addClass(ACTIVE_CLASS);
+
+        lng.Router.History.add(first_section_id);
+    };
+
+    var _initAllSections = function(sections) {
+        for (var i = 0, len = sections.length; i < len; i++) {
+            var section = lng.Dom.query(sections[i]);
+            _initSection(section);
+            _initFirstArticle(section);
+        }
+    };
+
+    var _initSection = function(section) {
+        var section_attributes = lng.Attributes.Section;
+
+        for (var attribute in section_attributes) {
+            if (lng.Core.isOwnProperty(section_attributes, attribute)) {
+                var property = section_attributes[attribute];
+                lng.View.Resize.article(section, property.name, property.bind);
+            }
+        }
+    };
+
+    var _allocateEasingTransition = function(sections, easing) {
+        var transition_property = { '-webkit-transition': easing };
+        sections.css(transition_property);
+    };
+
+    var _initFirstArticle = function(section) {
+        section.children(SELECTORS.ARTICLE).first().addClass(ACTIVE_CLASS);
+    };
+
+    return {
+        start: start
+    };
+
+})(LUNGO);

+ 22 - 0
src/boot/Lungo.Boot.js

@@ -0,0 +1,22 @@
+/** 
+ * Boot for a new LungoJS Application instance
+ * 
+ * @namespace LUNGO
+ * @class App
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Boot = (function(lng, undefined) {
+
+    return function() {
+        lng.Environment.start();
+
+        lng.Boot.Events.start();
+        lng.Boot.Data.start();
+        lng.Boot.Section.start();
+        lng.Boot.Article.start();
+    };
+
+})(LUNGO);

+ 83 - 0
src/data/Lungo.Data.Cache.js

@@ -0,0 +1,83 @@
+/** 
+ * Temporary cache system
+ * 
+ * @namespace LUNGO.Data
+ * @class Cache
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Data.Cache = (function(lng, undefined) {
+
+    var _cache = {};
+
+    /**
+     * Sets in the LungoJS cache system a new key/value
+     *
+     * @method set
+     *
+     * @param {string} Key for the new value
+     * @param {object} Type of environment: DESKTOP_ENVIRONMENT or MOBILE_ENVIRONMENT
+     */
+    var set = function(key, value) {
+        if (exists(key)) {
+            _cache[key] = lng.Core.mix(get(key), value);
+        } else {
+            _cache[key] = value;
+        }
+    };
+
+    /**
+     * Returns the value of a given key.
+     *
+     * @method get
+     *
+     * @param {string} Key in LungoJS Cache System
+     * @param {string} [OPTIONAL] Subkey in LungoJS Cache System
+     * @return {object} Value
+     */
+    var get = function(key, value) {
+        if (arguments.length === 1) {
+            return _cache[key];
+        } else {
+            return _cache[arguments[0]][arguments[1]];
+        }
+    };
+
+    /**
+     * Removes the instance in LungoJs Cache System of a given key
+     *
+     * @method remove
+     *
+     * @param {string} Key in LungoJS Cache System
+     * @param {string} [OPTIONAL] Subkey in LungoJS Cache System
+     */
+    var remove = function(key, value) {
+        if (arguments.length === 1) {
+            delete _cache[key];
+        } else {
+            delete _cache[arguments[0]][arguments[1]];
+        }
+    };
+
+    /**
+     * Returns the existence of a key in LungoJs Cache System
+     *
+     * @method exists
+     *
+     * @param {String} Key in LungoJS Cache System
+     * @return {Boolean} true if exists, false if not
+     */
+    var exists = function(key) {
+        return (_cache[key]) ? true : false;
+    };
+
+    return {
+        set: set,
+        get: get,
+        remove: remove,
+        exists: exists
+    };
+
+})(LUNGO);

+ 203 - 0
src/data/Lungo.Data.Sql.js

@@ -0,0 +1,203 @@
+/** 
+ * Wrapper for using WebSql (HTML5 feature)
+ * 
+ * @namespace LUNGO.Data
+ * @class Sql
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Data.Sql = (function(lng, undefined) {
+
+    var CONFIG = {
+        name: 'lungo_db',
+        version: '1.0',
+        size: 65536,
+        schema: []
+    };
+
+    var db = null;
+
+    /**
+     * Initialize the SQLite storage (HTML5 Feature)
+     *
+     * @method init
+     *
+     * @param {object} Configuration for the Database
+     */
+    var init = function(db_config) {
+        CONFIG = lng.Core.mix(CONFIG, db_config);
+
+        db = openDatabase(CONFIG.name, CONFIG.version, CONFIG.name, CONFIG.size);
+        if (db) {
+            _createSchema();
+        } else {
+            lng.Core.log(3, 'lng.Data.Sql >> Failed to connect to database.');
+        }
+    };
+
+    /**
+     * Select a data set of a given table and based on a selection object
+     *
+     * @method select
+     *
+     * @param {string} Name of the table in the database
+     * @param {object} [OPTIONAL] Object selection condition 
+     * @param {Function} Callback when the process is complete
+     */
+    var select = function(table, where_obj, callback) {
+        var where = (where_obj) ? ' WHERE ' + _convertToSql(where_obj, 'AND') : '';
+
+        execute('SELECT * FROM ' + table + where, function(rs) {
+            var result = [];
+            for (var i = 0, len = rs.rows.length; i < len; i++) {
+                result.push(rs.rows.item(i));
+            }
+
+            _callbackResponse(callback, result);
+        });
+    };
+
+    /**
+     * Inserts a data set of a given table and based on a data object
+     *
+     * @method insert
+     *
+     * @param {string} Name of the table in the database
+     * @param {object} Data object to insert in table
+     */
+    var insert = function(table, data_obj) {
+        var fields = '';
+        var values = '';
+
+        for (var field in data_obj) {
+            if (lng.Core.isOwnProperty(data_obj, field)) {
+                var value = data_obj[field];
+                fields += (fields) ? ', ' + field : field;
+                if (values) values += ', ';
+                values += (isNaN(value)) ? '"' + value + '"' : value;
+            }
+        }
+
+        execute('INSERT INTO ' + table + ' (' + fields + ') VALUES (' + values + ')');
+    };
+
+    /**
+     * Updates a data set of a given table and based on a data object and
+     * an optional selection object
+     *
+     * @method update
+     *
+     * @param {string} Name of the table in the database
+     * @param {object} Data object to update in table
+     * @param {object} [OPTIONAL] Object selection condition 
+     */
+    var update = function(table, data_obj, where_obj) {
+        var sql = 'UPDATE ' + table + ' SET ' + _convertToSql(data_obj, ',');
+        if (where_obj) sql += ' WHERE ' + _convertToSql(where_obj, 'AND');
+
+        execute(sql);
+    };
+
+    /**
+     * Delete a data set of a given table and based on a selection object
+     *
+     * @method drop
+     *
+     * @param {string} Name of the table in the database
+     * @param {object} [OPTIONAL] Object selection condition 
+     */
+    var drop = function(table, where_obj) {
+        var where = (where_obj) ? ' WHERE ' + _convertToSql(where_obj, 'AND') : null;
+
+        execute('DELETE FROM ' + table + where + ';');
+    };
+
+    /**
+     * Executes a SQL statement in the SQLite storage
+     *
+     * @method execute
+     *
+     * @param {string} SQL statement
+     * @param {Function} Callback when the process is complete
+     */
+    var execute = function(sql, callback) {
+        lng.Core.log(1, 'lng.Data.Sql >> ' + sql);
+
+        db.transaction(function(tx) {
+            tx.executeSql(sql, [], function(tx, rs) {
+                _callbackResponse(callback, rs);
+            }, _throwError);
+        });
+    };
+
+    var _createSchema = function() {
+        var schema = CONFIG.schema;
+        var schema_len = schema.length;
+        if (!schema_len) return;
+
+        for (var i = 0; i < schema_len; i++) {
+            var current = schema[i];
+
+            _regenerateTable(current);
+            _createTable(current.name, current.fields);
+        }
+    };
+
+    var _createTable = function(table, fields) {
+        var sql_fields = '';
+        for (var field in fields) {
+            if (lng.Core.isOwnProperty(fields, field)) {
+                if (sql_fields) sql_fields += ', ';
+                sql_fields += field + ' ' + fields[field];
+            }
+        }
+
+        execute('CREATE TABLE IF NOT EXISTS ' + table + ' (' + sql_fields + ');');
+    };
+
+    var _regenerateTable = function(table) {
+        if (table.drop === true) {
+            _dropTable(table.name);
+        }
+    };
+
+    var _dropTable = function(table) {
+        execute('DROP TABLE IF EXISTS ' + table);
+    };
+
+    var _convertToSql = function(fields, separator) {
+        var sql = '';
+
+        for (var field in fields) {
+            if (lng.Core.isOwnProperty(fields, field)) {
+                var value = fields[field];
+                if (sql) sql += ' ' + separator + ' ';
+                sql += field + '=';
+                sql += (isNaN(value)) ? '"' + value + '"' : value;
+            }
+        }
+        return sql;
+    };
+
+    var _callbackResponse = function(callback, response) {
+        if (lng.Core.toType(callback) === 'function') {
+            setTimeout(callback, 100, response);
+        }
+    };
+
+    var _throwError = function(transaction, error) {
+        lng.Core.log(3, 'lng.Data.Sql >> ' + error.code + ': ' + error.message);
+    };
+
+    return {
+        init: init,
+        select: select,
+        insert: insert,
+        update: update,
+        drop: drop,
+        execute: execute
+    };
+
+})(LUNGO);

+ 17 - 0
src/data/Lungo.Data.Storage.js

@@ -0,0 +1,17 @@
+/** 
+ * Wrapper for using LocalStorage & SessionStorage (HTML5 Feature)
+ * 
+ * @namespace LUNGO.Data
+ * @class Storage
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Data.Storage = (function(lng, undefined) {
+
+    return {
+
+    };
+
+})(LUNGO);

+ 150 - 0
src/dom/Lungo.Dom.Event.js

@@ -0,0 +1,150 @@
+/** 
+ * Lungo DOM UI events Manager
+ * 
+ * @namespace LUNGO.Dom
+ * @class Event
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Dom.Event = (function(lng, undefined) {
+
+    /**
+     * Add an event listener
+     *
+     * @method bind
+     *
+     * @param  {string} Selector that dispatches the event
+     * @param  {string} Touch event name
+     * @param  {Function} Callback function after the request
+     */
+    var bind = function(selector, event_name, callback) {
+        if (_isNotSpecialEvent(selector, event_name, callback)) {
+            lng.Dom.query(selector).bind(lng.Events.get(event_name), callback);
+        }
+    };
+
+    /**
+     * Remove bind event listener
+     *
+     * @method unbind
+     *
+     * @param  {string} Selector that dispatches the event
+     * @param  {string} Touch event name
+     */
+    var unbind = function(selector, event_name) {
+        lng.Dom.query(selector).unbind(lng.Events.get(event_name));
+    };
+
+    /**
+     * Add an event listener that listens to the selector for current and future elements
+     *
+     * @method live
+     *
+     * @param  {string} Selector that dispatches the event
+     * @param  {string} Touch event name
+     * @param  {Function} Callback function after the request
+     */
+    var live = function(selector, event_name, callback) {
+        if (_isNotSpecialEvent(selector, event_name, callback)) {
+            lng.Dom.query(selector).live(lng.Events.get(event_name), callback);
+        }
+    };
+
+    /**
+     * Remove live listener
+     *
+     * @method die
+     *
+     * @param  {string} Selector that dispatches the event
+     * @param  {string} Event name
+     */
+    var die = function(selector, event_name) {
+        lng.Dom.query(selector).die(lng.Events.get(event_name));
+    };
+
+    /**
+     * Add an event listener without event delegation
+     *
+     * @method delegate
+     *
+     * @param  {string} Selector that dispatches the event
+     * @param  {string} Children of selector that dispatches the event
+     * @param  {string} Touch event name
+     * @param  {Function} Callback function after the request
+     */
+    var delegate = function(selector, children_selector, event_name, callback) {
+        if (_isNotSpecialEvent(selector, event_name, callback)) {
+            lng.Dom.query(selector).delegate(children_selector, lng.Events.get(event_name), callback);
+        }
+    };
+
+    /**
+     * Remove delegate event listener
+     *
+     * @method undelegate
+     *
+     * @param  {string} Selector that dispatches the event
+     * @param  {string} Children of selector that dispatches the event
+     */
+    var undelegate = function(selector, children_selector) {
+        lng.Dom.query(selector).undelegate(selector);
+    };
+
+    var _isNotSpecialEvent = function(selector, event_name, callback) {
+        var is_special_event = false;
+        /*
+        var SPECIAL_EVENTS = {
+            SWIPE: 'swipe',
+            SWIPE_LEFT: 'swipeLeft',
+            SWIPE_RIGHT: 'swipeRight',
+            SWIPE_UP: 'swipeUp',
+            SWIPE_DOWN: 'swipeDown',
+            DOUBLE_TAP: 'doubleTap'
+        };
+        var special_event = SPECIAL_EVENTS[event_name];
+        lng.Dom.query(selector)[special_event](callback);
+        */
+
+        switch(event_name) {
+            case 'SWIPE':
+                lng.Dom.query(selector).swipe(callback);
+                break;
+            case 'SWIPE_LEFT':
+                lng.Dom.query(selector).swipeLeft(callback);
+                break;
+            case 'SWIPE_RIGHT':
+                lng.Dom.query(selector).swipeRight(callback);
+                break;
+            case 'SWIPE_UP':
+                lng.Dom.query(selector).swipeUp(callback);
+                break;
+            case 'SWIPE_DOWN':
+                lng.Dom.query(selector).swipeDown(callback);
+                break;
+            case 'DOUBLE_TAP':
+                if (lng.Environment.isDesktop()) {
+                    lng.Dom.query(selector).live(lng.Events.get(event_name), callback);
+                } else {
+                    lng.Dom.query(selector).doubleTap(callback);
+                }
+                break;
+            default:
+                is_special_event = true;
+        }
+
+        return is_special_event;
+    };
+
+    return {
+        bind: bind,
+        unbind: unbind,
+        live: live,
+        die: die,
+        delegate: delegate,
+        undelegate: undelegate
+    };
+
+})(LUNGO);

+ 29 - 0
src/dom/Lungo.Dom.js

@@ -0,0 +1,29 @@
+/** 
+ * LungoJS Dom Handler
+ * 
+ * @namespace LUNGO
+ * @class Dom
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Dom = (function(lng, $, undefined) {
+
+    /**
+     * Add an event listener
+     *
+     * @method query
+     *
+     * @param  {string} <Markup> element selector
+     * @return {Object} Zepto <element> instance
+     */
+    var query = function(selector) {
+        return $(selector);
+    };
+
+    return {
+        query: query
+    };
+
+})(LUNGO, Zepto);

文件差異過大導致無法顯示
+ 1044 - 0
src/lib/iscroll.js


文件差異過大導致無法顯示
+ 1193 - 0
src/lib/zepto.js


+ 54 - 0
src/router/Lungo.Router.History.js

@@ -0,0 +1,54 @@
+/** 
+ * Stores the displayed <sections> as a historical.
+ * 
+ * @namespace LUNGO.Router
+ * @class History
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Router.History = (function(undefined) {
+
+     var _history = [];
+
+     /**
+      * Create a new element to the browsing history based on the current section id.
+      *
+      * @method add
+      *
+      * @param  {string} Id of the section
+      */
+     var add = function(section_id) {
+         if (section_id !== current()) {
+             _history.push(section_id);
+         }
+     };
+
+     /**
+      * Returns the current browsing history section id.
+      *
+      * @method current
+      *
+      * @return {string} Current section id
+      */
+     var current = function() {
+         return _history[_history.length - 1];
+     };
+
+     /**
+      * Removes the current item browsing history.
+      *
+      * @method removeLast
+      */
+     var removeLast = function() {
+         _history.length -= 1;
+     };
+
+    return {
+        add: add,
+        current: current,
+        removeLast: removeLast
+    };
+
+})();

+ 69 - 0
src/router/Lungo.Router.js

@@ -0,0 +1,69 @@
+/** 
+ * Handles the <sections> and <articles> to show
+ * 
+ * @namespace LUNGO
+ * @class Router
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.Router = (function(lng, undefined) {
+
+    var CSS_CLASSES = {
+        SHOW: 'show',
+        HIDE: 'hide'
+    };
+
+    /**
+     * Navigate to a <section>.
+     *
+     * @method section
+     *
+     * @param {string} Id of the <section>
+     */
+    var section = function(section_id) {
+        section_id = (section_id.indexOf('#')) ? '#' + section_id : section_id;
+
+        lng.Dom.query(_getHistoryCurrent()).removeClass(CSS_CLASSES.SHOW).addClass(CSS_CLASSES.HIDE);
+        lng.Dom.query(section_id).addClass(CSS_CLASSES.SHOW);
+
+        lng.Router.History.add(section_id);
+    };
+
+    /**
+     * Displays the <article> in a particular <section>.
+     *
+     * @method article
+     *
+     * @param {string} <section> Id
+     * @param {string} <article> Id
+     */
+    var article = function(section_id, article_id) {
+        lng.View.Article.show(section_id, article_id);
+    };
+
+    /**
+     * Return to previous section.
+     *
+     * @method back
+     */
+    var back = function() {
+        lng.Dom.query(_getHistoryCurrent()).removeClass(CSS_CLASSES.SHOW);
+        lng.Router.History.removeLast();
+
+        lng.Dom.query(_getHistoryCurrent()).removeClass(CSS_CLASSES.HIDE).addClass(CSS_CLASSES.SHOW);
+    };
+
+    var _getHistoryCurrent = function() {
+        return lng.Router.History.current();
+    };
+
+    return {
+        section: section,
+        article: article,
+        back: back
+    };
+
+})(LUNGO);

文件差異過大導致無法顯示
+ 6 - 0
src/stylesheets/Lungo.icon.css


二進制
src/stylesheets/Lungo.icon.otf


+ 131 - 0
src/stylesheets/base.css

@@ -0,0 +1,131 @@
+/* ---- HTML5 Reset ---- */
+a,
+abbr,
+address,
+article,
+aside,
+audio,
+b,
+blockquote,
+body,
+caption,
+cite,
+code,
+dd,
+del,
+dfn,
+dialog,
+div,
+dl,
+dt,
+em,
+fieldset,
+figure,
+footer,
+form,
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+header,
+hgroup,
+hr,
+html,
+i,
+iframe,
+img,
+ins,
+kbd,
+label,
+legend,
+li,
+mark,
+menu,
+nav,
+object,
+ol,
+p,
+pre,
+q,
+samp,
+section,
+small,
+span,
+strong,
+sub,
+sup,
+table,
+tbody,
+td,
+tfoot,
+th,
+thead,
+time,
+tr,
+ul,
+var,
+video {
+	border: 0;
+	margin: 0;
+	outline: 0;
+	padding: 0;
+	font-size: 100%;}
+
+a{
+	color: #fff;
+	text-decoration: none;
+	
+	-webkit-tap-highlight-color: rgba(0,0,0,0);}
+
+a:hover{
+	opacity: 1;}
+
+html { 
+	height: 100%;
+	font-size: 100%; 
+	-webkit-text-size-adjust: 100%; 
+	-ms-text-size-adjust: 100%;}
+
+body {
+	margin: 0;
+	padding: 0;
+	width: 100%;
+	height: 100%;
+    min-height: 100%;
+    
+    overflow: hidden;
+    -webkit-touch-callout: none;
+    -webkit-tap-highlight-color: rgba(0,0,0,0);
+    
+    font-family: Helvetica, Arial, sans-serif;
+	font-size: 13px; 
+	line-height: 1.231;}
+
+b,
+strong {
+	font-weight: bold;}
+
+img {
+	border: 0; 
+	-ms-interpolation-mode: bicubic; 
+	vertical-align: middle;
+	
+	font-size: 0;}
+
+table {
+	border-collapse: collapse;
+	border-spacing: 0;}
+
+th,
+td,
+caption {
+	font-weight: normal;
+	vertical-align: top;
+	text-align: left;}
+
+* {
+    margin: 0;
+    padding: 0;}
+

+ 257 - 0
src/stylesheets/layout.css

@@ -0,0 +1,257 @@
+/* @group <section> */
+section {
+	position: absolute;
+	width: 100%; 
+	height: 100%;
+	overflow: hidden;
+	z-index: 0;
+	-webkit-backface-visibility: hidden;
+	-webkit-transform: translate3d(100%, 0, 0);}
+
+section:first-child{ 
+	z-index: 1; 
+	-webkit-transform: translate3d(0%, 0, 0);}
+	
+section.hide{ 
+	z-index: 0; 
+	-webkit-transform: translate3d(-100%, 0, 0);}
+section.show{ 
+	 z-index: 1;
+	-webkit-transform: translate3d(0%, 0, 0);}
+
+section.static.hide{
+	-webkit-transform: translate3d(0%, 0, 0);}
+
+section.popup{
+	-webkit-transform: translate3d(0, 100%, 0);}
+
+section.popup.show{
+	-webkit-transform: translate3d(0, 0%, 0);}
+
+section.popup.hide{
+	-webkit-transform: translate3d(0, -100%, 0);}
+/* @end */
+
+
+/* @group <header> & <footer> */
+header, footer{
+	position: absolute;
+	left: 0px;
+	width: 100%;
+	height: 40px;
+	line-height: 40px;
+	display: block;
+	z-index: 2;}
+	
+header{ 
+	top: 0px
+	}
+	
+footer{ 
+	bottom:0px;
+	}
+	
+header .title, footer .title {
+	float: left; 
+	margin: 0 8px; 
+	font-size: 1.1em;
+	}
+
+section header a.button, section footer a.button{
+	margin: 2px 2px auto;
+	}
+
+section header a.button.icon, section footer a.button.icon{
+	width: 17px;
+	font-size: 1.5em;
+	font-weight: normal;}
+
+/* @end */
+
+/* @group <nav> */
+nav{
+	height: inherit;
+	text-align: center;
+	font-weight: normal;}
+
+nav a{
+	display: inline-block;
+	padding: 0 9px 0 10px;
+	height: inherit;
+	min-width: 22px;	
+	float: left;
+	font-size: 2.0em;}
+
+nav a abbr{
+	position: relative;
+	bottom: 3px;
+	margin-left: 4px;
+	display: none;
+	font-size: 0.5em;
+	font-weight: bold;}
+
+nav a.current abbr{ 
+	display: inline; }
+	
+nav a.current{
+	-webkit-transition: all 0.3s ease-in-out;}
+/* @end */
+
+/* @group <article> */
+article{
+	position: absolute; 
+	width: 100%;
+	height: auto;
+	top: 0px; 
+	bottom: 0px;
+	display: block;
+	z-index: 0;
+	opacity: 0;}
+
+article.current{ 
+	z-index: 1;
+	opacity: 1; }
+/* @end */
+
+/* @group .list */
+.list li:not(.toolbar) {
+	padding: 8px 6px;
+	list-style-type: none;
+	}
+
+.list li:not(.toolbar) a {
+	width: auto; 
+	display: block;
+	}
+
+.list li:not(.toolbar) img, .list li:not(.toolbar) .icon {
+	float: left;
+	width: 32px;
+	height: 32px;
+	margin-right: 6px;	
+	font-size: 36px;
+	font-weight: normal;
+	line-height: 32px;
+	}
+	
+.list strong, .list small {
+	overflow: hidden;  
+	white-space: nowrap;
+	text-overflow: ellipsis;
+	}
+	
+.list strong {
+	font-size: 1.1em;
+	width: 75%;
+	}
+
+.list small {
+	display: block;
+	font-size: 0.9em;
+	font-style: normal;
+	}
+
+
+
+/* @group .anchor */
+.list li.anchor { 	
+	font-size: 0.9em; 
+	font-weight: bold;
+	padding-top: 3px;
+	padding-bottom: 3px;
+	}
+	
+.list .anchor a {
+	padding: 0px;
+	}
+
+.list .anchor .icon{
+	width: 14px !important;
+	height: 14px !important;	
+	margin-right: 4px !important; 
+	
+	font-size:  1.3em !important;
+	font-weight: normal;
+	line-height: 16px !important;	
+}
+
+.list .anchor .bubble{
+	display: none;
+	}
+/* @end */
+
+/* @group .info */
+.list li.info{
+	min-height: 48px;
+	}
+
+.list li.info img, .list li.info .icon{
+	height: 48px;
+	width: 48px;
+	line-height: 48px;
+	}
+/* @end */
+
+
+
+/* @group .search */
+.list .search input{
+	width: 88%;
+	}
+
+.list .search a{
+	float: right;
+	height: 30px;
+	margin: 0px;
+	padding: 0px;
+	}
+
+.list .search a .icon{
+	height: 12px;
+	width: 30px;
+	margin: 0px;
+	font-size: 1.2em;
+	font-weight: normal;
+	}
+/* @end */
+
+/* @group .arrow */
+.list a.arrow:before{
+	float: right;
+	content: ">";
+	position: relative;
+	top: 6px;
+	font-size: 16px;
+	font-family: "Courier New", Courier, mono;
+	font-weight: bold;
+	}
+/* @end */
+
+/* @group .bubble */
+.list .bubble{ 
+	position: relative; 
+	right: 2px;
+	top: 6px;
+	float: right;
+	padding: 2px 3px 3px;	
+	line-height: 1.0em;
+	font-weight: bold; 
+	font-size: 0.85em;
+	}
+/* @end */
+
+/* @end */
+
+/* @group .toolbar */
+.toolbar{
+	height: 48px; 
+	line-height: 44px;
+	display: block;
+	}
+
+.toolbar a {
+	float: left;
+	padding: 0;
+	font-size: 2.0em;
+	}
+/* @end */

+ 195 - 0
src/stylesheets/lungo.theme.default.css

@@ -0,0 +1,195 @@
+/* @group layout */
+.app {
+	font-size:13px;
+    background: #000;
+	}
+
+article{
+	background: #fff;
+	}
+	
+/* @group <header> & <footer> */
+header{
+	background: #42474d -webkit-gradient(linear, left top, left bottom, color-stop(0.25, #42474d), color-stop(1, #2c3137));
+	-webkit-box-shadow: 
+		inset 0px 1px 0px rgba(255,255,255,0.25), 
+		inset 0px -1px rgba(0,0,0,0.5), 
+		0px 2px 2px rgba(0,0,0,0.2);}
+	
+footer{
+	background: #2c2c2d -webkit-gradient(linear, left top, left bottom, color-stop(0.25, #2c2c2d), color-stop(1, #1c1d1d));
+	
+	-webkit-box-shadow: 
+		inset 0px 1px rgba(0,0,0,0.5), 
+		0px -1px 2px rgba(0,0,0,0.4);}
+
+.title{
+	color: #fff;
+	text-shadow: rgba(0,0,0,0.5) 0 -1px 1px; 
+	text-transform: uppercase;}
+/* @end */
+
+/* @group <nav> */
+nav a {
+	background: none;
+	color: rgba(0,0,0,0.5);
+	text-shadow: rgba(255,255,255,0.35) 0 1px 1px;}
+	
+header:not(.toolbar) nav a, footer:not(.toolbar) nav a{
+	text-shadow: none;
+	
+	-webkit-box-shadow: 
+	inset 0px 1px rgba(255,255,255,0.15), 
+	inset 1px 0px rgba(0,0,0,0.75), 
+	inset 2px 0px rgba(255,255,255,0.25);
+}
+
+nav a.current{
+	background: rgba(255,255,255,0.15);
+	color: #fff;
+	text-shadow: rgba(0,0,0,1) 0 1px 1px;
+	text-shadow: none;
+	}	
+
+//@ToDo: Change the colour for not .toolbar classed elements
+not(.toolbar) nav a.current{
+	
+}
+
+.toolbar a:first-child{	-webkit-box-shadow: none; }
+	
+/* @end */
+
+/* @group .list */
+.list {
+	background: #c5c5c5;
+	}
+
+.list li:not(.info){
+	background: #fff;
+	border-bottom: 1px inset #c3c4c6;
+	color: #333;
+	}
+	
+.list li.toolbar{
+	border-bottom-color: rgba(0,0,0,0.2);
+	}
+
+.list li:not(.toolbar) a { 
+	color: #333;
+	text-shadow: #fff 0px 1px 1px; 
+	-webkit-tap-highlight-color: #242525;
+	}
+
+.list strong {
+	color: #2343be;
+	}
+	
+.list small{
+	opacity: 0.75;
+	}
+	
+.list .bubble{
+	border: 1px solid rgba(0,0,0,0.15);
+	-webkit-border-radius: 2px;
+	color: #fff; 
+	text-shadow: 0 -1px 0 rgba(0,0,0,.3);
+	}	
+.list .button{
+	color: #fff;
+	text-shadow: 0 -1px 0 rgba(0,0,0,.3); 
+}
+
+.list .arrow:before{
+	color: #a9a9a9;}
+
+
+
+/* @group .anchor */
+.list li.anchor {
+	background: #e1e1e1;
+	color: #222;
+	}
+/* @end */
+
+
+/* @group .anchor */
+.list li.search {
+	border: none;
+	-webkit-box-shadow: 
+		inset 0px 1px 0px rgba(255,255,255,0.1), 
+		inset 0px -1px 0px rgba(0,0,0,0.25);
+	}
+/* @end */
+
+/* @group .info */
+.list li.info {	
+	border: none;
+	-webkit-box-shadow: 
+		inset 0px 1px 0px rgba(255,255,255,0.1), 
+		inset 0px -1px 0px rgba(0,0,0,0.25);
+	background: #cbcbcb;
+	}
+	
+.list .info strong{	
+	color: #333;
+	}
+
+.list .info.contrast strong{	
+	color: #fff;
+	}
+/* @end */
+/* @end */
+.list .contrast{
+	background: #2c2c2d -webkit-gradient(linear, left top, left bottom, color-stop(0.25, #2c2c2d), color-stop(1, #1c1d1d)) !important;
+	text-shadow: 0px 1px 1px rgba(0,0,0,0.25);
+	color: #fff;
+}
+
+.list .highlight{
+	background: #499dc4 -webkit-gradient(linear, left top, left bottom, color-stop(0, #499dc4), color-stop(1, #0e8eb6)) !important;
+	}
+	
+/* @end */
+
+/* @group .classes */
+/* @end */
+
+/* @group widgets */
+
+/* @group <section>.splash */
+.splash article { 
+	background: #2c2c2d -webkit-gradient(linear, left top, left bottom, color-stop(0.25, #2c2c2d), color-stop(1, #1c1d1d)) !important;
+	color: #fff; 
+	text-shadow: rgba(0,0,0,0.5) 0 1px 1px;}
+/* @end */
+
+/* @group <inputs> */
+input, textarea, select {
+	border: 1px solid #ccc;	
+	-webkit-border-radius: 2px;
+	background: #fff -webkit-gradient(linear, left top, left bottom, color-stop(0, #ddd), color-stop(0.15, #fff));}
+	
+input:focus, textarea:focus, select:focus { 
+	border: 1px solid #058cf5;
+	-webkit-box-shadow: 0 0 4px 1px #058cf5; 
+	background: #e8fefe;}
+	
+.select:after {
+	background: #ccc;
+	color: white;
+	-webkit-border-radius: 0 2px 2px 0;}
+/* @end */
+	
+
+
+/* @group .counter (BETA) */
+.counter{
+	background: #ee2c23;
+	-webkit-border-radius: 2px; 
+	-moz-border-radius: 2px; 
+	
+	color: #fff;
+}
+/* @end */
+/* @end */

文件差異過大導致無法顯示
+ 402 - 0
src/stylesheets/widgets.css


+ 56 - 0
src/view/Lungo.View.Article.js

@@ -0,0 +1,56 @@
+/** 
+ * Initialize the <articles> layout of a certain <section>
+ * 
+ * @namespace LUNGO.View
+ * @class Article
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.View.Article = (function(lng, undefined) {
+
+    var SELECTORS = {
+        ARTICLE: 'article',
+        NAVIGATION_ITEM: 'nav a'
+    };
+
+    var CSS_CLASSES = {
+        ACTIVE: 'current'
+    };
+
+    var show = function(section_id, article_id) {
+        var nav_items = section_id + ' ' + SELECTORS.NAVIGATION_ITEM;
+        _disableNavItems(nav_items);
+
+        var current_nav_item = lng.Dom.query(nav_items + '[href="' + article_id + '"]');
+        current_nav_item.addClass(CSS_CLASSES.ACTIVE);
+        _setTitle(section_id, current_nav_item);
+
+        _showContainer(section_id, article_id);
+    };
+
+    var _disableNavItems = function(items) {
+        lng.Dom.query(items).removeClass(CSS_CLASSES.ACTIVE);
+    };
+
+    var _showContainer = function(section_id, article_id) {
+        var section_articles = section_id + ' ' + SELECTORS.ARTICLE;
+        lng.Dom.query(section_articles).removeClass(CSS_CLASSES.ACTIVE);
+        lng.Dom.query(article_id).addClass(CSS_CLASSES.ACTIVE);
+    };
+
+    var _setTitle = function(id, item) {
+        var title = item.data('title');
+
+        if (title) {
+            var section_title = id + ' header .title, ' + id + ' footer .title';
+            lng.Dom.query(section_title).text(title);
+        }
+    };
+
+    return {
+        show: show
+    };
+
+})(LUNGO);

+ 87 - 0
src/view/Lungo.View.Resize.js

@@ -0,0 +1,87 @@
+/** 
+ * 
+ * 
+ * @namespace LUNGO.View
+ * @class Resize
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.View.Resize = (function(lng, undefined) {
+
+    /**
+     * Rezise a <scroll> element
+     *
+     * @method scroll
+     *
+     * @param {object} Object reference of a determinated <section>
+     */
+    var scroll = function(scroll) {
+        var container = scroll.children().first();
+        var child = container.children().first();
+
+        if (lng.View.Scroll.isHorizontal(scroll)) {
+            _resizeScrollContainerWidth(container, child);
+        } else {
+            _resizeScrollContainerHeight(scroll, container, child);
+        }
+    };
+
+    /**
+     * Resize all <article>s from determinated <section> based on a CSS property.
+     *
+     * @method article
+     *
+     * @param {object} Object reference of a determinated <section>
+     * @param {string} Selector that refers to a section element 
+     * @param {string} CSS property
+     */
+    var article = function(section, selector, property) {
+        var element = section.children(selector);
+        var ARTICLE = 'article'; //@todo >> refactor
+
+        if (element) {
+            section.children(ARTICLE).css(property, element.height() + 'px');
+        }
+    };
+
+    /**
+     * Sets toolbars width, using total screen width
+     *
+     * @method toolbars
+     */
+    var toolbars = function() {
+        var toolbar = '.toolbar nav';
+        var all_toolbars = lng.Dom.query(toolbar);
+
+        for (var i = 0, len = all_toolbars.length; i < len; i++) {
+            var toolbar = lng.Dom.query(all_toolbars[i]);
+            var toolbar_children = toolbar.children();
+            var toolbar_children_width = (toolbar.width() / toolbar_children.length);
+
+            toolbar_children.css('width', toolbar_children_width + 'px');
+        }
+    };
+
+    var _resizeScrollContainerWidth = function(container, child) {
+        var scroll_width = (container.children().length * child.width());
+        container.css('width', scroll_width + 'px');
+    };
+
+    var _resizeScrollContainerHeight = function(scroll, container, child) {
+        var total_children = container.children().length;
+        var children_in_scroll_width = Math.floor(scroll.width() / child.width());
+        var total_rows = Math.ceil(total_children / children_in_scroll_width);
+
+        var scroll_height = (total_rows * child.height());
+        container.css('height', scroll_height + 'px');
+    };
+
+    return {
+        scroll: scroll,
+        article: article,
+        toolbars: toolbars
+    };
+
+})(LUNGO);

+ 136 - 0
src/view/Lungo.View.Scroll.js

@@ -0,0 +1,136 @@
+/** 
+ * Wrapper of the third library iScroll
+ * 
+ * @namespace LUNGO.View
+ * @class Scroll
+ * @requires Zepto
+ * @requires iScroll
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.View.Scroll = (function(lng, undefined) {
+
+    var DEFAULT_PROPERTIES = {
+        hScroll: false,
+        vScroll: false,
+        useTransition: true,
+        momentum: true,
+        lockDirection: true,
+        fixedScrollbar: true,
+        fadeScrollbar: true,
+        hideScrollbar: true
+    };
+
+    var HORIZONTAL_CLASS = 'horizontal';
+
+    var CACHE_KEY = 'scrolls';
+
+    /**
+     * Creates a new iScroll element.
+     *
+     * @method create
+     *
+     * @param {string} Id of the container scroll.
+     * @param {object} [OPTIONAL] Properties
+     */
+    var create = function(id, properties) {
+        if (id) {
+            var scroll = lng.Dom.query('#' + id);
+            var scroll_children = scroll.children();
+            var need_scroll = (scroll_children.height() >= scroll.height());
+
+            if (scroll_children.length > 0 && need_scroll) {
+                properties = _mixProperties(scroll, properties);
+                _saveScrollInCache(id, properties);
+            }
+        } else {
+            lng.Core.log(3, 'ERROR: Impossible to create a <scroll> without ID');
+        }
+    };
+
+    /**
+     * Update iScroll element with new <markup> content.
+     *
+     * @method update
+     *
+     * @param {string} Id of the container scroll.
+     * @param {string} Markup content
+     */
+    var update = function(id, content) {
+        var scroll = lng.Dom.query('#' + id);
+        var container = scroll.children().first();
+
+        if (container.length === 0) {
+            scroll.html('<div id="' + id + '_scrl"></div>');
+            container = scroll.children().first();
+        }
+        container.html(content);
+
+        lng.View.Resize.scroll(scroll);
+        _refresh(id);
+    };
+
+    /**
+     * Removes iScroll instance.
+     *
+     * @method remove
+     *
+     * @param {string} Id of the <section>
+     */
+    var remove = function(id) {
+        if (lng.Data.Cache.exists(CACHE_KEY)) {
+            lng.Data.Cache.get(CACHE_KEY, id).destroy();
+            lng.Data.Cache.remove(CACHE_KEY, id);
+        }
+    };
+
+    /**
+     * Removes iScroll instance.
+     *
+     * @method scrollIsHorizontal
+     *
+     * @param {Object} Id of the <section>
+     */
+    var isHorizontal = function(scroll) {
+        return (scroll.hasClass(HORIZONTAL_CLASS)) ? true : false;
+    };
+
+    var _saveScrollInCache = function(id, properties) {
+        _createScrollIndexInCache();
+
+        var scroll = lng.Data.Cache.get(CACHE_KEY);
+        scroll[id] = new iScroll(id, properties);
+        lng.Data.Cache.set(CACHE_KEY, scroll);
+    };
+
+    var _createScrollIndexInCache = function() {
+        if (!lng.Data.Cache.exists(CACHE_KEY)) {
+            lng.Data.Cache.set(CACHE_KEY, {});
+        }
+    }
+
+    var _mixProperties = function(scroll, properties) {
+        var scroll_type = isHorizontal(scroll) ? 'hScroll' : 'vScroll';
+
+        properties || (properties = {});
+        properties[scroll_type] = true;
+        properties = lng.Core.mix(DEFAULT_PROPERTIES, properties);
+
+        return properties;
+    };
+
+    var _refresh = function(id, properties) {
+        !lng.Data.Cache.get(CACHE_KEY, id) && _saveScrollInCache(id);
+        lng.Data.Cache.get(CACHE_KEY, id).refresh();
+    };
+
+    return {
+        create: create,
+        update: update,
+        remove: remove,
+        isHorizontal: isHorizontal
+    };
+
+})(LUNGO);

+ 92 - 0
src/view/Lungo.View.Template.Binding.js

@@ -0,0 +1,92 @@
+/** 
+ * Lungo Data-Binding system
+ * 
+ * @namespace LUNGO.View.Template
+ * @class Binding
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+ 
+LUNGO.View.Template.Binding = (function(lng, undefined) {
+
+    var BINDING_START = '{{';
+    var BINDING_END = '}}';
+    var BINDING_PARSER = /\{{.*?\}}/gi;
+
+    /**
+     * Performs databinding process for a data set and a given template
+     *
+     * @method create
+     *
+     * @param {String} Id of the container showing the result of databinding
+     * @param {Number} Databinding Template Id
+     * @param {Object} Data for binding
+     * @param {Function} Callback when the process is complete
+     */
+    var create = function(container_id, id, data, callback) {
+        if (lng.View.Template.exists(id)) {
+            var template = lng.View.Template.get(id);
+            var markup = _processData(data, template);
+            _render(container_id, markup);
+            lng.Core.execute(callback);
+        } else {
+            lng.Core.log(3, 'lng.View.Template.binding: id ' + id + ' not exists');
+        }
+    };
+
+    var dataAttribute = function(element, attribute) {
+        var data = element.data(attribute.tag);
+
+        if (data) {
+            var html_binded = attribute.html.replace(BINDING_START + 'value' + BINDING_END, data);
+            element.prepend(html_binded);
+        }
+    };
+
+    var _processData = function(data, template) {
+        var data_type = lng.Core.toType(data);
+
+        if (data_type === 'array') {
+            return _bindPropertiesInMultiplesElements(data, template);
+        } else if (data_type === 'object') {
+            return _bindProperties(data, template);
+        } else {
+            lng.Core.log(3, 'View.Template ERROR >> No type defined.');
+        }
+    };
+
+    var _bindPropertiesInMultiplesElements = function(elements, template) {
+        var markup = '';
+        for (var i = 0, len = elements.length; i < len; i++) {
+            markup += _bindProperties(elements[i], template);
+        }
+        return markup;
+    };
+
+    var _bindProperties = function(element, template) {
+        for (var property in element) {
+            if (lng.Core.isOwnProperty(element, property)) {
+                template = template.replace(BINDING_START + property + BINDING_END, element[property]);
+            }
+        }
+
+        return _removeNoBindedProperties(template);
+    };
+
+    var _removeNoBindedProperties = function(template) {
+        return template.replace(BINDING_PARSER, '');
+    };
+
+    var _render = function(container_id, markup) {
+        var container = lng.Dom.query('#' + container_id);
+        container.html(markup);
+    };
+
+    return {
+        create: create,
+        dataAttribute: dataAttribute
+    };
+
+})(LUNGO);

+ 83 - 0
src/view/Lungo.View.Template.List.js

@@ -0,0 +1,83 @@
+/** 
+ * Auto generate lists based on Template and Data-Binding system
+ * 
+ * @namespace LUNGO.View.Template
+ * @class List
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+
+LUNGO.View.Template.List = (function(lng, undefined) {
+
+    var _config = null;
+
+    /**
+     * Create a list based DataBind with a configuration object for an element <article>
+     *
+     * @method create
+     *
+     * @param {object} Id of the container showing the result of databinding
+     */
+    var create = function(config) {
+        _config = config;
+        _config.container_id += '_list';
+
+        if (_validateConfig()) {
+            _order();
+            // @ToDo >> _group();
+            _render();
+            _createScroll();
+        }
+    };
+
+    var _validateConfig = function() {
+        var checked = false;
+        var container_exists = !! lng.Dom.query(_config.container_id);
+
+        if (container_exists) {
+            //@ToDo >> Refactor to other method
+            lng.Dom.query("#"+_config.container_id).html('');
+
+            var template_exists = lng.View.Template.exists(_config.template_id);
+            if (template_exists && _config.data.length) {
+                checked = true;
+            }
+        }
+
+        return checked;
+    };
+
+    var _order = function() {
+        var order_field = _config.order_field;
+        var order_type  = (_config.order_type === 'desc') ? -1 : 1;
+
+        if (order_field && order_type) {
+            _config.data.sort(function(a, b) {
+                return (a[order_field] < b[order_field]) ? - order_type :
+                       (a[order_field] > b[order_field]) ? order_type : 0;
+            });
+        }
+    };
+
+    // @ToDo >> group list by property
+    var _group = function() {
+    };
+
+    var _render = function() {
+        lng.View.Template.Binding.create(_config.container_id, _config.template_id, _config.data);
+    };
+
+    var _createScroll = function() {
+        var container_id_for_scroll = lng.Dom.query('#' + _config.container_id).parent().attr('id');
+        var list_config = {snap:'li'};
+
+        lng.View.Scroll.create(container_id_for_scroll, list_config);
+    };
+
+    return {
+        create: create
+    };
+
+})(LUNGO);

+ 58 - 0
src/view/Lungo.View.Template.js

@@ -0,0 +1,58 @@
+/** 
+ * Lungo Template system
+ * 
+ * @namespace LUNGO.View
+ * @class Template
+ * @requires Zepto
+ *
+ * @author Javier Jimenez Villar <javi@tapquo.com> || @soyjavi
+ * @author Guillermo Pascual <pasku@tapquo.com> || @pasku1
+ */
+ 
+LUNGO.View.Template = (function(undefined) {
+
+    var _templates = {};
+
+    /**
+     * Create a new databinding template based on a <markup>
+     *
+     * @method create
+     *
+     * @param {String} Id of the new databinding template
+     * @param {String} <markup> of the new databinding template
+     */
+    var create = function(id, markup) {
+        _templates[id] = markup;
+    };
+
+    /**
+     * Returns the existence of a certain Id databinding template
+     *
+     * @method exists
+     *
+     * @param {String} Id of the databinding template
+     * @return {Boolean} true if exists, false if not.
+     */
+    var exists = function(id) {
+        return (_templates[id]) ? true : false;
+    };
+
+    /**
+     * Returns the instance of a certain Id databinding template
+     *
+     * @method get
+     *
+     * @param {String} Id of the databinding template
+     * @return {String} Markup of template
+     */
+    var get = function(id) {
+        return _templates[id];
+    };
+
+    return {
+        create: create,
+        exists: exists,
+        get: get
+    };
+
+})();

+ 137 - 0
vendor/build.sh

@@ -0,0 +1,137 @@
+#!/bin/bash
+VERSION="1.0.0"
+
+#define paths
+COMPILER=google-compiler/compiler.jar
+COMPRESSOR=yuicompressor/yuicompressor-2.4.2.jar
+LUNGO_SOURCES=../src/
+LUNGO_NAMESPACE=Lungo.
+BUILDPATH=../release/
+MINIFIED="min"
+PACKED="packed"
+
+#script
+clear
+echo -e "\033[0m"============================ LUNGO COMPILER ============================
+echo -e "Do you wish to compile LungoJS Framework? (Y)es or (N)o?: \c "
+read WISH
+if [[ $WISH == "Y" || $WISH == "y" ]] ; then
+    ## Files to compile
+    FILES_TO_COMPILE=""
+    FILES_TO_JOIN=""
+    
+    #Main
+    DIR=$LUNGO_SOURCES$LUNGO_NAMESPACE
+    echo -e "\033[33m  [DIR]: "$LUNGO_SOURCES
+    FILES=(js App.js Environment.js Core.js Events.js Service.js)
+    for file in "${FILES[@]}"
+    do
+        FILES_TO_COMPILE=$FILES_TO_COMPILE" --js "$DIR$file
+        FILES_TO_JOIN=$FILES_TO_JOIN" "$DIR$file
+    done
+
+    #Router
+    DIR=$LUNGO_SOURCES"router/"$LUNGO_NAMESPACE"Router."
+    echo -e "\033[33m  [DIR]: "$LUNGO_SOURCES"router/"
+    FILES=(js History.js)
+    for file in "${FILES[@]}"
+    do  
+        FILES_TO_COMPILE=$FILES_TO_COMPILE" --js "$DIR$file
+        FILES_TO_JOIN=$FILES_TO_JOIN" "$DIR$file
+    done
+
+    #View
+    DIR=$LUNGO_SOURCES"view/"$LUNGO_NAMESPACE"View."
+    echo -e "\033[33m  [DIR]: "$LUNGO_SOURCES"view/"
+    FILES=(Article.js Resize.js Template.js Template.Binding.js Template.List.js Scroll.js)
+    for file in "${FILES[@]}"
+    do  
+        FILES_TO_COMPILE=$FILES_TO_COMPILE" --js "$DIR$file
+        FILES_TO_JOIN=$FILES_TO_JOIN" "$DIR$file
+    done
+
+    #Dom
+    DIR=$LUNGO_SOURCES"dom/"$LUNGO_NAMESPACE"Dom."
+    echo -e "\033[33m  [DIR]: "$LUNGO_SOURCES"dom/"
+    FILES=(js Event.js)
+    for file in "${FILES[@]}"
+    do  
+        FILES_TO_COMPILE=$FILES_TO_COMPILE" --js "$DIR$file
+        FILES_TO_JOIN=$FILES_TO_JOIN" "$DIR$file
+    done
+    
+    #Attributes
+    DIR=$LUNGO_SOURCES"attributes/"$LUNGO_NAMESPACE"Attributes."
+    echo -e "\033[33m  [DIR]: "$LUNGO_SOURCES"attributes/"
+    FILES=(Data.js Section.js)
+    for file in "${FILES[@]}"
+    do  
+        FILES_TO_COMPILE=$FILES_TO_COMPILE" --js "$DIR$file
+        FILES_TO_JOIN=$FILES_TO_JOIN" "$DIR$file
+    done
+
+    #Data
+    DIR=$LUNGO_SOURCES"data/"$LUNGO_NAMESPACE"Data."
+    echo -e "\033[33m  [DIR]: "$LUNGO_SOURCES"data/"
+    FILES=(Cache.js Sql.js Storage.js)
+    for file in "${FILES[@]}"
+    do  
+        FILES_TO_COMPILE=$FILES_TO_COMPILE" --js "$DIR$file
+        FILES_TO_JOIN=$FILES_TO_JOIN" "$DIR$file
+    done
+
+    #Dom
+    DIR=$LUNGO_SOURCES"boot/"$LUNGO_NAMESPACE"Boot."
+    echo -e "\033[33m  [DIR]: "$LUNGO_SOURCES"boot/"
+    FILES=(js Article.js Data.js Events.js Section.js)
+    for file in "${FILES[@]}"
+    do  
+        FILES_TO_COMPILE=$FILES_TO_COMPILE" --js "$DIR$file
+        FILES_TO_JOIN=$FILES_TO_JOIN" "$DIR$file
+    done
+
+    #UNCOMPRESED Version
+    cat $FILES_TO_JOIN > $BUILDPATH/lungo-$VERSION.js
+    echo -e "\033[32m  [BUILD]: lungo-"$VERSION.js"\033[0m"
+    
+    #MINIFIED Version
+    java -jar $COMPILER $FILES_TO_COMPILE --js_output_file $BUILDPATH/lungo-$VERSION.$MINIFIED.js
+    echo -e "\033[32m  [BUILD]: lungo-"$VERSION.$MINIFIED.js"\033[0m"
+    
+    #PACKED Version
+    FILES_TO_COMPILE=" --js "$LUNGO_SOURCES"lib/zepto.js --js "$LUNGO_SOURCES"lib/iscroll.js"$FILES_TO_COMPILE
+    java -jar $COMPILER $FILES_TO_COMPILE --js_output_file $BUILDPATH/lungo-$VERSION.$PACKED.js
+    echo -e "\033[32m  [BUILD]: lungo-"$VERSION.$PACKED.js"\033[0m"
+fi
+
+FILES_TO_COMPRESS=""
+echo -e "Do you wish to compress your STYLESHEETS? (Y)es or (N)o?: \c "
+read WISH
+if [[ $WISH == "Y" || $WISH == "y" ]] ; then
+	DIR=$LUNGO_SOURCES"stylesheets/"
+    echo -e "\033[33m  [DIR]: "$DIR
+    FILES=(base layout widgets)
+    for file in "${FILES[@]}"
+    do  
+        echo "    - Compressing "$DIR$file".css ..."
+        java -jar $COMPRESSOR $DIR$file".css" -o $DIR$file".min.css"
+        FILES_TO_COMPRESS=$FILES_TO_COMPRESS" "$DIR$file".min.css"
+    done
+	cat $FILES_TO_COMPRESS > $BUILDPATH/lungo-$VERSION.$MINIFIED.css
+
+	for file in "${FILES[@]}" 
+	do 
+	    rm $LUNGO_SOURCES"stylesheets/"$file".min.css" 
+	done
+	
+	DIR=$LUNGO_SOURCES"stylesheets/"
+	FILES=(default.css)
+	echo -e "\033[33m  [DIR]: "$DIR
+	for file in "${FILES[@]}"
+	do
+		echo "   - [THEME] "$file
+		cp $DIR"lungo.theme."$file $BUILDPATH'lungo.theme.'$file
+	done
+	echo -e "\033[32m  [BUILD]: lungo-"$VERSION.$MINIFIED".css\033[0m"
+fi
+echo ============================ /LUNGO COMPILER ============================

+ 202 - 0
vendor/google-compiler/COPYING

@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 289 - 0
vendor/google-compiler/README

@@ -0,0 +1,289 @@
+/*
+ * Copyright 2009 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Contents
+//
+
+The Closure Compiler performs checking, instrumentation, and
+optimizations on JavaScript code. The purpose of this README is to
+explain how to build and run the Closure Compiler.
+
+The Closure Compiler requires Java 6 or higher.
+http://www.java.com/
+
+
+//
+// Building The Closure Compiler
+//
+
+There are three ways to get a Closure Compiler executable.
+
+1) Use one we built for you.
+
+Pre-built Closure binaries can be found at
+http://code.google.com/p/closure-compiler/downloads/list
+
+
+2) Check out the source and build it with Apache Ant.
+
+First, check out the full source tree of the Closure Compiler. There
+are instructions on how to do this at the project site.
+http://code.google.com/p/closure-compiler/source/checkout
+
+Apache Ant is a cross-platform build tool.
+http://ant.apache.org/
+
+At the root of the source tree, there is an Ant file named
+build.xml. To use it, navigate to the same directory and type the
+command
+
+ant jar
+
+This will produce a jar file called "build/compiler.jar".
+
+
+3) Check out the source and build it with Eclipse.
+
+Eclipse is a cross-platform IDE.
+http://www.eclipse.org/
+
+Under Eclipse's File menu, click "New > Project ..." and create a
+"Java Project."  You will see an options screen. Give the project a
+name, select "Create project from existing source," and choose the
+root of the checked-out source tree as the existing directory. Verify
+that you are using JRE version 6 or higher.
+
+Eclipse can use the build.xml file to discover rules. When you
+navigate to the build.xml file, you will see all the build rules in
+the "Outline" pane. Run the "jar" rule to build the compiler in
+build/compiler.jar.
+
+
+//
+// Running The Closure Compiler
+//
+
+Once you have the jar binary, running the Closure Compiler is straightforward.
+
+On the command line, type
+
+java -jar compiler.jar
+
+This starts the compiler in interactive mode. Type
+
+var x = 17 + 25;
+
+then hit "Enter", then hit "Ctrl-Z" (on Windows) or "Ctrl-D" (on Mac or Linux)
+and "Enter" again. The Compiler will respond:
+
+var x=42;
+
+The Closure Compiler has many options for reading input from a file,
+writing output to a file, checking your code, and running
+optimizations. To learn more, type
+
+java -jar compiler.jar --help
+
+You can read more detailed documentation about the many flags at
+http://code.google.com/closure/compiler/docs/gettingstarted_app.html
+
+
+//
+// Compiling Multiple Scripts
+//
+
+If you have multiple scripts, you should compile them all together with
+one compile command.
+
+java -jar compiler.jar --js=in1.js --js=in2.js ... --js_output_file=out.js
+
+The Closure Compiler will concatenate the files in the order they're
+passed at the command line.
+
+If you need to compile many, many scripts together, you may start to
+run into problems with managing dependencies between scripts. You
+should check out the Closure Library. It contains functions for
+enforcing dependencies between scripts, and a tool called calcdeps.py
+that knows how to give scripts to the Closure Compiler in the right
+order.
+
+http://code.google.com/p/closure-library/
+
+//
+// Licensing
+//
+
+Unless otherwise stated, all source files are licensed under
+the Apache License, Version 2.0.
+
+
+-----
+Code under:
+src/com/google/javascript/rhino
+test/com/google/javascript/rhino
+
+URL: http://www.mozilla.org/rhino
+Version:  1.5R3, with heavy modifications
+License:  Netscape Public License and MPL / GPL dual license
+
+Description: A partial copy of Mozilla Rhino. Mozilla Rhino is an
+implementation of JavaScript for the JVM.  The JavaScript parser and
+the parse tree data structures were extracted and modified
+significantly for use by Google's JavaScript compiler.
+
+Local Modifications: The packages have been renamespaced. All code not
+relavant to parsing has been removed. A JSDoc parser and static typing
+system have been added.
+
+
+-----
+Code in:
+lib/libtrunk_rhino_parser_jarjared.jar
+
+Rhino
+URL: http://www.mozilla.org/rhino
+Version:  Trunk
+License:  Netscape Public License and MPL / GPL dual license
+
+Description: Mozilla Rhino is an implementation of JavaScript for the JVM.
+
+Local Modifications: None. We've used JarJar to renamespace the code
+post-compilation. See:
+http://code.google.com/p/jarjar/
+
+
+-----
+Code in:
+lib/args4j_deploy.jar
+
+Args4j
+URL: https://args4j.dev.java.net/
+Version: 2.0.9
+License: MIT
+
+Description:
+args4j is a small Java class library that makes it easy to parse command line
+options/arguments in your CUI application.
+
+Local Modifications: None.
+
+
+-----
+Code in:
+lib/guava-r06.jar
+
+Guava Libraries
+URL: http://code.google.com/p/guava-libraries/
+Version:  R6
+License: Apache License 2.0
+
+Description: Google's core Java libraries.
+
+Local Modifications: None.
+
+
+-----
+Code in:
+lib/hamcrest-core-1.1.jar
+
+Hamcrest
+URL: http://code.google.com/p/hamcrest
+License: BSD
+License File: LICENSE
+
+Description:
+Provides a library of matcher objects (also known as constraints or
+predicates) allowing 'match' rules to be defined declaratively, to be used in
+other frameworks. Typical scenarios include testing frameworks, mocking
+libraries and UI validation rules.
+
+Local modifications:
+The original jars contained both source code and compiled classes.
+
+hamcrest-core-1.1.jar just contains the compiled classes.
+
+
+
+-----
+Code in:
+lib/jsr305.jar
+
+Annotations for software defect detection
+URL: http://code.google.com/p/jsr-305/
+Version: svn revision 47
+License: BSD License
+
+Description: Annotations for software defect detection.
+
+Local Modifications: None.
+
+
+----
+Code in:
+lib/junit.jar
+
+JUnit
+URL:  http://sourceforge.net/projects/junit/
+Version:  4.5
+License:  Common Public License 1.0
+
+Description: A framework for writing and running automated tests in Java.
+
+Local Modifications: None.
+
+
+---
+Code in:
+lib/protobuf-java-2.3.0.jar
+
+Protocol Buffers
+URL: http://code.google.com/p/protobuf/
+Version: 2.3.0
+License: New BSD License
+
+Description: Supporting libraries for protocol buffers,
+an encoding of structured data.
+
+Local Modifications: None
+
+
+---
+Code in:
+lib/ant_deploy.jar
+
+URL: http://ant.apache.org/bindownload.cgi
+Version: 1.6.5
+License: Apache License 2.0
+Description:
+  Ant is a Java based build tool. In theory it is kind of like "make"
+  without make's wrinkles and with the full portability of pure java code.
+
+Local Modifications:
+  Modified apache-ant-1.6.5/bin/ant to look in the ant.runfiles directory
+
+
+---
+Code in:
+lib/json.jar
+URL: http://json.org/java/index.html
+Version: JSON version 2
+License: MIT license
+Description:
+JSON is a set of java files for use in transmitting data in JSON format.
+
+Local Modifications: None
+

二進制
vendor/google-compiler/compiler.jar


+ 20 - 0
vendor/jasmine-1.1.0/MIT.LICENSE

@@ -0,0 +1,20 @@
+Copyright (c) 2008-2011 Pivotal Labs
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

+ 190 - 0
vendor/jasmine-1.1.0/jasmine-html.js

@@ -0,0 +1,190 @@
+jasmine.TrivialReporter = function(doc) {
+  this.document = doc || document;
+  this.suiteDivs = {};
+  this.logRunningSpecs = false;
+};
+
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) { el.appendChild(child); }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
+  var showPassed, showSkipped;
+
+  this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
+      this.createDom('div', { className: 'banner' },
+        this.createDom('div', { className: 'logo' },
+            this.createDom('span', { className: 'title' }, "Jasmine"),
+            this.createDom('span', { className: 'version' }, runner.env.versionString())),
+        this.createDom('div', { className: 'options' },
+            "Show ",
+            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
+            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
+            )
+          ),
+
+      this.runnerDiv = this.createDom('div', { className: 'runner running' },
+          this.createDom('a', { className: 'run_spec', href: '?' }, "run all"),
+          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
+          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
+      );
+
+  this.document.body.appendChild(this.outerDiv);
+
+  var suites = runner.suites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    var suiteDiv = this.createDom('div', { className: 'suite' },
+        this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
+        this.createDom('a', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
+    this.suiteDivs[suite.id] = suiteDiv;
+    var parentDiv = this.outerDiv;
+    if (suite.parentSuite) {
+      parentDiv = this.suiteDivs[suite.parentSuite.id];
+    }
+    parentDiv.appendChild(suiteDiv);
+  }
+
+  this.startedAt = new Date();
+
+  var self = this;
+  showPassed.onclick = function(evt) {
+    if (showPassed.checked) {
+      self.outerDiv.className += ' show-passed';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
+    }
+  };
+
+  showSkipped.onclick = function(evt) {
+    if (showSkipped.checked) {
+      self.outerDiv.className += ' show-skipped';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
+    }
+  };
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
+  var results = runner.results();
+  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
+  this.runnerDiv.setAttribute("class", className);
+  //do it twice for IE
+  this.runnerDiv.setAttribute("className", className);
+  var specs = runner.specs();
+  var specCount = 0;
+  for (var i = 0; i < specs.length; i++) {
+    if (this.specFilter(specs[i])) {
+      specCount++;
+    }
+  }
+  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
+  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
+  this.runnerMessageSpan.replaceChild(this.createDom('a', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
+
+  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
+};
+
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
+  var results = suite.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.totalCount === 0) { // todo: change this to check results.skipped
+    status = 'skipped';
+  }
+  this.suiteDivs[suite.id].className += " " + status;
+};
+
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
+  if (this.logRunningSpecs) {
+    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+  }
+};
+
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
+  var results = spec.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+  var specDiv = this.createDom('div', { className: 'spec '  + status },
+      this.createDom('a', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
+      this.createDom('a', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(spec.getFullName()),
+        title: spec.getFullName()
+      }, spec.description));
+
+
+  var resultItems = results.getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    specDiv.appendChild(messagesDiv);
+  }
+
+  this.suiteDivs[spec.suite.id].appendChild(specDiv);
+};
+
+jasmine.TrivialReporter.prototype.log = function() {
+  var console = jasmine.getGlobal().console;
+  if (console && console.log) {
+    if (console.log.apply) {
+      console.log.apply(console, arguments);
+    } else {
+      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+    }
+  }
+};
+
+jasmine.TrivialReporter.prototype.getLocation = function() {
+  return this.document.location;
+};
+
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
+  var paramMap = {};
+  var params = this.getLocation().search.substring(1).split('&');
+  for (var i = 0; i < params.length; i++) {
+    var p = params[i].split('=');
+    paramMap[decodeURIComponent(p[0])] = decodeURIComponent(p[1]);
+  }
+
+  if (!paramMap.spec) {
+    return true;
+  }
+  return spec.getFullName().indexOf(paramMap.spec) === 0;
+};

+ 166 - 0
vendor/jasmine-1.1.0/jasmine.css

@@ -0,0 +1,166 @@
+body {
+  font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
+}
+
+
+.jasmine_reporter a:visited, .jasmine_reporter a {
+  color: #303; 
+}
+
+.jasmine_reporter a:hover, .jasmine_reporter a:active {
+  color: blue; 
+}
+
+.run_spec {
+  float:right;
+  padding-right: 5px;
+  font-size: .8em;
+  text-decoration: none;
+}
+
+.jasmine_reporter {
+  margin: 0 5px;
+}
+
+.banner {
+  color: #303;
+  background-color: #fef;
+  padding: 5px;
+}
+
+.logo {
+  float: left;
+  font-size: 1.1em;
+  padding-left: 5px;
+}
+
+.logo .version {
+  font-size: .6em;
+  padding-left: 1em;
+}
+
+.runner.running {
+  background-color: yellow;
+}
+
+
+.options {
+  text-align: right;
+  font-size: .8em;
+}
+
+
+
+
+.suite {
+  border: 1px outset gray;
+  margin: 5px 0;
+  padding-left: 1em;
+}
+
+.suite .suite {
+  margin: 5px; 
+}
+
+.suite.passed {
+  background-color: #dfd;
+}
+
+.suite.failed {
+  background-color: #fdd;
+}
+
+.spec {
+  margin: 5px;
+  padding-left: 1em;
+  clear: both;
+}
+
+.spec.failed, .spec.passed, .spec.skipped {
+  padding-bottom: 5px;
+  border: 1px solid gray;
+}
+
+.spec.failed {
+  background-color: #fbb;
+  border-color: red;
+}
+
+.spec.passed {
+  background-color: #bfb;
+  border-color: green;
+}
+
+.spec.skipped {
+  background-color: #bbb;
+}
+
+.messages {
+  border-left: 1px dashed gray;
+  padding-left: 1em;
+  padding-right: 1em;
+}
+
+.passed {
+  background-color: #cfc;
+  display: none;
+}
+
+.failed {
+  background-color: #fbb;
+}
+
+.skipped {
+  color: #777;
+  background-color: #eee;
+  display: none;
+}
+
+
+/*.resultMessage {*/
+  /*white-space: pre;*/
+/*}*/
+
+.resultMessage span.result {
+  display: block;
+  line-height: 2em;
+  color: black;
+}
+
+.resultMessage .mismatch {
+  color: black;
+}
+
+.stackTrace {
+  white-space: pre;
+  font-size: .8em;
+  margin-left: 10px;
+  max-height: 5em;
+  overflow: auto;
+  border: 1px inset red;
+  padding: 1em;
+  background: #eef;
+}
+
+.finished-at {
+  padding-left: 1em;
+  font-size: .6em;
+}
+
+.show-passed .passed,
+.show-skipped .skipped {
+  display: block;
+}
+
+
+#jasmine_content {
+  position:fixed;
+  right: 100%;
+}
+
+.runner {
+  border: 1px solid gray;
+  display: block;
+  margin: 5px 0;
+  padding: 2px 0 2px 10px;
+}

文件差異過大導致無法顯示
+ 2476 - 0
vendor/jasmine-1.1.0/jasmine.js


二進制
vendor/jasmine-1.1.0/jasmine_favicon.png


+ 131 - 0
vendor/node-jasmine-dom/README.md

@@ -0,0 +1,131 @@
+node-jasmine-dom
+================
+
+Run your browser-based [jasmine][1] specs headless with [node.js][2]. Then 
+output in one of many formats such as JSON or JUnit XML (perfect
+for integration with CI servers like [Jenkins][3]).
+
+installation
+------------
+1. you've got [npm][4], right?
+2. Groovy. <code>npm install jasmine-dom</code>, and you're away laughing.
+
+usage
+-----
+Construct your SpecRunner.html as shown in the jasmine examples,
+then run:
+
+    jasmine-dom --runner path/to/SpecRunner.html
+
+or
+
+    jasmine-dom --config path/to/config.yaml
+
+You can optionally provide the following arguments:
+
+ * <code>--help</code>, provides usage information
+ * <code>--format simple|nice|json|html|junit</code>, displays the result in the specified format
+ * <code>--output path</code>, writes the output to the specified file
+ * <code>--server [port]</code>, serves a simple (but effective) page showing the current state
+                                 of the tests. You can also specify an optional <code>--refresh
+                                 intervalInMS</code> argument to specify the wait between running
+                                 the tests (because the server is constantly running 'em).
+
+server
+------
+
+    jasmine-dom --runner examples/runner.html --server 8090 --refresh 3000
+
+will run a server on http://localhost:8090/. Here a simple green or red page will reflect the current state
+of your tests. The tests will run every 3000ms, and the page ajaximatically updates with the result.
+
+If you'd like to see the default jasmine html, visit http://localhost:8090/jasmine. Note, the result is still
+obtained via the nodejs runner (i.e. it wasn't run in your browser).
+
+specifying runners
+------------------
+A single runner file can be provided via the <code>--runner <path_to_runner></code> command. To specify more than one
+runner, use the <code>--config <path_to_config></code> argument and a yaml config file in the format:
+
+```yaml
+---
+  test_one:
+    name: This is the name of the first set of tests
+    runner: path/to/runner_1.html
+  test_two:
+    name: This is the name of the second set of tests
+    runner: path/to/another/runner.html
+```
+
+The config file allows you to provide names for your runners. These names will be used when identifying failing tests.
+
+example 1
+---------
+    jasmine-dom --runner examples/runner.html
+
+will output:
+
+    Failed.
+
+example 2
+---------
+
+    jasmine-dom --runner examples/runner.html --format junit --output javascript_results.xml
+
+will write to javascript_results.xml:
+
+    <testsuite>
+        <testcase classname="/Users/andrew/development/node-jasmine-dom/examples/runner.html.Example_functions_that_update_the_DOM.Should_add_two_numbers" name="expect toEqual 7" time="undefined"/>
+        <testcase classname="/Users/andrew/development/node-jasmine-dom/examples/runner.html.Example_functions.Should_multiply_two_numbers" name="expect toEqual 40" time="undefined"/>
+        <testcase classname="/Users/andrew/development/node-jasmine-dom/examples/runner.html.Example_functions.Should_fail!!" name="expect toEqual 8">
+            <failure>
+                <![CDATA[
+                    FAILURE in spec "Should fail!!": Expected 3 to equal 8.
+                    Error: Expected 3 to equal 8.
+                        at new <anonymous> (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:94:50)
+                        at [object Object].toEqual (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:1138:29)
+                        at [object Object].<anonymous> (/Users/andrew/development/node-jasmine-dom/examples/tests/spec/example-functions_spec.js:10:13)
+                        at [object Object].execute (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:968:15)
+                        at [object Object].next_ (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:1739:31)
+                        at [object Object].start (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:1692:8)
+                        at [object Object].execute (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:2018:14)
+                        at [object Object].next_ (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:1739:31)
+                        at [object Object].start (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:1692:8)
+                        at [object Object].execute (/Users/andrew/development/node-jasmine-dom/examples/tests/lib/jasmine.js:2163:14)
+                ]]>
+            </failure>
+        </testcase>
+    </testsuite>
+
+example 3
+---------
+
+    jasmine-dom --config ./examples/config.yaml --format nice
+
+with ./examples/config.yaml:
+
+    ---
+      test_one:
+        name: Example test one
+        runner: ./runner.html
+      test_two:
+        name: Example test two
+        runner: ./runner2.html
+
+will output:
+
+    Failed: 
+     - In Example test two >> Example functions (some more) >> Should fail!! :: Expected false to be truthy.
+     - In Example test one >> Example functions >> Should fail!! :: Expected 3 to equal 8.
+
+have you seen **[jasmine-node][5]**?
+------------------------------------
+It's provided a lot of inspiration for this project, and may be just what
+you're looking for. If you're not reliant on a DOM, then it's worth checking
+out.
+
+[1]: http://pivotal.github.com/jasmine/
+[2]: http://nodejs.org/
+[3]: http://jenkins-ci.org/
+[4]: http://npmjs.org/
+[5]: https://github.com/mhevery/jasmine-node

+ 186 - 0
vendor/node-jasmine-dom/bin/jasmine-dom

@@ -0,0 +1,186 @@
+#!/usr/bin/env node
+
+function getArguments(args){
+	var unprocessed = args,
+		processed = {}, 
+		key = "", 
+		value = "";
+
+	for(var i = 0; i < unprocessed.length; i++){
+		var arg = unprocessed[i];
+		if(arg.substr(0,2) == "--"){
+			if(key){
+				processed[key] = value;
+				key = "";
+				value = "";
+			}
+			key = arg.substr(2);
+		} else {
+			if(key) value += (value?" ":"") + arg;
+		}
+	};
+
+	if(key){
+		processed[key] = value;
+		key = "";
+		value = "";
+	};
+
+	return processed;
+};
+
+var arguments = getArguments(process.argv);
+var options = {
+	format: arguments.format || "default",
+	output: arguments.output || null,
+	debug: "debug" in arguments,
+	runner: arguments.runner,
+	server: "server" in arguments,
+	port: arguments.server,
+	help: "help" in arguments,
+	refreshInterval: arguments.refresh,
+	config: arguments.config
+};
+
+function _getAbsolutePath(file, base){
+	var path;
+	var base = base || process.cwd();
+	if (file.substring(0,1) == "/"){
+		path = file;
+	} else {
+		path = base + '/' + file;
+	}
+	return require('path').normalize(path);
+};
+
+function _getUsage(){
+	return "Usage: node run.js --runner <path> | --config <path> [--format simple|nice|json|html|junit] [--output <path>] [--server [port] [--refresh <ms interval>]] [--help]\n\n" +
+	       "For more information, visit https://github.com/andrewpmckenzie/node-jasmine-dom";
+};
+
+function _formatNice(obj){
+	if(obj.failed == 0){
+		return "Passed";
+	} else {
+		var message = "Failed: \n";
+		for( var k in obj.failureDetails ){
+			var details = obj.failureDetails[k];
+			message += " - In " + details.group + " >> " + details.suite + " >> " + details.spec + " :: " + details.message + "\n";
+		}
+		return message;
+	}
+};
+
+function _format(report){
+	var result = '';
+
+	switch(options.format){
+		case 'simple':
+		case 'default':
+			result = report.simple.status;
+			break;
+		case 'json':
+			result = JSON.stringify(report.simple);
+			break;
+		case 'nice':
+			result = _formatNice(report.simple);
+			break;
+		case 'html':
+			result = report.html;
+			break;
+		case 'junit':
+			result = report.junit;
+			break;
+		default:
+			console.error("Unknown format:" + options.format);
+			process.exit(0);
+			break;
+	};
+
+	return result;
+};
+
+function _output(text){
+	if(options.output){
+		var file = _getAbsolutePath(options.output);
+		require('fs').writeFile(file,text,function(err){
+			if(err) console.log("Something went wrong writing the report to disk: ",err);
+		});
+	} else if(options.server) {
+	    return;
+	} else {
+		console.log(text);
+	}
+};
+
+function _processReport(report){
+	var text = _format(report);
+	_output(text);
+};
+
+function _parseConfig(config){
+	var fs = require('fs'),
+		path = require('path');
+
+	var configPath = _getAbsolutePath(config);
+	var configBase = path.normalize(path.dirname(configPath));
+
+	var file = fs.readFileSync(config,'utf8');
+
+	var runnerConfig = [];
+	try {
+		var yaml = require('yaml').eval(file);
+
+		for(var k in yaml){
+			var runner = yaml[k];
+			if("runner" in runner){
+				runnerConfig.push({
+					name: runner.name,
+					runner: _getAbsolutePath(runner.runner, configBase)
+				});
+			}
+		};
+
+		if(runnerConfig.length == 0) throw "No runners specified in config. See examples/config.yaml for format.";
+
+	} catch (e) {
+		console.log("Invalid config file.\n\n");
+		console.error(e);
+		process.exit(1);
+	}
+
+	return runnerConfig;
+};
+
+function validate(options){
+	if (options.help){
+		console.log(_getUsage());
+		process.exit(0);
+	}
+	if (! options.runner && ! options.config) {
+		console.error("You need to specify a html --runner file.\n");
+		console.log(_getUsage());
+		process.exit(0);
+	}
+};
+
+function getRunners(runner, config){
+	if(config){
+		return _parseConfig(config);
+	} else {
+		return [ _getAbsolutePath(runner) ];
+	}
+};
+
+validate(options);
+
+require('../lib/jasmine-dom').run({
+	runners: getRunners(options.runner, options.config),
+	serve: options.server,
+	port: options.port,
+	refreshInterval: options.refreshInterval,
+	debug: options.debug,
+	onDone: function(report){
+		_processReport(report);
+	}
+});

+ 7 - 0
vendor/node-jasmine-dom/examples/config.yaml

@@ -0,0 +1,7 @@
+---
+  test_one:
+    name: Example test one
+    runner: ./runner.html
+  test_two:
+    name: Example test two
+    runner: ./runner2.html

+ 36 - 0
vendor/node-jasmine-dom/examples/runner.html

@@ -0,0 +1,36 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>Jasmine Test Runner</title>
+	<link rel="stylesheet" type="text/css" href="tests/lib/jasmine.css">
+	<script src="tests/lib/jasmine.js"></script>
+	<script src="tests/lib/jasmine-html.js"></script>
+
+	<!-- include helper files here... -->
+	<script src="tests/helper/jquery.js"></script>
+
+	<!-- include source files here... -->
+	<script src="src/example-dom.js"></script>
+	<script src="src/example-functions.js"></script>
+
+	<!-- include spec files here... -->
+	<script src="tests/spec/example-dom_spec.js"></script>
+	<script src="tests/spec/example-functions_spec.js"></script>
+
+</head>
+<body>
+
+	<div id="add_test_one">
+		<span id="add_test_one_x">5</span>
+		<span id="add_test_one_y">2</span>
+		<span id="add_test_one_result">X</span>
+	</div>
+
+<script type="text/javascript">
+  jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
+  jasmine.getEnv().execute();
+</script>
+
+</body>
+</html>

+ 28 - 0
vendor/node-jasmine-dom/examples/runner2.html

@@ -0,0 +1,28 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+  "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+	<title>Jasmine Test Runner</title>
+	<link rel="stylesheet" type="text/css" href="tests/lib/jasmine.css">
+	<script src="tests/lib/jasmine.js"></script>
+	<script src="tests/lib/jasmine-html.js"></script>
+
+	<!-- include helper files here... -->
+	<script src="tests/helper/jquery.js"></script>
+
+	<!-- include source files here... -->
+	<script src="src/example-functions-2.js"></script>
+
+	<!-- include spec files here... -->
+	<script src="tests/spec/example-functions-2_spec.js"></script>
+
+</head>
+<body>
+
+<script type="text/javascript">
+  jasmine.getEnv().addReporter(new jasmine.TrivialReporter());
+  jasmine.getEnv().execute();
+</script>
+
+</body>
+</html>

+ 7 - 0
vendor/node-jasmine-dom/examples/src/example-dom.js

@@ -0,0 +1,7 @@
+if(! window.EXAMPLES) window.EXAMPLES = {};
+
+EXAMPLES.domAdd = function(xId,yId,resultId){
+	var x = document.getElementById('add_test_one_x').innerHTML;
+	var y = document.getElementById('add_test_one_y').innerHTML;
+	document.getElementById('add_test_one_result').innerHTML = (+x) + (+y);
+};

+ 5 - 0
vendor/node-jasmine-dom/examples/src/example-functions-2.js

@@ -0,0 +1,5 @@
+if(! window.EXAMPLES) window.EXAMPLES = {};
+
+EXAMPLES.divide = function(x,y){
+	return x/y;
+}

+ 5 - 0
vendor/node-jasmine-dom/examples/src/example-functions.js

@@ -0,0 +1,5 @@
+if(! window.EXAMPLES) window.EXAMPLES = {};
+
+EXAMPLES.multiply = function(x,y){
+	return x*y;
+}

文件差異過大導致無法顯示
+ 18 - 0
vendor/node-jasmine-dom/examples/tests/helper/jquery.js


+ 183 - 0
vendor/node-jasmine-dom/examples/tests/lib/jasmine-html.js

@@ -0,0 +1,183 @@
+// NOTE: contains modifications for jasmine-dom
+
+jasmine.TrivialReporter = function(doc) {
+  this.document = doc || document;
+  this.suiteDivs = {};
+  this.logRunningSpecs = false;
+};
+
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) { el.appendChild(child); }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
+  var showPassed, showSkipped;
+
+  this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
+      this.createDom('div', { className: 'banner' },
+        this.createDom('div', { className: 'logo' },
+            this.createDom('a', { href: 'http://pivotal.github.com/jasmine/', target: "_blank" }, "Jasmine"),
+            this.createDom('span', { className: 'version' }, runner.env.versionString())),
+        this.createDom('div', { className: 'options' },
+            "Show ",
+            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
+            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
+            )
+          ),
+
+      this.runnerDiv = this.createDom('div', { className: 'runner running' },
+          this.createDom('span', { className: 'run_spec', href: '?' }, "run all"),
+          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
+          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
+      );
+
+  this.document.body.appendChild(this.outerDiv);
+
+  var suites = runner.suites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    var suiteDiv = this.createDom('div', { className: 'suite' },
+        this.createDom('span', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
+        this.createDom('span', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
+    this.suiteDivs[suite.id] = suiteDiv;
+    var parentDiv = this.outerDiv;
+    if (suite.parentSuite) {
+      parentDiv = this.suiteDivs[suite.parentSuite.id];
+    }
+    parentDiv.appendChild(suiteDiv);
+  }
+
+  this.startedAt = new Date();
+
+  var self = this;
+  showPassed.onclick = function(evt) {
+    if (showPassed.checked) {
+      self.outerDiv.className += ' show-passed';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
+    }
+  };
+
+  showSkipped.onclick = function(evt) {
+    if (showSkipped.checked) {
+      self.outerDiv.className += ' show-skipped';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
+    }
+  };
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
+  var results = runner.results();
+  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
+  this.runnerDiv.setAttribute("class", className);
+  //do it twice for IE
+  this.runnerDiv.setAttribute("className", className);
+  var specs = runner.specs();
+  var specCount = 0;
+  for (var i = 0; i < specs.length; i++) {
+    if (this.specFilter(specs[i])) {
+      specCount++;
+    }
+  }
+  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
+  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
+  this.runnerMessageSpan.replaceChild(this.createDom('span', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
+
+  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
+};
+
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
+  var results = suite.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.totalCount == 0) { // todo: change this to check results.skipped
+    status = 'skipped';
+  }
+  this.suiteDivs[suite.id].className += " " + status;
+};
+
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
+  if (this.logRunningSpecs) {
+    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+  }
+};
+
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
+  var results = spec.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+  var specDiv = this.createDom('div', { className: 'spec '  + status },
+      this.createDom('span', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
+      this.createDom('span', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(spec.getFullName()),
+        title: spec.getFullName()
+      }, spec.description));
+
+
+  var resultItems = results.getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    specDiv.appendChild(messagesDiv);
+  }
+
+  this.suiteDivs[spec.suite.id].appendChild(specDiv);
+};
+
+jasmine.TrivialReporter.prototype.log = function() {
+  var console = jasmine.getGlobal().console;
+  if (console && console.log) {
+    if (console.log.apply) {
+      console.log.apply(console, arguments);
+    } else {
+      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+    }
+  }
+};
+
+jasmine.TrivialReporter.prototype.getLocation = function() {
+  return this.document.location;
+};
+
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
+
+  return true;
+};

+ 167 - 0
vendor/node-jasmine-dom/examples/tests/lib/jasmine.css

@@ -0,0 +1,167 @@
+body {
+  font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif;
+}
+
+
+.jasmine_reporter a:visited, .jasmine_reporter a {
+  color: #303; 
+}
+
+.jasmine_reporter a:hover, .jasmine_reporter a:active {
+  color: blue; 
+}
+
+.run_spec {
+  float:right;
+  padding-right: 5px;
+  font-size: .8em;
+  text-decoration: none;
+}
+
+.jasmine_reporter {
+  margin: 0 5px;
+}
+
+.banner {
+  color: #303;
+  background-color: #fef;
+  padding: 5px;
+}
+
+.logo {
+  float: left;
+  font-size: 1.1em;
+  padding-left: 5px;
+}
+
+.logo .version {
+  font-size: .6em;
+  padding-left: 1em;
+}
+
+.runner.running {
+  background-color: yellow;
+}
+
+
+.options {
+  visibility:hidden;
+  text-align: right;
+  font-size: .8em;
+}
+
+
+
+
+.suite {
+  border: 1px outset gray;
+  margin: 5px 0;
+  padding-left: 1em;
+}
+
+.suite .suite {
+  margin: 5px; 
+}
+
+.suite.passed {
+  background-color: #dfd;
+}
+
+.suite.failed {
+  background-color: #fdd;
+}
+
+.spec {
+  margin: 5px;
+  padding-left: 1em;
+  clear: both;
+}
+
+.spec.failed, .spec.passed, .spec.skipped {
+  padding-bottom: 5px;
+  border: 1px solid gray;
+}
+
+.spec.failed {
+  background-color: #fbb;
+  border-color: red;
+}
+
+.spec.passed {
+  background-color: #bfb;
+  border-color: green;
+}
+
+.spec.skipped {
+  background-color: #bbb;
+}
+
+.messages {
+  border-left: 1px dashed gray;
+  padding-left: 1em;
+  padding-right: 1em;
+}
+
+.passed {
+  background-color: #cfc;
+  display: none;
+}
+
+.failed {
+  background-color: #fbb;
+}
+
+.skipped {
+  color: #777;
+  background-color: #eee;
+  display: none;
+}
+
+
+/*.resultMessage {*/
+  /*white-space: pre;*/
+/*}*/
+
+.resultMessage span.result {
+  display: block;
+  line-height: 2em;
+  color: black;
+}
+
+.resultMessage .mismatch {
+  color: black;
+}
+
+.stackTrace {
+  white-space: pre;
+  font-size: .8em;
+  margin-left: 10px;
+  max-height: 5em;
+  overflow: auto;
+  border: 1px inset red;
+  padding: 1em;
+  background: #eef;
+}
+
+.finished-at {
+  padding-left: 1em;
+  font-size: .6em;
+}
+
+.show-passed .passed,
+.show-skipped .skipped {
+  display: block;
+}
+
+
+#jasmine_content {
+  position:fixed;
+  right: 100%;
+}
+
+.runner {
+  border: 1px solid gray;
+  display: block;
+  margin: 5px 0;
+  padding: 2px 0 2px 10px;
+}

文件差異過大導致無法顯示
+ 2421 - 0
vendor/node-jasmine-dom/examples/tests/lib/jasmine.js


+ 9 - 0
vendor/node-jasmine-dom/examples/tests/spec/example-dom_spec.js

@@ -0,0 +1,9 @@
+describe("Example functions that update the DOM", function(){
+	it("Should add two numbers", function(){
+
+		EXAMPLES.domAdd('add_test_one_x', 'add_test_one_y', 'add_test_one_result');
+		var result = jQuery('#add_test_one_result').html();
+
+		expect(result).toEqual('7');
+	});
+});

+ 12 - 0
vendor/node-jasmine-dom/examples/tests/spec/example-functions-2_spec.js

@@ -0,0 +1,12 @@
+describe("Example functions (some more)", function(){
+	it("Should divide two numbers", function(){
+		var result = EXAMPLES.divide(8,2);
+
+		expect(result).toEqual(4);
+
+	});
+
+	it("Should fail!!", function(){
+		expect(false).toBeTruthy();
+	});
+});

+ 12 - 0
vendor/node-jasmine-dom/examples/tests/spec/example-functions_spec.js

@@ -0,0 +1,12 @@
+describe("Example functions", function(){
+	it("Should multiply two numbers", function(){
+		var result = EXAMPLES.multiply(5,8);
+
+		expect(result).toEqual(40);
+
+	});
+
+	it("Should fail!!", function(){
+		expect(3).toEqual(8);
+	});
+});

+ 174 - 0
vendor/node-jasmine-dom/lib/jasmine-dom/index.js

@@ -0,0 +1,174 @@
+var fs = require('fs'), 
+	path = require('path');
+
+function JasmineRunner(options, callback){
+
+	var doServer = options.serve,
+		port = options.port || 8081,
+		refreshInterval = options.refreshInterval || 2000,
+		runners = options.runners || null,
+		that = this;
+
+	this.isRunningTests = false;
+	this.isTestRunQueued = false;
+
+	this.reporter = this._setupReporter(function(){
+
+		callback();
+
+	}); // async
+
+	if(doServer){
+		this.server = that._setupServer({
+						port: port, 
+						reporter: this.reporter,
+						refreshInterval: refreshInterval
+					});
+
+	};
+
+	if(runners){
+		this.tests = this._setupRunners({
+						runners: runners, 
+						jasmineReporter: this.reporter.getJasmineReporter(),
+					});
+	} else {
+		console.error("I don't have any tests to run!");
+		process.exit(1);
+	}
+};
+
+JasmineRunner.prototype._setupReporter = function(callback){
+	console.debug("Setting up reporter");
+
+	var reporter = require('./reporter-agregator.js').create({
+			onDone: function(){ 
+					console.debug("Reporter created.");
+					callback();
+			}
+	});
+
+	return reporter;
+
+};
+
+JasmineRunner.prototype._setupServer = function(options){
+	console.debug("Setting up server");
+	var that = this,
+		server = require('./server.js');
+
+	server.start(options.port, function(){
+		callback(server);
+	});
+
+	options.reporter.onUpdate(function(report){
+		server.updateHtml(report.html);
+		server.updateJson(report.simple);
+
+		console.debug("Tests will run again in " + (options.refreshInterval/1000) + " seconds.");
+		setTimeout(function(){
+			that.runTests();
+		}, options.refreshInterval);
+	});
+
+	return server;
+};
+
+JasmineRunner.prototype._setupRunners = function(options,callback){
+	console.debug("Setting up tests.");
+
+	var tests = [];
+	var runners = options.runners || [],
+		jasmineReporter = options.jasmineReporter;
+
+	for(var k in runners){
+		var runner = runners[k];
+		console.debug('Creating test based on ' + runner);
+
+		var test = require('./runner-from-html.js').create({
+			runner: runner,
+			jasmineReporter: jasmineReporter
+		});
+		tests.push(test);
+	}
+
+	console.debug("Finished creating tests ( based on " +tests.length + " runner/s).");
+
+	return tests;
+};
+
+JasmineRunner.prototype.runTests = function(callback){
+
+	var that = this;
+
+	// We have to maintain synchronisity, otherwise things
+	// get hectic.
+	if(this.isRunningTests) {
+		// DANGER: if more than one call to runTests is made when
+		// tests are running, only the last callback will ever be
+		// triggered.
+		this.isTestRunQueued = true;
+		this.queuedCallback = callback;
+		console.debug("Cannot run tests concurrently. Queued another run.");
+		return;
+	};
+	var whenAllTestsHaveRun = function(){
+		console.debug("Finished running tests.");
+
+		if(callback) callback(that.reporter.getReport());
+		that.reporter.reset();
+
+		that.isRunningTests = false;
+		if(that.isTestRunQueued){
+			that.isTestRunQueued = false;
+			that.runTests(that.queuedCallback);
+			that.queuedCallback = false;
+		}
+	};
+
+	console.debug("Running tests.");
+	this.isRunningTests = true;
+
+	var queue = [],
+		tests = this.tests;
+	for(var i = 0; i < tests.length; i++) queue.push(tests[i]);
+
+	// !! Is recursive
+	var runNextTest = function(){
+		if(queue.length == 0){
+			whenAllTestsHaveRun();
+			return;
+		} 
+
+		var test = queue.pop();
+		console.debug("Running " + test.name);
+		test.run(function(){
+			console.debug("Finished running " + test.name);
+			runNextTest();
+		});
+	};
+
+	runNextTest();
+};
+
+exports.run = function(options, callback){
+	var onDone = options.onDone || function(){},
+		debug = options.debug;
+
+	console.debug = debug ? function(msg){ console.log(msg); } : function(msg){};
+
+	process.on('uncaughtException', function(err){
+		if(debug){
+			console.debug("An uncaught error occured!");
+			console.error(err.message + "\n",err.stack);
+		} else {
+			console.error("An error occurred while running the tests. Use the --debug switch to find out more.")
+			exit(1);
+		}
+	});
+
+	var runner = new JasmineRunner(options, function(){
+		runner.runTests(onDone);
+		callback( runner );
+	});
+};

+ 110 - 0
vendor/node-jasmine-dom/lib/jasmine-dom/reporter-agregator.js

@@ -0,0 +1,110 @@
+var path = require('path');
+var cp = function(o){ if(typeof o != 'object') return o; var n = {}; for(var k in o) n[k] = o[k]; return n; };
+
+function Reporter(options){
+
+	var onDone = options.onDone || function(){};
+
+	/**
+	 * A reporter should implement the jasmine reporter interface
+	 * as well as the following properties:
+	 * - {string} format         e.g. 'html'
+	 * - {function} getReport()
+	 * - {function} updateReport()
+	 * - {function} reset()
+	 */
+	this._updateListeners = [];
+
+	this.report = {};
+	this._jasmineReporters = {};
+	this._jasmineReporters['simpleReporter'] = require('./reporter-simple').create();
+	this._jasmineReporters['junitReporter'] = require('./reporter-junit').create();
+	this._jasmineReporters['htmlReporter'] = require('./reporter-html').create({
+		jasminePath: path.normalize(__dirname+'/resources/jasmine.js'),
+		jasmineHtmlPath: path.normalize(__dirname+'/resources/jasmine-html.js'),
+		skeletonPath: path.normalize(__dirname+'/resources/skeleton.html')
+	}, onDone);
+}
+
+Reporter.prototype._updateReport = function(){
+	// tell reporters to update themselves
+	for(var k in this._jasmineReporters){
+		var reporter = this._jasmineReporters[k];
+		reporter.updateReport();
+		this.report[reporter.format] = reporter.getReport();
+	}
+
+	for(var i = 0; i < this._updateListeners.length; i++){
+		this._updateListeners[i]( cp(this.report) );
+	}
+};
+
+Reporter.prototype.getJasmineReporter = function(){
+	var that = this;
+	return {
+		log : function(str){
+			for(var k in that._jasmineReporters){
+				var reporter = that._jasmineReporters[k];
+				if(reporter.log) reporter.log(str);
+			}
+		},
+		reportSpecStarting : function(runner) {
+			for(var k in that._jasmineReporters){
+				var reporter = that._jasmineReporters[k];
+				if(reporter.reportSpecStarting) reporter.reportSpecStarting(runner);
+			}
+		},
+		reportRunnerStarting : function(runner) { 
+			for(var k in that._jasmineReporters){
+				var reporter = that._jasmineReporters[k];
+				if(reporter.reportRunnerStarting) reporter.reportRunnerStarting(runner);
+			}
+		},
+		reportSuiteResults : function(suite) { 
+			for(var k in that._jasmineReporters){
+				var reporter = that._jasmineReporters[k];
+				if(reporter.reportSuiteResults) reporter.reportSuiteResults(suite);
+			}
+		},
+		reportSpecResults : function(spec) { 
+			for(var k in that._jasmineReporters){
+				var reporter = that._jasmineReporters[k];
+				if(reporter.reportSpecResults) reporter.reportSpecResults(spec);
+			}
+		},
+		reportRunnerResults : function(runner) {
+			for(var k in that._jasmineReporters){
+				var reporter = that._jasmineReporters[k];
+				if(reporter.reportRunnerResults) reporter.reportRunnerResults(runner);
+			}
+			that._updateReport();
+		},
+		reportStartingGroup : function(name){
+			for(var k in that._jasmineReporters){
+				var reporter = that._jasmineReporters[k];
+				if(reporter.reportStartingGroup) reporter.reportStartingGroup(name);
+			}
+		}
+	};
+};
+
+Reporter.prototype.onUpdate = function(callback){
+	this._updateListeners.push(callback);
+};
+
+Reporter.prototype.getReport = function(){
+	return this.report;
+};
+
+Reporter.prototype.reset = function(){
+	
+	for(var k in this._jasmineReporters){
+		var reporter = this._jasmineReporters[k];
+		reporter.reset();
+	}
+}
+
+exports.create = function(options){ 
+	var reporter = new Reporter(options);
+	return reporter;
+};

+ 45 - 0
vendor/node-jasmine-dom/lib/jasmine-dom/reporter-html.js

@@ -0,0 +1,45 @@
+function HtmlReporter(options, callback){
+	console.debug('Creating HtmlReporter');
+
+	this.format = 'html';
+	this._report = '';
+	this._window = {};
+
+	var jasmine = options.jasminePath,
+		jasmineHtml = options.jasmineHtmlPath,
+		skeleton = options.skeletonPath,
+		that = this;
+
+	console.debug('Constructing HtmlReporter DOM');
+	require('jsdom').env({
+		html: skeleton,
+		scripts: [ jasmine, jasmineHtml ],
+		done: function(errors,window){
+			if(errors) console.error('Error construction DOM for html reporter: ',errors);
+
+			console.debug('Creating TrivialReporter instance.');
+			var trivialReporter = new window.jasmine.TrivialReporter();
+
+			console.debug('Transferring TrivialReporter methods to HtmlReporter object');
+			for(var k in trivialReporter) that[k] = trivialReporter[k];
+
+			that._window = window;
+
+			console.debug('Done creating HtmlReporter');
+			callback(that);
+		}
+	});
+};
+HtmlReporter.prototype.updateReport = function(){
+	this._report = this._window.document.outerHTML;
+};
+HtmlReporter.prototype.getReport = function(){
+	return this._report;
+};
+HtmlReporter.prototype.reset = function(){
+	this._window.document.body.innerHTML = ''; // clear for next report
+};
+
+exports.create = function(options,callback){
+	return new HtmlReporter(options,callback);
+};

+ 72 - 0
vendor/node-jasmine-dom/lib/jasmine-dom/reporter-junit.js

@@ -0,0 +1,72 @@
+function JunitXmlReporter(opt){
+	console.debug('Creating JunitXmlReporter');
+
+	var that = this;
+
+	this.format = 'junit';
+	this.report = '';
+
+	this.xml = '';
+	this.currentSetData = '';
+	this.currentGroupName = '';
+
+};
+
+JunitXmlReporter.prototype.getReport = function(){
+	return this.report;
+};
+
+JunitXmlReporter.prototype.reportSpecResults = function(spec){
+	var s = function(s){
+		return s.replace(/\s/gi, '_');
+	};
+	var e = function(s){
+	    return s.replace(/\</gi, '&lt;').replace(/\>/gi, '&gt;').replace(/"/gi, "'");
+	}
+	
+	var specName = s(this.currentGroupName) + "." + s(spec.suite.description) + "." + s(spec.description);
+	
+	var results = spec.results().getItems();
+	for(var k in results){
+		var result = results[k];
+		var name = result.type + " " + result.matcherName + " " + (result.expected ? result.expected : "");
+		name = e(name);
+		specName = e(specName);
+		if(result.passed()){
+			this.xml += '<testcase classname="'+specName+'" name="'+name+'" time="'+result.time+'"/>';
+		} else {
+			this.xml += '<testcase classname="'+specName+'" name="'+name+'">';
+			this.xml += 	'<failure><![CDATA[';
+			this.xml += 		'FAILURE in spec "' + spec.description + '": ';
+			this.xml += 		result.message + "\n\n\n\n" + result.trace.stack;
+			this.xml += 	']]></failure>';
+			this.xml += '</testcase>';
+		}
+	}
+};
+
+JunitXmlReporter.prototype.reportSuiteResults = function(suite){
+	
+};
+
+JunitXmlReporter.prototype.reportRunnerResults = function(runner){
+	
+};
+
+JunitXmlReporter.prototype.reportStartingGroup = function(name){
+	this.currentGroupName = name;
+};
+
+JunitXmlReporter.prototype.updateReport = function(){
+	this.report = "<testsuite>"+this.xml+"</testsuite>";
+};
+
+JunitXmlReporter.prototype.reset = function(){
+	this.results = [];
+};
+
+
+
+exports.create = function(opt){
+	return new JunitXmlReporter(opt);
+}

+ 91 - 0
vendor/node-jasmine-dom/lib/jasmine-dom/reporter-simple.js

@@ -0,0 +1,91 @@
+var cp = function(o){ if(typeof o != 'object') return o; var n = {}; for(var k in o) n[k] = o[k]; return n; };
+
+function SimpleReporter(opt){
+	console.debug('Creating SimpleReporter');
+
+	var that = this;
+
+	this.format = 'simple';
+	this._report = {};
+
+	this.results = [];
+	this.currentSet = 0;
+
+};
+
+SimpleReporter.prototype.getReport = function(){
+	return cp(this._report);
+};
+
+SimpleReporter.prototype.reportStartingGroup = function(name){
+	this.results[this.currentSet] = this.results[this.currentSet] || {};
+	this.results[this.currentSet].name = name;
+}
+
+SimpleReporter.prototype.reportSpecResults = function(spec){
+
+	var items = spec.results().getItems();
+	for (var i = 0; i < items.length; i++){
+		if( ! items[i].passed_){
+			this.results[this.currentSet] = this.results[this.currentSet] || {};
+			this.results[this.currentSet].failures = this.results[this.currentSet].failures || [];
+
+			var failureReport = items[i];
+			failureReport.suite = spec.suite.description;
+			failureReport.spec = spec.description;
+			failureReport.group = this.results[this.currentSet].name;
+			this.results[this.currentSet].failures.push(failureReport);
+		}
+	}
+}
+
+SimpleReporter.prototype.reportRunnerResults = function(runner){
+	var results = runner.results();
+
+	this.results[this.currentSet] = this.results[this.currentSet] || {};
+	this.results[this.currentSet].passed = results.passedCount;
+	this.results[this.currentSet].failed = results.failedCount;
+	this.results[this.currentSet].total = results.totalCount;
+	this.currentSet++;
+}
+
+SimpleReporter.prototype.updateReport = function(){
+	var totalPassed = 0,
+		totalFailed = 0,
+		totalTests = 0,
+		totalSuites = 0,
+		failureDetails = [];
+
+	for(var k in this.results){
+		var result = this.results[k];
+		totalPassed += result.passed;
+		totalFailed += result.failed;
+		totalTests += result.total;
+		totalSuites++;
+		if(result.failures){
+			for(var j = 0; j < result.failures.length; j++){
+				failureDetails.push(result.failures[j]);
+			}
+		}
+	}
+
+	this._report = {
+		details: cp(this.results),
+		passed: totalPassed,
+		failed: totalFailed,
+		total: totalTests,
+		suites: totalSuites,
+		failureDetails: cp(failureDetails),
+		status: (totalFailed == 0) ? "Passed" : "Failed"
+	};
+};
+
+SimpleReporter.prototype.reset = function(){
+	this.results = [];
+};
+
+
+
+exports.create = function(opt){
+	return new SimpleReporter(opt);
+}

+ 183 - 0
vendor/node-jasmine-dom/lib/jasmine-dom/resources/jasmine-html.js

@@ -0,0 +1,183 @@
+// NOTE: contains modifications for jasmine-dom
+
+jasmine.TrivialReporter = function(doc) {
+  this.document = doc || document;
+  this.suiteDivs = {};
+  this.logRunningSpecs = false;
+};
+
+jasmine.TrivialReporter.prototype.createDom = function(type, attrs, childrenVarArgs) {
+  var el = document.createElement(type);
+
+  for (var i = 2; i < arguments.length; i++) {
+    var child = arguments[i];
+
+    if (typeof child === 'string') {
+      el.appendChild(document.createTextNode(child));
+    } else {
+      if (child) { el.appendChild(child); }
+    }
+  }
+
+  for (var attr in attrs) {
+    if (attr == "className") {
+      el[attr] = attrs[attr];
+    } else {
+      el.setAttribute(attr, attrs[attr]);
+    }
+  }
+
+  return el;
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerStarting = function(runner) {
+  var showPassed, showSkipped;
+
+  this.outerDiv = this.createDom('div', { className: 'jasmine_reporter' },
+      this.createDom('div', { className: 'banner' },
+        this.createDom('div', { className: 'logo' },
+            this.createDom('a', { href: 'http://pivotal.github.com/jasmine/', target: "_blank" }, "Jasmine"),
+            this.createDom('span', { className: 'version' }, runner.env.versionString())),
+        this.createDom('div', { className: 'options' },
+            "Show ",
+            showPassed = this.createDom('input', { id: "__jasmine_TrivialReporter_showPassed__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showPassed__" }, " passed "),
+            showSkipped = this.createDom('input', { id: "__jasmine_TrivialReporter_showSkipped__", type: 'checkbox' }),
+            this.createDom('label', { "for": "__jasmine_TrivialReporter_showSkipped__" }, " skipped")
+            )
+          ),
+
+      this.runnerDiv = this.createDom('div', { className: 'runner running' },
+          this.createDom('span', { className: 'run_spec', href: '?' }, "run all"),
+          this.runnerMessageSpan = this.createDom('span', {}, "Running..."),
+          this.finishedAtSpan = this.createDom('span', { className: 'finished-at' }, ""))
+      );
+
+  this.document.body.appendChild(this.outerDiv);
+
+  var suites = runner.suites();
+  for (var i = 0; i < suites.length; i++) {
+    var suite = suites[i];
+    var suiteDiv = this.createDom('div', { className: 'suite' },
+        this.createDom('span', { className: 'run_spec', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, "run"),
+        this.createDom('span', { className: 'description', href: '?spec=' + encodeURIComponent(suite.getFullName()) }, suite.description));
+    this.suiteDivs[suite.id] = suiteDiv;
+    var parentDiv = this.outerDiv;
+    if (suite.parentSuite) {
+      parentDiv = this.suiteDivs[suite.parentSuite.id];
+    }
+    parentDiv.appendChild(suiteDiv);
+  }
+
+  this.startedAt = new Date();
+
+  var self = this;
+  showPassed.onclick = function(evt) {
+    if (showPassed.checked) {
+      self.outerDiv.className += ' show-passed';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-passed/, '');
+    }
+  };
+
+  showSkipped.onclick = function(evt) {
+    if (showSkipped.checked) {
+      self.outerDiv.className += ' show-skipped';
+    } else {
+      self.outerDiv.className = self.outerDiv.className.replace(/ show-skipped/, '');
+    }
+  };
+};
+
+jasmine.TrivialReporter.prototype.reportRunnerResults = function(runner) {
+  var results = runner.results();
+  var className = (results.failedCount > 0) ? "runner failed" : "runner passed";
+  this.runnerDiv.setAttribute("class", className);
+  //do it twice for IE
+  this.runnerDiv.setAttribute("className", className);
+  var specs = runner.specs();
+  var specCount = 0;
+  for (var i = 0; i < specs.length; i++) {
+    if (this.specFilter(specs[i])) {
+      specCount++;
+    }
+  }
+  var message = "" + specCount + " spec" + (specCount == 1 ? "" : "s" ) + ", " + results.failedCount + " failure" + ((results.failedCount == 1) ? "" : "s");
+  message += " in " + ((new Date().getTime() - this.startedAt.getTime()) / 1000) + "s";
+  this.runnerMessageSpan.replaceChild(this.createDom('span', { className: 'description', href: '?'}, message), this.runnerMessageSpan.firstChild);
+
+  this.finishedAtSpan.appendChild(document.createTextNode("Finished at " + new Date().toString()));
+};
+
+jasmine.TrivialReporter.prototype.reportSuiteResults = function(suite) {
+  var results = suite.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.totalCount == 0) { // todo: change this to check results.skipped
+    status = 'skipped';
+  }
+  this.suiteDivs[suite.id].className += " " + status;
+};
+
+jasmine.TrivialReporter.prototype.reportSpecStarting = function(spec) {
+  if (this.logRunningSpecs) {
+    this.log('>> Jasmine Running ' + spec.suite.description + ' ' + spec.description + '...');
+  }
+};
+
+jasmine.TrivialReporter.prototype.reportSpecResults = function(spec) {
+  var results = spec.results();
+  var status = results.passed() ? 'passed' : 'failed';
+  if (results.skipped) {
+    status = 'skipped';
+  }
+  var specDiv = this.createDom('div', { className: 'spec '  + status },
+      this.createDom('span', { className: 'run_spec', href: '?spec=' + encodeURIComponent(spec.getFullName()) }, "run"),
+      this.createDom('span', {
+        className: 'description',
+        href: '?spec=' + encodeURIComponent(spec.getFullName()),
+        title: spec.getFullName()
+      }, spec.description));
+
+
+  var resultItems = results.getItems();
+  var messagesDiv = this.createDom('div', { className: 'messages' });
+  for (var i = 0; i < resultItems.length; i++) {
+    var result = resultItems[i];
+
+    if (result.type == 'log') {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage log'}, result.toString()));
+    } else if (result.type == 'expect' && result.passed && !result.passed()) {
+      messagesDiv.appendChild(this.createDom('div', {className: 'resultMessage fail'}, result.message));
+
+      if (result.trace.stack) {
+        messagesDiv.appendChild(this.createDom('div', {className: 'stackTrace'}, result.trace.stack));
+      }
+    }
+  }
+
+  if (messagesDiv.childNodes.length > 0) {
+    specDiv.appendChild(messagesDiv);
+  }
+
+  this.suiteDivs[spec.suite.id].appendChild(specDiv);
+};
+
+jasmine.TrivialReporter.prototype.log = function() {
+  var console = jasmine.getGlobal().console;
+  if (console && console.log) {
+    if (console.log.apply) {
+      console.log.apply(console, arguments);
+    } else {
+      console.log(arguments); // ie fix: console.log.apply doesn't exist on ie
+    }
+  }
+};
+
+jasmine.TrivialReporter.prototype.getLocation = function() {
+  return this.document.location;
+};
+
+jasmine.TrivialReporter.prototype.specFilter = function(spec) {
+
+  return true;
+};

+ 0 - 0
vendor/node-jasmine-dom/lib/jasmine-dom/resources/jasmine.css


部分文件因文件數量過多而無法顯示