├── .bowerrc ├── .gitignore ├── .jshintrc ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── bower.json ├── bower_modules ├── backbone │ └── backbone.js ├── jquery │ └── jquery.js ├── requirejs-text │ └── text.js ├── requirejs │ └── require.js ├── sinonjs │ └── sinon.js └── underscore │ └── underscore.js ├── build ├── config.gypi └── tasks │ └── build.js ├── docs ├── app.js ├── basics.html ├── building_your_xf_app_for_testing_and_production.html ├── buttons.html ├── collection.html ├── component.html ├── component_creation.html ├── creation_of_custom_xframework_build.html ├── data │ ├── basics.html │ ├── building_your_xf_app_for_testing_and_production.html │ ├── buttons.html │ ├── collection.html │ ├── component.html │ ├── component_creation.html │ ├── creation_of_custom_xframework_build.html │ ├── footer.html │ ├── form_elements.html │ ├── header.html │ ├── installing_xframework_generator.html │ ├── list_of_built-in_events.html │ ├── listview.html │ ├── model.html │ ├── supported_platforms_and_browsers.html │ ├── tabs.html │ ├── the_idea_behind.html │ ├── updating_xf_and_dependencies.html │ ├── view.html │ ├── what_is_xframework_.html │ ├── xf_app.html │ ├── xf_device.html │ ├── xf_pages.html │ ├── xf_router.html │ ├── xf_settings.html │ ├── xf_source_modules.html │ ├── xf_storage.html │ ├── xf_touch.html │ ├── xf_utils.html │ ├── xf_zepto_support_js.html │ └── your_first_xf_web_app.html ├── docs.css ├── footer.html ├── form_elements.html ├── header.html ├── installing_xframework_generator.html ├── js │ └── components │ │ ├── home.js │ │ └── menu.js ├── list_of_built-in_events.html ├── listview.html ├── mocks │ └── menu.json ├── model.html ├── supported_platforms_and_browsers.html ├── tabs.html ├── the_idea_behind.html ├── tmpl │ ├── mobile │ │ ├── header.tmpl │ │ ├── home.tmpl │ │ └── menu.tmpl │ └── tablet │ │ ├── header.tmpl │ │ ├── home.tmpl │ │ └── menu.tmpl ├── updating_xf_and_dependencies.html ├── view.html ├── what_is_xframework_.html ├── xf_app.html ├── xf_device.html ├── xf_pages.html ├── xf_router.html ├── xf_settings.html ├── xf_source_modules.html ├── xf_storage.html ├── xf_touch.html ├── xf_utils.html ├── xf_zepto_support_js.html └── your_first_xf_web_app.html ├── files ├── js ├── xf.js └── xf.min.js ├── mocks └── menu.json ├── package.json ├── styles ├── fonts │ ├── entypo-social.eot │ ├── entypo-social.svg │ ├── entypo-social.ttf │ ├── entypo-social.woff │ ├── entypo.eot │ ├── entypo.svg │ ├── entypo.ttf │ └── entypo.woff ├── lesshat.less ├── xf.animations.less ├── xf.buttons.less ├── xf.css ├── xf.dialog.less ├── xf.footer.less ├── xf.forms.less ├── xf.header.less ├── xf.icons.less ├── xf.layout.less ├── xf.less ├── xf.list.less ├── xf.loader.less ├── xf.min.css ├── xf.mixins.less ├── xf.pages.less ├── xf.reset.less ├── xf.tabs.less ├── xf.theme.dark.less ├── xf.theme.less ├── xf.type.less ├── xf.utilities.less ├── xf.variables.less └── xf.variables.theme.dark.less ├── test ├── components │ └── test.js ├── index.html ├── lib │ ├── qunit-1.12.0.css │ ├── qunit-1.12.0.js │ └── run-qunit.js ├── main.js ├── sample-feed.xml ├── src │ ├── app │ │ └── start.js │ ├── dom │ │ ├── data.html │ │ └── dom.js │ ├── xf.app.js │ ├── xf.collection.js │ ├── xf.component.js │ ├── xf.core.js │ ├── xf.device.js │ ├── xf.model.js │ ├── xf.pages.js │ ├── xf.router.js │ ├── xf.settings.js │ ├── xf.storage.js │ ├── xf.touch.js │ └── xf.ui.js ├── test-files.js ├── test.html ├── test.json ├── test.tmpl └── ui │ ├── xf.ui.button.js │ ├── xf.ui.dialog.js │ ├── xf.ui.fieldset.js │ └── xf.ui.list.js └── xf ├── src ├── app │ └── start.js ├── dom │ └── dom.js ├── xf.app.js ├── xf.collection.js ├── xf.component.js ├── xf.core.js ├── xf.device.js ├── xf.framework.js ├── xf.log.js ├── xf.model.js ├── xf.pages.js ├── xf.router.js ├── xf.settings.js ├── xf.storage.js ├── xf.touch.js ├── xf.ui.js ├── xf.utils.js ├── xf.view.js └── xf.zepto.support.js └── ui ├── xf.ui.button.js ├── xf.ui.checkboxradio.js ├── xf.ui.core.js ├── xf.ui.fieldset.js ├── xf.ui.footer.js ├── xf.ui.header.js ├── xf.ui.list.js ├── xf.ui.loader.js ├── xf.ui.popup.js ├── xf.ui.scrollable.js ├── xf.ui.slidemenu.js ├── xf.ui.tabs.js └── xf.ui.textinput.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory" : "js/lib/" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .idea/.name 3 | 4 | .idea/codeStyleSettings.xml 5 | 6 | *.xml 7 | 8 | *.iml 9 | 10 | data.json 11 | 12 | docs.js 13 | 14 | index.html 15 | 16 | js/app.js 17 | 18 | js/appx.js 19 | 20 | js/collections/customCollection.js 21 | 22 | js/components/component1.js 23 | 24 | js/components/root.js 25 | 26 | js/index.js 27 | 28 | js/models/customModel.js 29 | 30 | styles/sec.css 31 | 32 | tmpl/desktop/comments.tmpl 33 | 34 | tmpl/desktop/favourites.tmpl 35 | 36 | tmpl/desktop/feedback.tmpl 37 | 38 | tmpl/desktop/home.tmpl 39 | 40 | tmpl/desktop/root.tmpl 41 | 42 | tmpl/desktop/presentation.tmpl 43 | 44 | tmpl/desktop/speaker.tmpl 45 | 46 | tmpl/desktop/speakers.tmpl 47 | 48 | tmpl/mobile/comments.tmpl 49 | 50 | tmpl/mobile/favourites.tmpl 51 | 52 | tmpl/mobile/feedback.tmpl 53 | 54 | tmpl/mobile/presentation.tmpl 55 | 56 | tmpl/mobile/home.tmpl 57 | 58 | tmpl/mobile/speaker.tmpl 59 | 60 | tmpl/mobile/speakers.tmpl 61 | 62 | tmpl/phone/root.tmpl 63 | 64 | tmpl/tablet/root.tmpl 65 | .DS_Store 66 | npm-debug.log 67 | 68 | js/lib 69 | 70 | index2.html 71 | 72 | xf/src/xf.storage-w-collections.js 73 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "jasmine": false, 4 | "spyOn": false, 5 | "it": false, 6 | "console": false, 7 | "describe": false, 8 | "expect": false, 9 | "beforeEach": false, 10 | "waits": false, 11 | "waitsFor": false, 12 | "runs": false 13 | }, 14 | 15 | "node" : true, 16 | "es5" : false, 17 | "expr" : false, 18 | "browser" : true, 19 | "boss" : false, 20 | "curly": false, 21 | "debug": true, 22 | "devel": true, 23 | "eqeqeq": false, 24 | "evil": true, 25 | "forin": false, 26 | "immed": true, 27 | "laxbreak": false, 28 | "newcap": false, 29 | "noarg": true, 30 | "noempty": false, 31 | "nonew": false, 32 | "nomen": false, 33 | "onevar": false, 34 | "plusplus": false, 35 | "regexp": false, 36 | "undef": false, 37 | "sub": true, 38 | "strict": false, 39 | "white": false, 40 | "unused": false 41 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | 5 | before_script: 6 | - "export DISPLAY=:99.0" 7 | - "sh -e /etc/init.d/xvfb start" 8 | - npm install nws -g 9 | - nws -p 9292 & 10 | - sleep 5 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013–2014, EPAM Systems, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | * Neither the name of the EPAM Systems, Inc. nor the names of its contributors 13 | may be used to endorse or promote products derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 16 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 18 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 19 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 21 | IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "xframework", 3 | "version": "0.9.1", 4 | "devDependencies": { 5 | "jquery": "latest", 6 | "backbone": "latest", 7 | "underscore": "latest", 8 | "requirejs": "latest", 9 | "requirejs-text": "latest", 10 | "sinonjs": "latest", 11 | "xframework": "https://github.com/epam/xframework.git" 12 | }, 13 | "homepage": "http://xframeworkjs.org", 14 | "authors": [ 15 | "EPAM" 16 | ], 17 | "main": "js/xf.js", 18 | "license": "BSD", 19 | "ignore": [ 20 | "**/.*", 21 | "node_modules", 22 | "bower_modules", 23 | ".", 24 | "test" 25 | ], 26 | "exportsOverride": { 27 | "backbone": { 28 | ".": "backbone.js" 29 | }, 30 | "jquery": { 31 | ".": "jquery.js" 32 | }, 33 | "underscore": { 34 | ".": "underscore.js" 35 | }, 36 | "requirejs": { 37 | ".": "require.js" 38 | }, 39 | "requirejs-text": { 40 | ".": "text.js" 41 | }, 42 | "sinonjs": { 43 | ".": "sinon.js" 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /build/config.gypi: -------------------------------------------------------------------------------- 1 | # Do not edit. File was generated by node-gyp's "configure" step 2 | { 3 | "target_defaults": { 4 | "cflags": [], 5 | "default_configuration": "Release", 6 | "defines": [], 7 | "include_dirs": [], 8 | "libraries": [] 9 | }, 10 | "variables": { 11 | "clang": 1, 12 | "host_arch": "x64", 13 | "node_install_npm": "true", 14 | "node_prefix": "/", 15 | "node_shared_cares": "false", 16 | "node_shared_http_parser": "false", 17 | "node_shared_libuv": "false", 18 | "node_shared_openssl": "false", 19 | "node_shared_v8": "false", 20 | "node_shared_zlib": "false", 21 | "node_tag": "", 22 | "node_unsafe_optimizations": 0, 23 | "node_use_dtrace": "true", 24 | "node_use_etw": "false", 25 | "node_use_openssl": "true", 26 | "node_use_perfctr": "false", 27 | "python": "/usr/bin/python", 28 | "target_arch": "x64", 29 | "v8_enable_gdbjit": 0, 30 | "v8_no_strict_aliasing": 1, 31 | "v8_use_snapshot": "false", 32 | "nodedir": "/Users/larchanka/.node-gyp/0.10.15", 33 | "copy_dev_lib": "true", 34 | "standalone_static_library": 1 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /docs/app.js: -------------------------------------------------------------------------------- 1 | define(['../xf/src/xf.framework'], function (XF) { 2 | 3 | return XF.App.extend({ 4 | initialize: function () { 5 | XF.ui.loader.show(); 6 | XF.once('component:menu:constructed', XF.ui.loader.hide); 7 | } 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /docs/basics.html: -------------------------------------------------------------------------------- 1 |

Basics

2 | 3 |

XFramework UI element is 'an extended version of Document Object' or the another parallel could be done with jQuery Plugin. It fills the gap with Rich UI Elements for all the browsers. It means that XFramework will parse your HTML and add some markup to make UI controls user- and developer-friendly.

4 | 5 |

Each UI element has a simple markup that will be enhanced into the rich element.

6 | 7 |
<ul data-role="listview">
 8 |     <li data-role="divider">A</li>
 9 |     <li>
10 |         <h2>Header</h2>
11 |         <p>No link</p>
12 |     </li>
13 |     <li><a href="#">Simple link</a></li>
14 |     <li data-role="divider">Divider</li>
15 |     <li><a href="#">
16 |         <h2>Header</h2>
17 |         <p>Header and description</p>
18 |     </a></li>
19 | </ul>
20 | 
21 | 22 |

Will be converted to:

23 | 24 |
<ul data-role="listview" data-skip-enhance="true" id="xf-8293" class="xf-listview">
25 |     <li class=" xf-li xf-li-divider">A</li>
26 |     <li class="xf-li-static xf-li">
27 |         <div class="xf-li-wrap">
28 |             <h2 class="xf-li-header">Header</h2>
29 |             <p class="xf-li-desc">No link</p>
30 |         </div>
31 |     </li>
32 |     <li class=" xf-li">
33 |         <a href="#" class="xf-li-btn">
34 |             Simple link
35 |             <div class="xf-btn-text"></div>
36 |         </a>
37 |     </li>
38 |     <li class=" xf-li xf-li-divider">Divider</li>
39 |     <li class=" xf-li">
40 |         <a href="#" class="xf-li-btn">
41 |             <div class="xf-btn-text">
42 |                 <h2 class="xf-li-header">Header</h2>
43 |                 <p class="xf-li-desc">Header and description</p>
44 |             </div>
45 |         </a>
46 |     </li>
47 | </ul>
48 | 
49 | 50 |

The look of form inputs, buttons will be enhanced automatically, but you can use certain attributes to customize some of them. See full list of the data- attributes below.

-------------------------------------------------------------------------------- /docs/building_your_xf_app_for_testing_and_production.html: -------------------------------------------------------------------------------- 1 |

Building your XF app for testing and production

2 | 3 |

yo xf:application build [appName]

-------------------------------------------------------------------------------- /docs/buttons.html: -------------------------------------------------------------------------------- 1 |

Buttons

2 | 3 |

Inputs of types submit, reset, button, <button>s, and links with attribute [data-role=button] will be styled as buttons. For example a usual input[type=submit] will look like this:

4 | 5 |
<a data-role="button">A button</a>
 6 | 
7 | 8 |

A button

9 | 10 |

Buttons can be of several types: normal, special and alert. They differ only in appearance.

11 | 12 |

A special button is displayed in an accented color, blue by default, generally needed to highlight the most important button among several. To make a button special add data-special=true attribute to it.

13 | 14 |

The following code:

15 | 16 |
<a data-special="true"
17 |    data-appearance="button" href="#">
18 |    A special button
19 | </a>
20 | 
21 | 22 |

Will produce the following button:

23 | 24 |

data-appearance="button" href="#">
A special button

25 | 26 |

An alert button is red by default and is recommended to be used for actions that may cause data loss, e.g. for a delete button. Add data-alert=true attribute to get such a button:

27 | 28 |
<button data-alert="true">An alert button</button>
29 | 
30 | 31 |

Will produce the following button:

32 | 33 |

34 | 35 |

You may need to use smaller buttons in your app. To make them, add a [data-small=true] attribute to the necessary controls.

36 | 37 |

To make a common back button use a [data-back=true] attribute. It will go back in browser history by default.

38 | 39 |
<a data-appearance="button" data-small="true" href="#">A small button</a>
40 | <a data-appearance="backbtn" href="#"> Back button</a>
41 | 
42 | 43 |

A small button
Back button

-------------------------------------------------------------------------------- /docs/collection.html: -------------------------------------------------------------------------------- 1 |

Collection

2 | 3 |

XF.Collection is an extended version of Backbone.Collection to make it perfectly fit XF Component based architecture.

4 | 5 |

To create a standalone view XF Generator can be used with yo xf:collection [name].

6 | 7 |

XF.Collection properties:

8 | 9 | 14 | 15 |

XF.Collection methods:

16 | 17 | 21 | 22 |

XF.Collection hooks:

23 | 24 | 28 | 29 |

For other methods, properties and hooks available see Backbone.Collection.

-------------------------------------------------------------------------------- /docs/component.html: -------------------------------------------------------------------------------- 1 |

Component

2 | 3 |

Components are the building blocks of XFramework. A component is a part of an application that can be abstracted as an independent unit and can be reused throughout the application. Components can be nested in one another, can have customized presentations depending on the device it is used on. You can create as many device profiles as you need and customize whole application in any way for each of them.

4 | 5 |

A component separates logic from presentation, which are stored as separate files and are loaded only when the component needs to be rendered, which helps to save on load times and device resources. Though component caching mechanism allows to prefetch component parts when developer deems it necessary.

6 | 7 |

To use a component a developer places a marker where the component will be rendered. The marker is a

with special attributes which denote the component's name and instance id.

8 | 9 |

See an example of two nested components declared in the page markup:

10 | 11 |
<div data-component="categoryList" data-id="categoryListBooks">
12 |     This content will be shown while component is loading...
13 | </div>
14 | 
15 | 16 |

Such an approach lets a developer build asynchronuous UIs and avoid loading extraneous resources.

17 | 18 |

There's a way to customize the component with the starting options from outside:

19 | 20 |
    <div data-component="categoryList" data-id="categoryListBooks">
21 |             This content will be shown while component is loading...
22 |             <script>
23 |                 XF.setOptionsByID('categoryListBooks',
24 |                     {
25 |                         currentPage: 2
26 |                     }
27 |                 );
28 |             </script>
29 |         </div>
30 | 
31 | 32 |

You can use XF.setOptionsByID method in the place you decided but before component instance was created.

-------------------------------------------------------------------------------- /docs/component_creation.html: -------------------------------------------------------------------------------- 1 |

Component creation

2 | 3 |

There are two ways to create a component:

4 | 5 | 9 | 10 |

Every component should extend XF.Component that allows general functionality, loading and rendering processes work without your input.

11 | 12 |

First of all, you will need a JavaScript file which defines component's logic and may override some XF.Component properties to achieve specific behavior.

13 | 14 |
XF.define(
15 |     'MyApp.components.categoryList', // just 'categoryList' is fine as well
16 |     XF.Component.extend({
17 |         construct: function () {
18 |             // will be called on the start of construction
19 |         },
20 | 
21 | 
    initialize: function () {
22 |         // this method will be called after component construction
23 |     },
24 | 
25 |     // for more flexibility you can define Views in separate files
26 |     View: XF.View.extend({
27 |         initialize: function () {
28 | 
29 |         }
30 |     }),
31 | 
32 |     // for more flexibility you can define Collections in separate files
33 |     Collection: XF.Collection.extend({
34 |         url: 'books.json',
35 |         initialize: function () {
36 | 
37 |         }
38 |     })
39 | })
40 | 
41 | 42 | ); 43 |
44 | 45 |

This call defines a new component via two arguments: component name and component class.

46 | 47 |

Component name: used to setup a placeholder (-s) for instance (-s) of the component and should be unique within the application.

48 | 49 |

Component class: definition is created via XF.[some_base_component].extend() call. Here you should pass properties and methods you want to add to the base class functionality as non-static and static part of a class respectively.

50 | 51 |

You may add/override properties of the component model, collection and view. Basically you are able to modify everything you see.

52 | 53 |

Be aware of proper usage Collections and Models. By default XF.Component has XF.View and XF.Collection classes (XF.Component.Model is not defined). If the component has linkage to Collection and Model classes the collection will be created.

54 | 55 |

XF.Component properties:

56 | 57 | 69 | 70 |

XF.Component methods:

71 | 72 | 75 | 76 |

XF.Component hooks:

77 | 78 | -------------------------------------------------------------------------------- /docs/creation_of_custom_xframework_build.html: -------------------------------------------------------------------------------- 1 |

Creation of custom XFramework build

2 | 3 |

Custom xf.js and xf.min js build:

4 | 5 | 9 | 10 |

Full list of available elements can be found at xf/ui and xf/src directory of XFramework repository.

-------------------------------------------------------------------------------- /docs/data/basics.html: -------------------------------------------------------------------------------- 1 |

Basics

2 | 3 |

XFramework UI element is 'an extended version of Document Object' or the another parallel could be done with jQuery Plugin. It fills the gap with Rich UI Elements for all the browsers. It means that XFramework will parse your HTML and add some markup to make UI controls user- and developer-friendly.

4 | 5 |

Each UI element has a simple markup that will be enhanced into the rich element.

6 | 7 |
<ul data-role="listview">
 8 |     <li data-role="divider">A</li>
 9 |     <li>
10 |         <h2>Header</h2>
11 |         <p>No link</p>
12 |     </li>
13 |     <li><a href="#">Simple link</a></li>
14 |     <li data-role="divider">Divider</li>
15 |     <li><a href="#">
16 |         <h2>Header</h2>
17 |         <p>Header and description</p>
18 |     </a></li>
19 | </ul>
20 | 
21 | 22 |

Will be converted to:

23 | 24 |
<ul data-role="listview" data-skip-enhance="true" id="xf-8293" class="xf-listview">
25 |     <li class=" xf-li xf-li-divider">A</li>
26 |     <li class="xf-li-static xf-li">
27 |         <div class="xf-li-wrap">
28 |             <h2 class="xf-li-header">Header</h2>
29 |             <p class="xf-li-desc">No link</p>
30 |         </div>
31 |     </li>
32 |     <li class=" xf-li">
33 |         <a href="#" class="xf-li-btn">
34 |             Simple link
35 |             <div class="xf-btn-text"></div>
36 |         </a>
37 |     </li>
38 |     <li class=" xf-li xf-li-divider">Divider</li>
39 |     <li class=" xf-li">
40 |         <a href="#" class="xf-li-btn">
41 |             <div class="xf-btn-text">
42 |                 <h2 class="xf-li-header">Header</h2>
43 |                 <p class="xf-li-desc">Header and description</p>
44 |             </div>
45 |         </a>
46 |     </li>
47 | </ul>
48 | 
49 | 50 |

The look of form inputs, buttons will be enhanced automatically, but you can use certain attributes to customize some of them. See full list of the data- attributes below.

-------------------------------------------------------------------------------- /docs/data/building_your_xf_app_for_testing_and_production.html: -------------------------------------------------------------------------------- 1 |

Building your XF app for testing and production

2 | 3 |

yo xf:application build [appName]

-------------------------------------------------------------------------------- /docs/data/buttons.html: -------------------------------------------------------------------------------- 1 |

Buttons

2 | 3 |

Inputs of types submit, reset, button, <button>s, and links with attribute [data-role=button] will be styled as buttons. For example a usual input[type=submit] will look like this:

4 | 5 |
<a data-role="button">A button</a>
 6 | 
7 | 8 |

A button

9 | 10 |

Buttons can be of several types: normal, special and alert. They differ only in appearance.

11 | 12 |

A special button is displayed in an accented color, blue by default, generally needed to highlight the most important button among several. To make a button special add data-special=true attribute to it.

13 | 14 |

The following code:

15 | 16 |
<a data-special="true"
17 |    data-appearance="button" href="#">
18 |    A special button
19 | </a>
20 | 
21 | 22 |

Will produce the following button:

23 | 24 |

data-appearance="button" href="#">
A special button

25 | 26 |

An alert button is red by default and is recommended to be used for actions that may cause data loss, e.g. for a delete button. Add data-alert=true attribute to get such a button:

27 | 28 |
<button data-alert="true">An alert button</button>
29 | 
30 | 31 |

Will produce the following button:

32 | 33 |

34 | 35 |

You may need to use smaller buttons in your app. To make them, add a [data-small=true] attribute to the necessary controls.

36 | 37 |

To make a common back button use a [data-back=true] attribute. It will go back in browser history by default.

38 | 39 |
<a data-appearance="button" data-small="true" href="#">A small button</a>
40 | <a data-appearance="backbtn" href="#"> Back button</a>
41 | 
42 | 43 |

A small button
Back button

-------------------------------------------------------------------------------- /docs/data/collection.html: -------------------------------------------------------------------------------- 1 |

Collection

2 | 3 |

XF.Collection is an extended version of Backbone.Collection to make it perfectly fit XF Component based architecture.

4 | 5 |

To create a standalone view XF Generator can be used with yo xf:collection [name].

6 | 7 |

XF.Collection properties:

8 | 9 | 14 | 15 |

XF.Collection methods:

16 | 17 | 21 | 22 |

XF.Collection hooks:

23 | 24 | 28 | 29 |

For other methods, properties and hooks available see Backbone.Collection.

-------------------------------------------------------------------------------- /docs/data/component.html: -------------------------------------------------------------------------------- 1 |

Component

2 | 3 |

Components are the building blocks of XFramework. A component is a part of an application that can be abstracted as an independent unit and can be reused throughout the application. Components can be nested in one another, can have customized presentations depending on the device it is used on. You can create as many device profiles as you need and customize whole application in any way for each of them.

4 | 5 |

A component separates logic from presentation, which are stored as separate files and are loaded only when the component needs to be rendered, which helps to save on load times and device resources. Though component caching mechanism allows to prefetch component parts when developer deems it necessary.

6 | 7 |

To use a component a developer places a marker where the component will be rendered. The marker is a

with special attributes which denote the component's name and instance id.

8 | 9 |

See an example of two nested components declared in the page markup:

10 | 11 |
<div data-component="categoryList" data-id="categoryListBooks">
12 |     This content will be shown while component is loading...
13 | </div>
14 | 
15 | 16 |

Such an approach lets a developer build asynchronuous UIs and avoid loading extraneous resources.

17 | 18 |

There's a way to customize the component with the starting options from outside:

19 | 20 |
    <div data-component="categoryList" data-id="categoryListBooks">
21 |             This content will be shown while component is loading...
22 |             <script>
23 |                 XF.setOptionsByID('categoryListBooks',
24 |                     {
25 |                         currentPage: 2
26 |                     }
27 |                 );
28 |             </script>
29 |         </div>    
30 | 
31 | 32 |

You can use XF.setOptionsByID method in the place you decided but before component instance was created.

-------------------------------------------------------------------------------- /docs/data/component_creation.html: -------------------------------------------------------------------------------- 1 |

Component creation

2 | 3 |

There are two ways to create a component:

4 | 5 | 9 | 10 |

Every component should extend XF.Component that allows general functionality, loading and rendering processes work without your input.

11 | 12 |

First of all, you will need a JavaScript file which defines component's logic and may override some XF.Component properties to achieve specific behavior.

13 | 14 |
XF.define(
15 |     'MyApp.components.categoryList', // just 'categoryList' is fine as well
16 |     XF.Component.extend({
17 |         construct: function () {
18 |             // will be called on the start of construction 
19 |         },
20 | 
21 | 
    initialize: function () {
22 |         // this method will be called after component construction
23 |     },
24 | 
25 |     // for more flexibility you can define Views in separate files
26 |     View: XF.View.extend({
27 |         initialize: function () {
28 | 
29 |         }
30 |     }),
31 | 
32 |     // for more flexibility you can define Collections in separate files
33 |     Collection: XF.Collection.extend({
34 |         url: 'books.json',
35 |         initialize: function () {
36 | 
37 |         }
38 |     })
39 | })
40 | 
41 | 42 | ); 43 |
44 | 45 |

This call defines a new component via two arguments: component name and component class.

46 | 47 |

Component name: used to setup a placeholder (-s) for instance (-s) of the component and should be unique within the application.

48 | 49 |

Component class: definition is created via XF.[some_base_component].extend() call. Here you should pass properties and methods you want to add to the base class functionality as non-static and static part of a class respectively.

50 | 51 |

You may add/override properties of the component model, collection and view. Basically you are able to modify everything you see.

52 | 53 |

Be aware of proper usage Collections and Models. By default XF.Component has XF.View and XF.Collection classes (XF.Component.Model is not defined). If the component has linkage to Collection and Model classes the collection will be created.

54 | 55 |

XF.Component properties:

56 | 57 | 69 | 70 |

XF.Component methods:

71 | 72 | 75 | 76 |

XF.Component hooks:

77 | 78 | -------------------------------------------------------------------------------- /docs/data/creation_of_custom_xframework_build.html: -------------------------------------------------------------------------------- 1 |

Creation of custom XFramework build

2 | 3 |

Custom xf.js and xf.min js build:

4 | 5 | 9 | 10 |

Full list of available elements can be found at xf/ui and xf/src directory of XFramework repository.

-------------------------------------------------------------------------------- /docs/data/footer.html: -------------------------------------------------------------------------------- 1 |

Footer

2 | 3 |

Below you can find an example of footer UI element:

4 | 5 |
<div data-role="footer" data-fixed="true">
 6 |     <button data-icon="star">Link 1</button>
 7 |     <button data-icon="help">Link 2</button>
 8 |     <button data-icon="heart">Link 3</button>
 9 |     <button data-icon="print">Link 4</button>
10 | </div>
11 | 
12 | 13 |

Such code will be transformed into:

14 | 15 |
<div data-role="footer" data-fixed="true" data-id="xf-29339" id="xf-29339" data-skip-enhance="true">
16 |     <div class="xf-footer  xf-footer-fixed ">
17 |         <ul class="xf-nav">
18 |             <li class="xf-grid-unit xf-grid-unit-1of4">
19 |                 <a data-href="" class="xf-nav-item xf-iconpos-top" id="xf-29339-item0">
20 |                     <div class="xf-icon xf-icon-big xf-icon-star"></div>
21 |                     <div class="xf-nav-item-text ">Link 1</div>
22 |                 </a>
23 |             </li>
24 |             <li class="xf-grid-unit xf-grid-unit-1of4">
25 |                 <a data-href="" class="xf-nav-item xf-iconpos-top" id="xf-29339-item1">
26 |                     <div class="xf-icon xf-icon-big xf-icon-help"></div>
27 |                     <div class="xf-nav-item-text ">Link 2</div>
28 |                 </a>
29 |             </li>
30 |             <li class="xf-grid-unit xf-grid-unit-1of4">
31 |                 <a data-href="" class="xf-nav-item xf-iconpos-top" id="xf-29339-item2">
32 |                     <div class="xf-icon xf-icon-big xf-icon-heart"></div>
33 |                     <div class="xf-nav-item-text ">Link 3</div>
34 |                 </a>
35 |             </li>
36 |             <li class="xf-grid-unit xf-grid-unit-1of4">
37 |                 <a data-href="" class="xf-nav-item xf-iconpos-top" id="xf-29339-item3">
38 |                     <div class="xf-icon xf-icon-big xf-icon-print"></div>
39 |                     <div class="xf-nav-item-text ">Link 4</div>
40 |                 </a>
41 |             </li>
42 |         </ul>
43 |     </div>
44 | </div>
45 | 
-------------------------------------------------------------------------------- /docs/data/header.html: -------------------------------------------------------------------------------- 1 |

Header

2 | 3 |

Below you can find an example of header UI element:

4 | 5 |
<div data-role="header" data-fixed="true">
 6 |     <button data-small="true" data-icon="backbtn" data-position="left"></button>
 7 |     <h1>Title</h1>
 8 | </div>
 9 | 
10 | 11 |

Such code will be transformed into:

12 | 13 |
<div data-role="header" data-id="xf-18586" id="xf-18586" data-skip-enhance="true">
14 |     <header class="xf-header ">
15 |         <button data-small="true" data-icon="backbtn" id="backbtn" data-position="left" data-animation="slideright"  data-skip-enhance="true" class="xf-button-float-left xf-button-header-left xf-button-small xf-iconpos-left xf-button-small-icon-only">
16 |             <span class="xf-icon xf-icon-backbtn xf-icon-small"></span>
17 |         </button>
18 |         <h1 class="xf-header-title">Title</h1>
19 |     </header>
20 | </div>
21 | 
-------------------------------------------------------------------------------- /docs/data/installing_xframework_generator.html: -------------------------------------------------------------------------------- 1 |

Installing XFramework Generator

2 | 3 |

You don't need to download the source code from the repo, create all the necessary files for the web app, writing two thousands line of code just to create a Hello world! app. XFramework Generator can make everything for you.

4 | 5 |

XF Generator has a number of dependencies such as:

6 | 7 | 12 | 13 |

To install first two of them on Mac OS X or Windows computers you just need to download a package from nodejs.org/download/. For other platforms see the readme.

14 | 15 |

After installing node.js and npm go to terminal and install Yeoman writing npm install -g yo (with sudo if necessary).

16 | 17 |

Almost there! After these steps you need to install XF Generator with npm install -g generator-xf.

-------------------------------------------------------------------------------- /docs/data/list_of_built-in_events.html: -------------------------------------------------------------------------------- 1 |

List of built-in events

2 | 3 |

XF level (XF.on, XF.off, XF.trigger, etc.)

4 | 5 | 15 | 16 |

You can fire any event to the desired component in format component:componentID:eventName. Even if there is no component with such id these events will be delayed until it will be rendered.

17 | 18 |

Component level

19 | 20 | 23 | 24 |

Model level

25 | 26 | 30 | 31 |

Keep in mind that all Backbone.js built-in events are available.

32 | 33 |

Collection level

34 | 35 | 39 | 40 |

Keep in mind that all Backbone.js built-in events are available.

41 | 42 |

View level

43 | 44 | 49 | 50 |

Keep in mind that all Backbone.js built-in events are available.

-------------------------------------------------------------------------------- /docs/data/listview.html: -------------------------------------------------------------------------------- 1 |

Listview

2 | 3 |

To make a data list add a [data-role="listview"] attribute to a UL or OL element.

4 | 5 |

If list elements must be clickable wrap all the contents of the list items in A elements: UL[data-role=listview] > LI > A > whatever.

6 | 7 |

To make a divider between list items make another LI with a data-role="divider" attribute.

8 | 9 |

See code and result lower in this section.

10 | 11 |

List items with icons

12 | 13 |

Just like with buttons icons can be added to list items using data-icon and data-iconpos attributes. Note that [data-iconpos=top] and [data-iconpos=bottom] values are not supported on list items.

14 | 15 |

List elements can have a count bubble. Just add a SPAN.xf-count-bubble inside them

16 | 17 |

Lists with thumbnails

18 | 19 |

If you need to have thumbnails inside list items, you don't need to do anything special, just put the image inside the list item, and it will be displayed on the left hand side. The image can be moved to the right side by adding [data-thumbpos="right"] attribute to the corresponding LI element.

20 | 21 |

Sample listview markup:

22 | 23 |
<ul data-role="listview">
24 |     <li data-role="divider">A</li>
25 |     <li>
26 |         <h2>Header</h2>
27 |         <p>No link</p>
28 |     </li>
29 |     <li><a href="#">Simple link</a></li>
30 |     <li data-role="divider">Divider</li>
31 |     <li><a href="#">
32 |         <h2>Header</h2>
33 |         <p>Header and description</p>
34 |     </a></li>
35 |     <li data-icon="chevron-thin-right" data-iconpos="right"><a href="#">
36 |         <h2>With Icon</h2>
37 |         <p>List item  with icon on the right</p>
38 |     </a></li>
39 |     <li data-icon="chevron-thin-right" data-iconpos="right"><a href="#">
40 |         <h2>With Icon and Count</h2>
41 |         <p>List item  with icon on the right and count</p>
42 |         <span class="xf-count-bubble">32</span>
43 |     </a></li>
44 |     <li data-thumbpos="right"><a href="#">
45 |         <img src="../img/_thumb1.jpg" alt="">
46 |         <h2>With Thumbnail on the right</h2>
47 |         <p>List item  with a thumbnail on the right</p>
48 |     </a></li>
49 | </ul>
50 | 
51 | 52 |

The above code will result in:

53 | 54 | -------------------------------------------------------------------------------- /docs/data/model.html: -------------------------------------------------------------------------------- 1 |

Model

2 | 3 |

XF.Model is an extended version of Backbone.Model to make it perfectly fit XF Component based architecture.

4 | 5 |

To create a standalone view XF Generator can be used with yo xf:model [name].

6 | 7 |

XF.Model properties:

8 | 9 | 14 | 15 |

XF.Model methods:

16 | 17 | 21 | 22 |

XF.Model hooks:

23 | 24 | 28 | 29 |

For other methods, properties and hooks available see Backbone.Model.

-------------------------------------------------------------------------------- /docs/data/supported_platforms_and_browsers.html: -------------------------------------------------------------------------------- 1 |

Supported Platforms and Browsers

2 | 3 |

Mobile phones and tablets:

4 | 5 | 10 | 11 |

Desktop:

12 | 13 | 20 | 21 |

It doesn't mean that another browsers or platforms are not supported. We just don't have a possibility to test XF across all existing platforms and browsers.

22 | 23 |

The roadmap for the next versions can be found on GitHub.

-------------------------------------------------------------------------------- /docs/data/tabs.html: -------------------------------------------------------------------------------- 1 |

Tabs

2 | 3 |

Below you can find an example of tabs UI element:

4 | 5 |
<div data-role="tabs">
 6 |     <button data-active="true">Link 1</button>
 7 |     <button>Link 2</button>
 8 |     <button>Link 3</button>
 9 |     <button>Link 4</button>
10 | </div>
11 | 
12 | 13 |

Such code will be transformed into:

14 | 15 |
<div data-role="tabs" data-id="xf-37715" id="xf-37715" data-skip-enhance="true">
16 |     <ul class="xf-tabs">
17 |         <li class="xf-grid-unit  xf-grid-unit-1of4">
18 |             <a class="xf-tabs-button  xf-corner-tl  xf-corner-bl" id="xf-71177">
19 |                 <span class="xf-tabs-button-text">Link 1</span>
20 |             </a>
21 |         </li>
22 |         <li class="xf-grid-unit  xf-grid-unit-1of4">
23 |             <a class="xf-tabs-button" id="xf-7896">
24 |                 <span class="xf-tabs-button-text">Link 2</span>
25 |             </a>
26 |         </li>
27 |         <li class="xf-grid-unit  xf-grid-unit-1of4">
28 |             <a class="xf-tabs-button" id="xf-62398">
29 |                 <span class="xf-tabs-button-text">Link 3</span>
30 |             </a>
31 |         </li>
32 |         <li class="xf-grid-unit  xf-grid-unit-1of4">
33 |             <a class="xf-tabs-button  xf-corner-tr  xf-corner-br xf-tabs-button-active" id="xf-83850">
34 |                 <span class="xf-tabs-button-text">Link 4</span>
35 |             </a>
36 |         </li>
37 |     </ul>
38 | </div>
39 | 
-------------------------------------------------------------------------------- /docs/data/the_idea_behind.html: -------------------------------------------------------------------------------- 1 |

The idea behind

2 | 3 |

There are some rules behind the XFramework:

4 | 5 | -------------------------------------------------------------------------------- /docs/data/updating_xf_and_dependencies.html: -------------------------------------------------------------------------------- 1 |

Updating XF and dependencies

2 | 3 |

XF Generator allows you to update sources and dependencies:

4 | 5 | -------------------------------------------------------------------------------- /docs/data/view.html: -------------------------------------------------------------------------------- 1 |

View

2 | 3 |

XF.View is an extended version of Backbone.View to make it perfectly fit XF Component based architecture.

4 | 5 |

To create a standalone view XF Generator can be used with yo xf:view [name].

6 | 7 |

XF.View properties:

8 | 9 | 14 | 15 |

XF.View methods:

16 | 17 | 23 | 24 |

XF.View hooks:

25 | 26 | 36 | 37 |

Note that data from collection or model of the component is available in the template via localized variable data.

38 | 39 |

For other methods, properties and hooks available see Backbone.View.

-------------------------------------------------------------------------------- /docs/data/what_is_xframework_.html: -------------------------------------------------------------------------------- 1 |

What is XFramework?

2 | 3 |

Version 0.9.1

4 | 5 |

XFramework (next XF) is a small yet powerful Javascript framework for quick development of cross-device web applications. Honestly saying, XF is a high-level framework based on Backbone.js that implements its own architecture paradigm, based on MV* on the component level.

6 | 7 |

XFramework makes it easy to reuse the application logic and provide various layouts or widgets for different devices based on the criteria that you define.

8 | 9 |

XFramework is designed to be extremely modular, flexible, fast, and easy to use. To develop an app in X-Framework a developer should be familiar with common web technologies such as HTML/CSS/JS, LESS for editing styles, Handlebars-style templating and have an understanding of how MV* architecture works. Experience using Backbone.js, Angular.js, Ember.js, jQuery Mobile or other framework will be helpful.

10 | 11 |

XFramework currently features:

12 | 13 | -------------------------------------------------------------------------------- /docs/data/xf_app.html: -------------------------------------------------------------------------------- 1 |

XF.App

2 | 3 |

XF.App is a 'class' that you able to extend with your own methods and properties needed in the application. In this case an instance of this class is something like a main controller of the whole app.

4 | 5 |
// if the app boilerplate was created via XF Generator
 6 | // these lines can be found in `app.js` file
 7 | var MyApp = XF.App.extend({
 8 |     initialize: function () {
 9 |         // this code will be executed before XF will be started 
10 |             // but you can put the preparation code here
11 |             // …            
12 |             this.myAwesomeMethod();
13 |     },
14 |         myAwesomeMethod: function () {
15 | 
16 | 
    }
17 | 
18 | 19 | }); 20 |
-------------------------------------------------------------------------------- /docs/data/xf_device.html: -------------------------------------------------------------------------------- 1 |

XF.device

2 | 3 |

XF.device contains the information about current user device app was launched:

4 | 5 | 12 | 13 |
var app = new MyApp({
14 |         // …
15 |         // other settings for the application
16 |     device: {
17 |                 types : [{
18 |             name : 'tablet',
19 |             range : {
20 |                 max : 1024,
21 |                 min : 569
22 |             },
23 |             templatePath : 'tablet/' // template path for tablet devices (by default it will be tmpl/tablet/componentName.tmpl)
24 |         }, {
25 |             name : 'phone',
26 |             range : {
27 |                 max : 568,
28 |                 min : null
29 |             },
30 |             templatePath : 'phone/' // path to templates for phones (by default it     will be tmpl/phone/componentName.tmpl)
31 |         }]
32 |     }
33 | });
34 | 
-------------------------------------------------------------------------------- /docs/data/xf_router.html: -------------------------------------------------------------------------------- 1 |

XF.Router

2 | 3 |

XF.Router is an extended [Backbone.Router]. XF cares about creation of router instance, its starting, binding handlers and so on. Everything you just need to do is to pass your routes and handlers with starting options for the application:

4 | 5 |
    // if the app boilerplate was created via XF Generator
 6 |     // these lines cab be found in `index.js` file
 7 |     var app = new MyApp({
 8 |         // …
 9 |         // other settings for the application
10 |     router: {
11 |         routes: {
12 |             '':                                         'home',
13 |             'search/:q':                            'searchByQuery',
14 |             'item:id':                              'showItemById',
15 |                         'books/:cat(/:subcat)': 'showBooksCategory',
16 |                         'news/*any':                            'showNews'
17 |         },
18 | 
19 | 
    home: function () {
20 | 
21 |     },
22 | 
23 |     searchByQuery: function (query) {
24 | 
25 |     },
26 | 
27 |     showItemById: function (id) {
28 | 
29 |     },
30 | 
31 |             showBooksCategory: function (cat, subcat) {
32 | 
33 |             },
34 | 
35 |             showNews: function (param) {
36 | 
37 |             }
38 | }
39 | 
40 | 41 | }); 42 |
43 | 44 |

In the example above the handler home for empty route was created. In case you want to define the starting route for the application or turn off HTML5 pushState (using pushState support is turned on by default) you should pass the necessary starting parameters to XF.history which actually is a link to Backbone.history.

45 | 46 |
var app = new MyApp({
47 |         // …
48 |         // other settings for the application
49 |     history: {
50 |                 pushState: false,
51 |                 root: 'books/fiction'
52 |     }
53 | });
54 | 
55 | 56 |

To force navigation to another url fragment a number of ways is available:

57 | 58 | 63 | 64 |

All elements with the attribute data-href or href will work on changing url fragment.

65 | 66 |
<a data-href="books/fiction">Books</a>
67 | 
-------------------------------------------------------------------------------- /docs/data/xf_settings.html: -------------------------------------------------------------------------------- 1 |

XF.settings

2 | 3 |

This simple object contains the settings for the application, that could be overridden on the start:

4 | 5 | 16 | 17 |
var app = new MyApp({
18 |         // …
19 |         // other settings for the application
20 |         settings: {
21 |                 appVersion: '2.0.1',
22 | 
23 | 
    dataUrlPrefix: 'http://api.example.com/',
24 | 
25 |     ajaxSettings: {
26 |         // settings that are provided to collections and models to fetch and sync the data
27 |                 // see $.ajax options
28 |                 crossDomain: true
29 |     }
30 | }
31 | 
32 | 33 | }); 34 |
-------------------------------------------------------------------------------- /docs/data/xf_source_modules.html: -------------------------------------------------------------------------------- 1 |

XF source modules

2 | 3 |

XFramework has its own building blocks that drive it on. Some blocks are mandatory to include in the build of XFramework, other ones are not required.

4 | 5 |

Mandatory XF src modules are:

6 | 7 | 19 | 20 |

Optional XF src modules are:

21 | 22 | -------------------------------------------------------------------------------- /docs/data/xf_storage.html: -------------------------------------------------------------------------------- 1 |

XF.storage

2 | 3 |

XF.storage is just a wrapper for localStorage that allows you easily to interact with it: set, get, clear.

4 | 5 |
XF.storage.set('booksCategory', 12);
 6 | 
 7 | XF.storage.get('booksCategory');
 8 | 
 9 | XF.storage.clear();
10 | 
-------------------------------------------------------------------------------- /docs/data/xf_touch.html: -------------------------------------------------------------------------------- 1 |

XF.touch

2 | 3 |

XF.touch makes the life in such a multidevice world easier — it is an adapter for all types of user contexts: touch screens, mouse, pointers.

4 | 5 |

For now it contains the following user interaction events:

6 | 7 | 11 | 12 |

* XF.touch fixes the 300ms gap between click and touch events as well*

13 | 14 |

24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /docs/data/xf_utils.html: -------------------------------------------------------------------------------- 1 |

XF.utils

2 | 3 |

This source module will contain all the helpers will needed to make the work with XF much more easier. Right now it contains only address bar hiding helper for iOS and Android mobile phones.

-------------------------------------------------------------------------------- /docs/data/xf_zepto_support_js.html: -------------------------------------------------------------------------------- 1 |

XF.zepto.support.js

2 | 3 |

This piece enables the support of zepto.js instead of jQuery extending it with all missing methods and properties.

4 | 5 |

Attention! Include this module on your own fear and risk. It is in experimental status right away.

-------------------------------------------------------------------------------- /docs/data/your_first_xf_web_app.html: -------------------------------------------------------------------------------- 1 |

Your first XF web app

2 | 3 |

To create the first XF application the simplest way is to use XF Generator through yo xf:application init [appName].

4 | 5 |

For now it scaffolds an app in the way you can see by example: XF Hello World App.

-------------------------------------------------------------------------------- /docs/docs.css: -------------------------------------------------------------------------------- 1 | html { 2 | font: 15px/1.5 sans-serif; 3 | } 4 | a { 5 | cursor: pointer; 6 | } 7 | ul, ol, dl { 8 | padding: 0 0 0 2em; 9 | } 10 | .example { 11 | margin: 1em 0; 12 | } 13 | code { 14 | color: #000070; 15 | -o-tab-size: 4; 16 | tab-size: 4; 17 | } 18 | .sublist { 19 | margin-left: 40px; 20 | } 21 | .data-table { 22 | margin: 1em 0; 23 | width: 100%; 24 | } 25 | .data-table td, 26 | .data-table th { 27 | padding: .5em; 28 | } 29 | 30 | .data-table td { 31 | vertical-align: middle 32 | } 33 | .col-icon { 34 | width: 20px; 35 | } 36 | .col-icon-label { 37 | } 38 | .nobr { 39 | white-space: nowrap; 40 | } 41 | .msg { 42 | padding: .4em 1em; 43 | border: 1px solid rgba(0,0,0,0.15); 44 | border-radius: 3px; 45 | -webkit-transition: all .5s; 46 | transition: all .5s; 47 | display: block; 48 | } 49 | 50 | .ok { 51 | background: #D1EDC8; 52 | } 53 | .warning { 54 | background: #EDC7C7; 55 | } 56 | 57 | #content .xf-has-header { 58 | margin-top: 0; 59 | } 60 | -------------------------------------------------------------------------------- /docs/footer.html: -------------------------------------------------------------------------------- 1 |

Footer

2 | 3 |

Below you can find an example of footer UI element:

4 | 5 |
<div data-role="footer" data-fixed="true">
 6 |     <button data-icon="star">Link 1</button>
 7 |     <button data-icon="help">Link 2</button>
 8 |     <button data-icon="heart">Link 3</button>
 9 |     <button data-icon="print">Link 4</button>
10 | </div>
11 | 
12 | 13 |

Such code will be transformed into:

14 | 15 |
<div data-role="footer" data-fixed="true" data-id="xf-29339" id="xf-29339" data-skip-enhance="true">
16 |     <div class="xf-footer  xf-footer-fixed ">
17 |         <ul class="xf-nav">
18 |             <li class="xf-grid-unit xf-grid-unit-1of4">
19 |                 <a data-href="" class="xf-nav-item xf-iconpos-top" id="xf-29339-item0">
20 |                     <div class="xf-icon xf-icon-big xf-icon-star"></div>
21 |                     <div class="xf-nav-item-text ">Link 1</div>
22 |                 </a>
23 |             </li>
24 |             <li class="xf-grid-unit xf-grid-unit-1of4">
25 |                 <a data-href="" class="xf-nav-item xf-iconpos-top" id="xf-29339-item1">
26 |                     <div class="xf-icon xf-icon-big xf-icon-help"></div>
27 |                     <div class="xf-nav-item-text ">Link 2</div>
28 |                 </a>
29 |             </li>
30 |             <li class="xf-grid-unit xf-grid-unit-1of4">
31 |                 <a data-href="" class="xf-nav-item xf-iconpos-top" id="xf-29339-item2">
32 |                     <div class="xf-icon xf-icon-big xf-icon-heart"></div>
33 |                     <div class="xf-nav-item-text ">Link 3</div>
34 |                 </a>
35 |             </li>
36 |             <li class="xf-grid-unit xf-grid-unit-1of4">
37 |                 <a data-href="" class="xf-nav-item xf-iconpos-top" id="xf-29339-item3">
38 |                     <div class="xf-icon xf-icon-big xf-icon-print"></div>
39 |                     <div class="xf-nav-item-text ">Link 4</div>
40 |                 </a>
41 |             </li>
42 |         </ul>
43 |     </div>
44 | </div>
45 | 
46 | 47 |

Bitdeli Badge

-------------------------------------------------------------------------------- /docs/header.html: -------------------------------------------------------------------------------- 1 |

Header

2 | 3 |

Below you can find an example of header UI element:

4 | 5 |
<div data-role="header" data-fixed="true">
 6 |     <button data-small="true" data-icon="backbtn" data-position="left"></button>
 7 |     <h1>Title</h1>
 8 | </div>
 9 | 
10 | 11 |

Such code will be transformed into:

12 | 13 |
<div data-role="header" data-id="xf-18586" id="xf-18586" data-skip-enhance="true">
14 |     <header class="xf-header ">
15 |         <button data-small="true" data-icon="backbtn" id="backbtn" data-position="left" data-animation="slideright"  data-skip-enhance="true" class="xf-button-float-left xf-button-header-left xf-button-small xf-iconpos-left xf-button-small-icon-only">
16 |             <span class="xf-icon xf-icon-backbtn xf-icon-small"></span>
17 |         </button>
18 |         <h1 class="xf-header-title">Title</h1>
19 |     </header>
20 | </div>
21 | 
-------------------------------------------------------------------------------- /docs/installing_xframework_generator.html: -------------------------------------------------------------------------------- 1 |

Installing XFramework Generator

2 | 3 |

You don't need to download the source code from the repo, create all the necessary files for the web app, writing two thousands line of code just to create a Hello world! app. XFramework Generator can make everything for you.

4 | 5 |

XF Generator has a number of dependencies such as:
* node.js
* NPM
* [Yeoman] (http://yeoman.io)

6 | 7 |

To install first two of them on Mac OS X or Windows computers you just need to download a package from nodejs.org/download/. For other platforms see the readme.

8 | 9 |

After installing node.js and npm go to terminal and install Yeoman writing npm install -g yo (with sudo if necessary).

10 | 11 |

Almost there! After these steps you need to install XF Generator with npm install -g generator-xf.

-------------------------------------------------------------------------------- /docs/js/components/home.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | XF.defineComponent( 3 | 'home', [], function() { 4 | return XF.Component.extend({ 5 | Collection: null, 6 | Model: null, 7 | initialize: function () { 8 | 9 | } 10 | }) 11 | } 12 | ); 13 | 14 | }); 15 | -------------------------------------------------------------------------------- /docs/js/components/menu.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | 3 | var extending = { 4 | getMenuItem: function (item) { 5 | var component = this, 6 | $component = component.selector(), 7 | $a = $('li > a', $component); 8 | 9 | XF.ui.loader.hide(); 10 | XF.ui.loader.show(); 11 | 12 | $a.removeClass('xf-li-btn-active'); 13 | $($component).find('[data-url=' + item + ']').addClass('xf-li-btn-active'); 14 | 15 | 16 | 17 | 18 | if (!$('#' + item).length) { 19 | $.get( 20 | this.getItemURL(item), 21 | function (data, status) { 22 | component.constructMenuItem(item, data); 23 | } 24 | ); 25 | }else{ 26 | this.showMenuItem(item); 27 | } 28 | 29 | 30 | }, 31 | 32 | constructMenuItem: function (item, data) { 33 | var html = '
' + data + '
'; 34 | if (this.options.contentSelector.length) $(this.options.contentSelector).append(html); 35 | else $('body').append(html); 36 | 37 | 38 | 39 | XF.ui.enhance($('#' + item)); 40 | this.showMenuItem(item); 41 | }, 42 | 43 | getItemURL: function (item) { 44 | return this.options.itemURL + item + this.options.itemURLPostfix; 45 | }, 46 | 47 | showMenuItem: function (item) { 48 | XF.trigger('pages:show', item); 49 | XF.ui.loader.hide(); 50 | }, 51 | 52 | Collection : XF.Collection.extend({ 53 | url: 'mocks/menu.json' 54 | 55 | }), 56 | 57 | options: { 58 | itemURL: 'data/', 59 | itemURLPostfix: '.html', 60 | contentSelector: '#content', 61 | defaultItem: 'what_is_xframework_', 62 | autorender: true, 63 | autoload: true, 64 | showFirstItem: true 65 | }, 66 | 67 | View: XF.View.extend({}), 68 | 69 | initialize: function () { 70 | var component = this, 71 | fragment = (XF.history.fragment !== '' && XF.history.fragment !== '/') ? 72 | XF.history.fragment : 73 | ((component.options.showFirstItem) ? component.options.defaultItem : XF.history.fragment ); 74 | 75 | XF.on('menu:go', _.bind(component.go, this)); 76 | XF.on('component:menu:rendered', function () {component.go({hash: fragment}); }); 77 | 78 | }, 79 | 80 | go: function (data) { 81 | if (_.isEmpty(data)) return; 82 | if (_.isEmpty(data.hash)) return; 83 | 84 | var hash = data.hash.replace(/^\/|\/$/g, ''); 85 | 86 | if (this.collection.where({url: hash})) { 87 | this.getMenuItem(hash); 88 | } else if (menu[this.options.defaultItem] !== undefined) { 89 | this.getMenuItem(this.options.defaultItem); 90 | } 91 | } 92 | 93 | }; 94 | 95 | 96 | 97 | XF.define( 98 | 'menu', [], 99 | function() {return XF.Component.extend(extending, {})} 100 | ); 101 | 102 | }); -------------------------------------------------------------------------------- /docs/list_of_built-in_events.html: -------------------------------------------------------------------------------- 1 |

List of built-in events

2 | 3 |

XF level (XF.on, XF.off, XF.trigger, etc.)

4 | 5 | 16 | 17 |

You can fire any event to the desired component in format component:componentID:eventName. Even if there is no component with such id these events will be delayed until it will be rendered.

18 | 19 |

Component level

20 | 21 | 24 | 25 |

Model level

26 | 27 | 31 | 32 |

Keep in mind that all Backbone.js built-in events are available.

33 | 34 |

Collection level

35 | 36 | 40 | 41 |

Keep in mind that all Backbone.js built-in events are available.

42 | 43 |

View level

44 | 45 | 50 | 51 |

Keep in mind that all Backbone.js built-in events are available.

-------------------------------------------------------------------------------- /docs/listview.html: -------------------------------------------------------------------------------- 1 |

Listview

2 | 3 |

To make a data list add a [data-role="listview"] attribute to a UL or OL element.

4 | 5 |

If list elements must be clickable wrap all the contents of the list items in A elements: UL[data-role=listview] > LI > A > whatever.

6 | 7 |

To make a divider between list items make another LI with a data-role="divider" attribute.

8 | 9 |

See code and result lower in this section.

10 | 11 |

List items with icons

12 | 13 |

Just like with buttons icons can be added to list items using data-icon and data-iconpos attributes. Note that [data-iconpos=top] and [data-iconpos=bottom] values are not supported on list items.

14 | 15 |

List elements can have a count bubble. Just add a SPAN.xf-count-bubble inside them

16 | 17 |

Lists with thumbnails

18 | 19 |

If you need to have thumbnails inside list items, you don't need to do anything special, just put the image inside the list item, and it will be displayed on the left hand side. The image can be moved to the right side by adding [data-thumbpos="right"] attribute to the corresponding LI element.

20 | 21 |

Sample listview markup:

22 | 23 |
<ul data-role="listview">
24 |     <li data-role="divider">A</li>
25 |     <li>
26 |         <h2>Header</h2>
27 |         <p>No link</p>
28 |     </li>
29 |     <li><a href="#">Simple link</a></li>
30 |     <li data-role="divider">Divider</li>
31 |     <li><a href="#">
32 |         <h2>Header</h2>
33 |         <p>Header and description</p>
34 |     </a></li>
35 |     <li data-icon="chevron-thin-right" data-iconpos="right"><a href="#">
36 |         <h2>With Icon</h2>
37 |         <p>List item  with icon on the right</p>
38 |     </a></li>
39 |     <li data-icon="chevron-thin-right" data-iconpos="right"><a href="#">
40 |         <h2>With Icon and Count</h2>
41 |         <p>List item  with icon on the right and count</p>
42 |         <span class="xf-count-bubble">32</span>
43 |     </a></li>
44 |     <li data-thumbpos="right"><a href="#">
45 |         <img src="../img/_thumb1.jpg" alt="">
46 |         <h2>With Thumbnail on the right</h2>
47 |         <p>List item  with a thumbnail on the right</p>
48 |     </a></li>
49 | </ul>
50 | 
51 | 52 |

The above code will result in:

53 | 54 | -------------------------------------------------------------------------------- /docs/model.html: -------------------------------------------------------------------------------- 1 |

Model

2 | 3 |

XF.Model is an extended version of Backbone.Model to make it perfectly fit XF Component based architecture.

4 | 5 |

To create a standalone view XF Generator can be used with yo xf:model [name].

6 | 7 |

XF.Model properties:

8 | 9 | 14 | 15 |

XF.Model methods:

16 | 17 | 21 | 22 |

XF.Model hooks:

23 | 24 | 28 | 29 |

For other methods, properties and hooks available see Backbone.Model.

-------------------------------------------------------------------------------- /docs/supported_platforms_and_browsers.html: -------------------------------------------------------------------------------- 1 |

Supported Platforms and Browsers

2 | 3 |

Mobile phones and tablets:

4 | 5 | 10 | 11 |

Desktop:

12 | 13 | 20 | 21 |

It doesn't mean that another browsers or platforms are not supported. We just don't have a possibility to test XF across all existing platforms and browsers.

22 | 23 |

The roadmap for the next versions can be found on GitHub.

-------------------------------------------------------------------------------- /docs/tabs.html: -------------------------------------------------------------------------------- 1 |

Tabs

2 | 3 |

Below you can find an example of tabs UI element:

4 | 5 |
<div data-role="tabs">
 6 |     <button data-active="true">Link 1</button>
 7 |     <button>Link 2</button>
 8 |     <button>Link 3</button>
 9 |     <button>Link 4</button>
10 | </div>
11 | 
12 | 13 |

Such code will be transformed into:

14 | 15 |
<div data-role="tabs" data-id="xf-37715" id="xf-37715" data-skip-enhance="true">
16 |     <ul class="xf-tabs">
17 |         <li class="xf-grid-unit  xf-grid-unit-1of4">
18 |             <a class="xf-tabs-button  xf-corner-tl  xf-corner-bl" id="xf-71177">
19 |                 <span class="xf-tabs-button-text">Link 1</span>
20 |             </a>
21 |         </li>
22 |         <li class="xf-grid-unit  xf-grid-unit-1of4">
23 |             <a class="xf-tabs-button" id="xf-7896">
24 |                 <span class="xf-tabs-button-text">Link 2</span>
25 |             </a>
26 |         </li>
27 |         <li class="xf-grid-unit  xf-grid-unit-1of4">
28 |             <a class="xf-tabs-button" id="xf-62398">
29 |                 <span class="xf-tabs-button-text">Link 3</span>
30 |             </a>
31 |         </li>
32 |         <li class="xf-grid-unit  xf-grid-unit-1of4">
33 |             <a class="xf-tabs-button  xf-corner-tr  xf-corner-br xf-tabs-button-active" id="xf-83850">
34 |                 <span class="xf-tabs-button-text">Link 4</span>
35 |             </a>
36 |         </li>
37 |     </ul>
38 | </div>
39 | 
-------------------------------------------------------------------------------- /docs/the_idea_behind.html: -------------------------------------------------------------------------------- 1 |

The idea behind

2 | 3 |

There are some rules behind the XFramework:

4 | 5 | -------------------------------------------------------------------------------- /docs/tmpl/mobile/header.tmpl: -------------------------------------------------------------------------------- 1 |
2 | <% _.each(get('buttons'), function(button) { %> 3 | data-<%=prop%>="<%=value%>" <% }); %> <%= button.dataHrefString %> <%= button.hasTooltip ? 'title="' + button.tooltip + '"' : '' %> id="<%= button.id %>"> 4 | <% if(button.hasText) { %> 5 | <%= button.text %> 6 | <% } %> 7 | <% if(button.hasIcon) { %> 8 | 9 | <% } %> 10 | 11 | <% }); %> 12 | <% if(get('hasTitle')) { %> 13 | <<%= get('headerElement') %> class="xf-header-title <%= get('titleClass') %>"><%= get('title') %>> 14 | <% } %> 15 |
-------------------------------------------------------------------------------- /docs/tmpl/mobile/home.tmpl: -------------------------------------------------------------------------------- 1 |
2 | 3 |

XFramework Docs

4 |
5 | 6 |
7 |
8 | 9 |
10 |
11 | 22 |
23 |
24 | 25 |
26 |
27 | 28 | -------------------------------------------------------------------------------- /docs/tmpl/mobile/menu.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/tmpl/tablet/header.tmpl: -------------------------------------------------------------------------------- 1 |
2 | <% _.each(get('buttons'), function(button) { %> 3 | data-<%=prop%>="<%=value%>" <% }); %> <%= button.dataHrefString %> <%= button.hasTooltip ? 'title="' + button.tooltip + '"' : '' %> id="<%= button.id %>"> 4 | <% if(button.hasText) { %> 5 | <%= button.text %> 6 | <% } %> 7 | <% if(button.hasIcon) { %> 8 | 9 | <% } %> 10 | 11 | <% }); %> 12 | <% if(get('hasTitle')) { %> 13 | <<%= get('headerElement') %> class="xf-header-title <%= get('titleClass') %>"><%= get('title') %>> 14 | <% } %> 15 |
-------------------------------------------------------------------------------- /docs/tmpl/tablet/home.tmpl: -------------------------------------------------------------------------------- 1 |
2 |

XFramework Docs

3 |
4 |
5 |
6 |
7 |
8 |
9 | 10 |
11 | 12 | -------------------------------------------------------------------------------- /docs/tmpl/tablet/menu.tmpl: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/updating_xf_and_dependencies.html: -------------------------------------------------------------------------------- 1 |

Updating XF and dependencies

2 | 3 |

XF Generator allows you to update sources and dependencies:

4 | 5 | -------------------------------------------------------------------------------- /docs/view.html: -------------------------------------------------------------------------------- 1 |

View

2 | 3 |

XF.View is an extended version of Backbone.View to make it perfectly fit XF Component based architecture.

4 | 5 |

To create a standalone view XF Generator can be used with yo xf:view [name].

6 | 7 |

XF.View properties:

8 | 9 | 14 | 15 |

XF.View methods:

16 | 17 | 23 | 24 |

XF.View hooks:

25 | 26 | 36 | 37 |

Note that data from collection or model of the component is available in the template via localized variable data.

38 | 39 |

For other methods, properties and hooks available see Backbone.View.

-------------------------------------------------------------------------------- /docs/what_is_xframework_.html: -------------------------------------------------------------------------------- 1 |

What is XFramework?

2 | 3 |

Version 0.9.1

4 | 5 |

XFramework (next XF) is a small yet powerful Javascript framework for quick development of cross-device web applications. Honestly saying, XF is a high-level framework based on Backbone.js that implements its own architecture paradigm, based on MV* on the component level.

6 | 7 |

XFramework makes it easy to reuse the application logic and provide various layouts or widgets for different devices based on the criteria that you define.

8 | 9 |

XFramework is designed to be extremely modular, flexible, fast, and easy to use. To develop an app in X-Framework a developer should be familiar with common web technologies such as HTML/CSS/JS, LESS for editing styles, Handlebars-style templating and have an understanding of how MV* architecture works. Experience using Backbone.js, Angular.js, Ember.js, jQuery Mobile or other framework will be helpful.

10 | 11 |

TODO: update!
XFramework currently features:

12 | 13 | -------------------------------------------------------------------------------- /docs/xf_app.html: -------------------------------------------------------------------------------- 1 |

XF.App

2 | 3 |

XF.App is a 'class' that you able to extend with your own methods and properties needed in the application. In this case an instance of this class is something like a main controller of the whole app.

4 | 5 |
// if the app boilerplate was created via XF Generator
 6 | // these lines can be found in `app.js` file
 7 | var MyApp = XF.App.extend({
 8 |     initialize: function () {
 9 |         // this code will be executed before XF will be started
10 |             // but you can put the preparation code here
11 |             // …
12 |             this.myAwesomeMethod();
13 |     },
14 |         myAwesomeMethod: function () {
15 | 
16 | 
    }
17 | 
18 | 19 | }); 20 |
-------------------------------------------------------------------------------- /docs/xf_device.html: -------------------------------------------------------------------------------- 1 |

XF.device

2 | 3 |

XF.device contains the information about current user device app was launched:

4 | 5 | 12 | 13 |
var app = new MyApp({
14 |         // …
15 |         // other settings for the application
16 |     device: {
17 |                 types : [{
18 |             name : 'tablet',
19 |             range : {
20 |                 max : 1024,
21 |                 min : 569
22 |             },
23 |             templatePath : 'tablet/' // template path for tablet devices (by default it will be tmpl/tablet/componentName.tmpl)
24 |         }, {
25 |             name : 'phone',
26 |             range : {
27 |                 max : 568,
28 |                 min : null
29 |             },
30 |             templatePath : 'phone/' // path to templates for phones (by default it     will be tmpl/phone/componentName.tmpl)
31 |         }]
32 |     }
33 | });
34 | 
-------------------------------------------------------------------------------- /docs/xf_router.html: -------------------------------------------------------------------------------- 1 |

XF.Router

2 | 3 |

XF.Router is an extended [Backbone.Router]. XF cares about creation of router instance, its starting, binding handlers and so on. Everything you just need to do is to pass your routes and handlers with starting options for the application:

4 | 5 |
    // if the app boilerplate was created via XF Generator
 6 |     // these lines cab be found in `index.js` file
 7 |     var app = new MyApp({
 8 |         // …
 9 |         // other settings for the application
10 |     router: {
11 |         routes: {
12 |             '':                                         'home',
13 |             'search/:q':                            'searchByQuery',
14 |             'item:id':                              'showItemById',
15 |                         'books/:cat(/:subcat)': 'showBooksCategory',
16 |                         'news/*any':                            'showNews'
17 |         },
18 | 
19 | 
    home: function () {
20 | 
21 |     },
22 | 
23 |     searchByQuery: function (query) {
24 | 
25 |     },
26 | 
27 |     showItemById: function (id) {
28 | 
29 |     },
30 | 
31 |             showBooksCategory: function (cat, subcat) {
32 | 
33 |             },
34 | 
35 |             showNews: function (param) {
36 | 
37 |             }
38 | }
39 | 
40 | 41 | }); 42 |
43 | 44 |

In the example above the handler home for empty route was created. In case you want to define the starting route for the application or turn off HTML5 pushState (using pushState support is turned on by default) you should pass the necessary starting parameters to XF.history which actually is a link to Backbone.history.

45 | 46 |
var app = new MyApp({
47 |         // …
48 |         // other settings for the application
49 |     history: {
50 |                 pushState: false,
51 |                 root: 'books/fiction'
52 |     }
53 | });
54 | 
55 | 56 |

To force navigation to another url fragment a number of ways is available:

57 | 58 | 63 | 64 |

All elements with the attribute data-href or href will work on changing url fragment.

65 | 66 |
<a data-href="books/fiction">Books</a>
67 | 
-------------------------------------------------------------------------------- /docs/xf_settings.html: -------------------------------------------------------------------------------- 1 |

XF.settings

2 | 3 |

This simple object contains the settings for the application, that could be overridden on the start:

4 | 5 | 16 | 17 |
var app = new MyApp({
18 |         // …
19 |         // other settings for the application
20 |         settings: {
21 |                 appVersion: '2.0.1',
22 | 
23 | 
    dataUrlPrefix: 'http://api.example.com/',
24 | 
25 |     ajaxSettings: {
26 |         // settings that are provided to collections and models to fetch and sync the data
27 |                 // see $.ajax options
28 |                 crossDomain: true
29 |     }
30 | }
31 | 
32 | 33 | }); 34 |
-------------------------------------------------------------------------------- /docs/xf_source_modules.html: -------------------------------------------------------------------------------- 1 |

XF source modules

2 | 3 |

XFramework has its own building blocks that drive it on. Some blocks are mandatory to include in the build of XFramework, other ones are not required.

4 | 5 |

Mandatory XF src modules are:

6 | 7 | 19 | 20 |

Optional XF src modules are:

21 | 22 | -------------------------------------------------------------------------------- /docs/xf_storage.html: -------------------------------------------------------------------------------- 1 |

XF.storage

2 | 3 |

XF.storage is just a wrapper for localStorage that allows you easily to interact with it: set, get, clear.

4 | 5 |
XF.storage.set('booksCategory', 12);
 6 | 
 7 | XF.storage.get('booksCategory');
 8 | 
 9 | XF.storage.clear();
10 | 
-------------------------------------------------------------------------------- /docs/xf_touch.html: -------------------------------------------------------------------------------- 1 |

XF.touch

2 | 3 |

XF.touch makes the life in such a multidevice world easier — it is an adapter for all types of user contexts: touch screens, mouse, pointers.

4 | 5 |

For now it contains the following user interaction events:

6 | 7 | -------------------------------------------------------------------------------- /docs/xf_utils.html: -------------------------------------------------------------------------------- 1 |

XF.utils

2 | 3 |

This source module will contain all the helpers will needed to make the work with XF much more easier. Right now it contains only address bar hiding helper for iOS and Android mobile phones.

-------------------------------------------------------------------------------- /docs/xf_zepto_support_js.html: -------------------------------------------------------------------------------- 1 |

XF.zepto.support.js

2 | 3 |

This piece enables the support of zepto.js instead of jQuery extending it with all missing methods and properties.

4 | 5 |

Attention! Include this module on your own fear and risk. It is in experimental status right away.

-------------------------------------------------------------------------------- /docs/your_first_xf_web_app.html: -------------------------------------------------------------------------------- 1 |

Your first XF web app

2 | 3 |

To create the first XF application the simplest way is to use XF Generator through yo xf:application init [appName].

4 | 5 |

For now it scaffolds an app in the way you can see by example: XF Hello World App.

-------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "X-Framework", 3 | "version": "0.9.1", 4 | "devDependencies": { 5 | "grunt": "latest", 6 | "grunt-contrib-jshint": "latest", 7 | "grunt-contrib-nodeunit": "latest", 8 | "grunt-contrib-uglify": "latest", 9 | "grunt-contrib-concat": "latest", 10 | "grunt-contrib-less": "latest", 11 | "grunt-contrib-qunit": "latest", 12 | "grunt-recess": "latest", 13 | "grunt-processhtml": "latest", 14 | "underscore.string": "latest", 15 | "requirejs": "latest", 16 | "grunt-bower-task": "latest", 17 | "load-grunt-tasks": "latest" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git://github.com/epam/xframework.git" 22 | }, 23 | "scripts": { 24 | "test": "grunt test" 25 | }, 26 | "description": "ERROR: No README data found!", 27 | "main": "Gruntfile.js", 28 | "dependencies": { 29 | "grunt-cli": "latest", 30 | "mocha": "latest", 31 | "yeoman-generator": "latest" 32 | }, 33 | "peerDependencies": { 34 | "yo": ">=1.0.0-rc.1.1" 35 | }, 36 | "author": "EPAM", 37 | "license": "BSD" 38 | } -------------------------------------------------------------------------------- /styles/fonts/entypo-social.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epam/xframework/6b2a1b954dc998274922feaf3a363a47258c7bd5/styles/fonts/entypo-social.eot -------------------------------------------------------------------------------- /styles/fonts/entypo-social.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /styles/fonts/entypo-social.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epam/xframework/6b2a1b954dc998274922feaf3a363a47258c7bd5/styles/fonts/entypo-social.ttf -------------------------------------------------------------------------------- /styles/fonts/entypo-social.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epam/xframework/6b2a1b954dc998274922feaf3a363a47258c7bd5/styles/fonts/entypo-social.woff -------------------------------------------------------------------------------- /styles/fonts/entypo.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epam/xframework/6b2a1b954dc998274922feaf3a363a47258c7bd5/styles/fonts/entypo.eot -------------------------------------------------------------------------------- /styles/fonts/entypo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /styles/fonts/entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epam/xframework/6b2a1b954dc998274922feaf3a363a47258c7bd5/styles/fonts/entypo.ttf -------------------------------------------------------------------------------- /styles/fonts/entypo.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/epam/xframework/6b2a1b954dc998274922feaf3a363a47258c7bd5/styles/fonts/entypo.woff -------------------------------------------------------------------------------- /styles/xf.dialog.less: -------------------------------------------------------------------------------- 1 | /* Dialogs 2 | =========================================== */ 3 | 4 | .xf-dialog { 5 | position: fixed; 6 | display: block; 7 | z-index: 1000; 8 | width: 100%; 9 | height: 100%; 10 | top: 0; 11 | left: 0; 12 | text-align: center; 13 | } 14 | .xf-dialog:before { 15 | height: 100%; 16 | content: ""; 17 | display: inline-block; 18 | vertical-align: middle; 19 | margin-left: -4px; 20 | } 21 | .xf-dialog-content { 22 | text-align: left; 23 | display: inline-block; 24 | vertical-align: middle; 25 | max-height: 95%; 26 | max-width: 95%; 27 | } 28 | .xf-dialog-box { 29 | padding: 16px; 30 | margin: 16px; 31 | //.xf-corner-all; 32 | } 33 | .xf-dialog { 34 | background: @dialog-dim-color; 35 | } 36 | .xf-dialog-box { 37 | //border: 2px solid @dialog-box-border-color; 38 | .bg-gradient(@dialog-box-bg-color-start, @dialog-box-bg-color-start, @dialog-box-bg-color-end); 39 | text-shadow: 0 1px 0 @dialog-box-text-shadow-color; 40 | color: @dialog-box-text-color; 41 | } 42 | .xf-dialog-box-header { 43 | h1, h2, h3, h4, h5, h6 { 44 | margin-top: 0; 45 | margin-bottom: 18px; 46 | } 47 | } 48 | .xf-dialog-box-content { 49 | min-height: 2em; 50 | margin: 1em 0; 51 | } 52 | .xf-dialog-box-footer { 53 | .xf-grid-unit { 54 | padding-left: 8px; 55 | padding-right: 8px; 56 | 57 | &:first-child { 58 | padding-left:0; 59 | } 60 | &:last-child { 61 | padding-right:0; 62 | } 63 | } 64 | } 65 | 66 | /* Loader dialog */ 67 | 68 | .xf-dialog-notification { 69 | background: none; 70 | } 71 | .xf-notification { 72 | display: table; 73 | vertical-align: middle; 74 | text-align: center; 75 | height: 120px; 76 | width: 120px; 77 | .xf-corner-all; 78 | } 79 | .xf-notification { 80 | background: @notification-bg-color; 81 | color: @notification-text-color; 82 | //text-shadow: 0 1px 0 @notification-text-shadow-color; 83 | } 84 | .xf-notification-wrap { 85 | display: table-cell; 86 | vertical-align: middle; 87 | height: 110px; 88 | width: 110px; 89 | padding: 10px 90 | } 91 | 92 | .xf-notification-text { 93 | margin-top: 2px; 94 | font-weight: bold; 95 | } -------------------------------------------------------------------------------- /styles/xf.footer.less: -------------------------------------------------------------------------------- 1 | 2 | /* Footer Navigation 3 | =========================================================== */ 4 | 5 | .xf-footer { 6 | position: relative; 7 | z-index: 20; 8 | } 9 | .xf-footer-fixed { 10 | position: fixed; 11 | left: 0; 12 | right: 0; 13 | bottom: 0; 14 | } 15 | .xf-nav { 16 | position: relative; 17 | overflow: hidden; 18 | margin: 0; 19 | padding: 0; 20 | 21 | border-top:1px solid @nav-bg-color; 22 | background: @nav-bg-color; 23 | color: @nav-item-text-color; 24 | } 25 | .xf-nav-item { 26 | display: block; 27 | position:relative; 28 | .box-sizing-border-box; 29 | height: 48px; 30 | padding: 4px; 31 | text-align: center; 32 | text-decoration: none; 33 | 34 | color: @nav-item-text-color; 35 | background: -moz-linear-gradient(top, @nav-item-bg-color-start 0%, @nav-item-bg-color-end 50%); 36 | background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, @nav-item-bg-color-start), color-stop(50%,@nav-item-bg-color-end)); 37 | background: -webkit-linear-gradient(top, @nav-item-bg-color-start 0%, @nav-item-bg-color-end 50%); 38 | background: -o-linear-gradient(top, @nav-item-bg-color-start 0%, @nav-item-bg-color-end 50%); 39 | background: -ms-linear-gradient(top, @nav-item-bg-color-start 0%, @nav-item-bg-color-end 50%); 40 | background: linear-gradient(top, @nav-item-bg-color-start 0%, @nav-item-bg-color-end 50%); 41 | 42 | border-top: 1px solid @nav-item-border-color; 43 | } 44 | .xf-nav-item.xf-iconpos-top { 45 | padding: 32px 4px 2px 4px; 46 | } 47 | .xf-nav-item-text { 48 | .overflow-ellipsis; 49 | font-size: 10px; 50 | } 51 | .xf-nav-item .xf-icon { 52 | position: absolute; 53 | 54 | color: @nav-item-icon-color; 55 | } 56 | .xf-nav-item.xf-iconpos-top .xf-icon { 57 | top: 2px; 58 | left:-15px; 59 | margin-left: 50%; 60 | } 61 | .xf-has-footer { 62 | margin-bottom: 50px; 63 | } 64 | .xf-nav-item-active { 65 | //.gradient(@nav-item-active-bg-color-start, @nav-item-active-bg-color-end); 66 | border-color: @nav-item-active-border-color; 67 | color: @nav-item-active-text-color; 68 | } 69 | .xf-nav-item-active .xf-icon { 70 | color: @nav-item-active-icon-color; 71 | } 72 | .xf-nav-item:hover { 73 | background-repeat: no-repeat; 74 | .background-size(100% 130%); 75 | } 76 | -------------------------------------------------------------------------------- /styles/xf.header.less: -------------------------------------------------------------------------------- 1 | 2 | /* Header 3 | ========================================== */ 4 | 5 | .xf-header { 6 | display: block; 7 | position: relative; 8 | min-height: 43px; 9 | z-index: 20; /* z-index > 0 causes the rotated pseudo-el inside .xf-button-back to not be rotated on android*/ 10 | text-align: center; 11 | border-bottom: 1px solid transparent; 12 | background: @header-bg-color-end; 13 | .bg-gradient(@header-bg-color-start, @header-bg-color-start, @header-bg-color-end); 14 | border-bottom: 1px solid @header-border-color; 15 | } 16 | .xf-header-title { 17 | margin: 0 8px; 18 | padding: 0; 19 | font: bold 18px sans-serif; 20 | line-height: 43px; 21 | vertical-align: middle; 22 | .overflow-ellipsis; 23 | 24 | color: @header-text-color; 25 | text-shadow: 0 1px 0 @header-text-shadow-color; 26 | } 27 | .xf-button-header-left + .xf-header-title, 28 | .xf-button-header-right + .xf-header-title { 29 | margin-left: 80px; 30 | margin-right: 80px; 31 | } 32 | 33 | 34 | .xf-button-header-left, 35 | .xf-button-header-right { 36 | position: absolute; 37 | top: 0; 38 | height: 44px; 39 | line-height: 43px; 40 | min-width: 44px; 41 | background: none; 42 | max-width: 66px; 43 | } 44 | .xf-button-header-left { 45 | left: 0; 46 | } 47 | .xf-button-header-right { 48 | right: 0; 49 | } 50 | 51 | .xf-header-fixed { 52 | position: fixed; 53 | left: 0; 54 | right: 0; 55 | top: 0; 56 | } 57 | 58 | .xf-has-header { 59 | margin-top: 44px; 60 | } -------------------------------------------------------------------------------- /styles/xf.layout.less: -------------------------------------------------------------------------------- 1 | 2 | /* Layout 3 | ========================================== */ 4 | 5 | .xf-grid-unit-1of1 { width: 100%;float:none; display: block;} 6 | .xf-grid-unit-1of2, 7 | .xf-grid-unit-2of4, 8 | .xf-grid-unit-3of6 { width:50%;} 9 | .xf-grid-unit-1of3, 10 | .xf-grid-unit-2of6 { width:33.333333%;} 11 | .xf-grid-unit-2of3, 12 | .xf-grid-unit-4of6 { width:66.666666%;} 13 | .xf-grid-unit-1of4 { width:25%;} 14 | .xf-grid-unit-3of4 { width:75%;} 15 | .xf-grid-unit-1of5 { width:20%;} 16 | .xf-grid-unit-2of5 { width:40%;} 17 | .xf-grid-unit-3of5 { width:60%;} 18 | .xf-grid-unit-4of5 { width:80%;} 19 | .xf-grid-unit-1of6 { width:16.66666666%;} 20 | .xf-grid-unit-5of6 { width:83.33333333%;} 21 | 22 | .xf-grid-unit { 23 | float: left; 24 | margin: 0; 25 | padding: 0; 26 | list-style: none; 27 | .box-sizing-border-box; 28 | } 29 | .xf-indented { 30 | padding-top: 8px; 31 | padding-left: 8px; 32 | padding-right: 8px; 33 | } 34 | .xf-grid-padded { 35 | 36 | >.xf-grid-unit{ 37 | padding-left: 8px; 38 | padding-right: 8px; 39 | } 40 | 41 | >.xf-grid-unit:first-child { 42 | padding-left: 0; 43 | } 44 | 45 | >.xf-grid-unit:last-child { 46 | padding-right: 0; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /styles/xf.less: -------------------------------------------------------------------------------- 1 | 2 | /* X-Framework styles 3 | Author: Pavel_Shut@epam.com 4 | ============================== */ 5 | 6 | 7 | @import 'xf.reset.less'; 8 | @import 'xf.type.less'; 9 | 10 | @import 'xf.variables.less'; // Modify this for custom colors, font-sizes, etc 11 | 12 | 13 | @import "xf.mixins.less"; 14 | @import 'lesshat.less'; 15 | 16 | @import 'xf.pages.less'; 17 | @import 'xf.animations.less'; 18 | @import 'xf.layout.less'; 19 | 20 | @import 'xf.icons.less'; 21 | @import 'xf.forms.less'; 22 | @import 'xf.buttons.less'; 23 | 24 | @import 'xf.header.less'; 25 | @import 'xf.footer.less'; 26 | @import 'xf.tabs.less'; 27 | @import 'xf.list.less'; 28 | @import 'xf.dialog.less'; 29 | @import 'xf.loader.less'; 30 | 31 | //@import 'your.stylesheet.less'; // Import your styles before utilities 32 | 33 | @import 'xf.utilities.less'; 34 | -------------------------------------------------------------------------------- /styles/xf.loader.less: -------------------------------------------------------------------------------- 1 | /* Loader 2 | =========================================== */ 3 | .xf-loader { 4 | position: fixed; 5 | display: block; 6 | z-index: 1000; 7 | width: 100%; 8 | height: 100%; 9 | top: 0; 10 | left: 0; 11 | text-align: center; 12 | background: @dialog-dim-color; 13 | } 14 | .xf-loader:before { 15 | height: 100%; 16 | content:""; 17 | display: inline-block; 18 | vertical-align: middle; 19 | margin-left: -4px; 20 | } 21 | .xf-loader-content { 22 | background: @notification-bg-color; 23 | width: 110px; 24 | height: 110px; 25 | padding: 10px; 26 | border-radius: 5px; 27 | -webkit-border-radius: 5px; 28 | -moz-border-radius: 5px; 29 | -o-border-radius: 5px; 30 | position: absolute; 31 | top: 50%; 32 | left: 50%; 33 | margin-left: -65px; 34 | margin-top: -65px; 35 | } 36 | .loading { 37 | width: 3em; 38 | height: 3em; 39 | margin: 32px; 40 | -moz-animation: rotatex 0.6s linear 0s infinite; 41 | -ms-animation: rotatex 0.6s linear 0s infinite; 42 | -o-animation: rotatex 0.6s linear 0s infinite; 43 | -webkit-animation: rotatex 0.6s linear 0s infinite; 44 | animation: rotatex 0.6s linear 0s infinite; 45 | -moz-box-shadow: inset 0.15em 0 0 rgba(255, 0, 0, 0.5), inset 0 0.15em 0 rgba(252, 150, 0, 0.5), inset -0.15em 0 0 rgba(0, 255, 0, 0.5), inset 0 -0.15em 0 rgba(0, 150, 255, 0.5); 46 | -o-box-shadow: inset 0.15em 0 0 rgba(255, 0, 0, 0.5), inset 0 0.15em 0 rgba(252, 150, 0, 0.5), inset -0.15em 0 0 rgba(0, 255, 0, 0.5), inset 0 -0.15em 0 rgba(0, 150, 255, 0.5); 47 | -webkit-box-shadow: inset 0.15em 0 1px rgba(255, 0, 0, 0.5), inset 0 0.15em 1px rgba(252, 150, 0, 0.5), inset -0.15em 0 1px rgba(0, 255, 0, 0.5), inset 0 -0.15em 1px rgba(0, 150, 255, 0.5); 48 | box-shadow: inset 0.15em 0 0 rgba(255, 0, 0, 0.5), inset 0 0.15em 0 rgba(252, 150, 0, 0.5), inset -0.15em 0 0 rgba(0, 255, 0, 0.5), inset 0 -0.15em 0 rgba(0, 150, 255, 0.5); 49 | -o-transition: 1s linear rotate(3600deg); 50 | } 51 | .loading, .loading:before, .loading:after { 52 | border-radius: 10em; 53 | -webkit-border-radius: 10em; 54 | -moz-border-radius: 10em; 55 | -o-border-radius: 10em; 56 | } 57 | @-moz-keyframes rotatex { 58 | 0% { 59 | -moz-transform: rotate(0deg); 60 | } 61 | 100% { 62 | -moz-transform: rotate(360deg); 63 | } 64 | } 65 | @-o-keyframes rotatex { 66 | 0% { 67 | -o-transform: rotate(0deg); 68 | } 69 | 100% { 70 | -o-transform: rotate(360deg); 71 | } 72 | } 73 | @-ms-keyframes rotatex { 74 | 0% { 75 | -ms-transform: rotate(0deg); 76 | } 77 | 100% { 78 | -ms-transform: rotate(360deg); 79 | } 80 | } 81 | @-webkit-keyframes rotatex { 82 | 0% { 83 | -webkit-transform: rotate(0deg); 84 | } 85 | 100% { 86 | -webkit-transform: rotate(360deg); 87 | } 88 | } 89 | @keyframes rotatex { 90 | 0% { 91 | transform: rotate(0deg); 92 | } 93 | 100% { 94 | transform: rotate(360deg); 95 | } 96 | } -------------------------------------------------------------------------------- /styles/xf.pages.less: -------------------------------------------------------------------------------- 1 | html, body, .xf-viewport { 2 | height: 100%; 3 | width: 100%; 4 | background: @app-bg; 5 | color: @text-color; 6 | } 7 | 8 | .viewport, 9 | .xf-viewport { 10 | position: relative; 11 | margin: 0; 12 | overflow-x: visible; 13 | -webkit-text-size-adjust: none; 14 | -ms-text-size-adjust: none; 15 | -webkit-tap-highlight-color: rgba(0, 0, 0, 0); 16 | -webkit-touch-callout: none; 17 | } 18 | /* .viewport is body, or an element wrapping all content inside it, e.g. form 19 | https://github.com/jquery/jquery-mobile/issues/2066 */ 20 | body.viewport, 21 | div.viewport, 22 | body.xf-viewport, 23 | div.xf-viewport { 24 | overflow-x: hidden; 25 | } 26 | 27 | .xf-page { 28 | position: absolute; 29 | width: 100%; 30 | min-height: 100%; 31 | left: 0; 32 | top: 0; 33 | border: 0; 34 | display: none; 35 | outline: none; /* on ios4, setting focus on the page element causes flashing during transitions when there is an outline */ 36 | visibility: visible; 37 | z-index: 1; 38 | 39 | background: @page-bg; 40 | } 41 | .xf-page-active { 42 | display: block; 43 | overflow: visible; 44 | } 45 | 46 | .xf-page-content { 47 | overflow: hidden; 48 | position: relative; 49 | z-index: 10; 50 | } -------------------------------------------------------------------------------- /styles/xf.tabs.less: -------------------------------------------------------------------------------- 1 | 2 | /* Tabs 3 | ========================================== */ 4 | 5 | .xf-tabs { 6 | display: block; 7 | position: relative; 8 | margin: 5px 0 3px; /* 5&3 to we compensate for the shift when buttons have borders and are moved 1px up and left */ 9 | padding: 0 0 0 1px; 10 | .box-sizing-border-box; 11 | list-style: none; 12 | 13 | margin-top: 5px; /* */ 14 | margin-bottom: 3px; 15 | .clearfix(); 16 | 17 | } 18 | .xf-tabs-button { 19 | display: block; 20 | position: relative; 21 | .box-sizing-border-box; 22 | height: 44px; 23 | padding: 10px; 24 | //margin: -1px 0 0 -1px; /* needed to hide adjacent borders */ 25 | text-decoration: none; 26 | text-align: center; 27 | cursor: pointer; 28 | color: @button-text-color; 29 | text-shadow: @tabs-text-shadow; 30 | .bg-gradient(@tabs-active-bg-color-start, @tabs-active-bg-color-start, @tabs-active-bg-color-end); 31 | 32 | //border-left: 1px solid white; 33 | //border-right: 1px solid white; 34 | border-bottom: 5px solid @tabs-border-color; 35 | 36 | &:hover { 37 | -webkit-background-size: 100% 150%; 38 | background-size: 100% 150%; 39 | } 40 | } 41 | .xf-grid-unit:first-child > .xf-tabs-button { 42 | border-left: none; 43 | } 44 | .xf-grid-unit-1of2 ~ .xf-grid-unit:last-child > .xf-tabs-button, 45 | .xf-grid-unit-1of3 ~ .xf-grid-unit:last-child > .xf-tabs-button, 46 | .xf-grid-unit-1of4 ~ .xf-grid-unit:last-child > .xf-tabs-button { 47 | border-right: none; 48 | margin-right: 0px; 49 | } 50 | 51 | .xf-tabs-button-text { 52 | display: block; 53 | height: 24px; 54 | line-height: 24px; 55 | vertical-align: middle; 56 | .overflow-ellipsis; 57 | color: @tabs-text-color; 58 | } 59 | 60 | .xf-tabs-button-active { 61 | z-index: 1; 62 | .bg-gradient(@tabs-active-bg-color-start, @tabs-active-bg-color-start, @tabs-active-bg-color-end); 63 | color: @tabs-active-text-color; 64 | text-shadow: @tabs-text-shadow; 65 | font-weight: bold; 66 | border-bottom: 5px solid @tabs-active-border-color; 67 | 68 | &:hover { 69 | -webkit-background-size: auto; 70 | background-size: auto; 71 | cursor: default; 72 | border-bottom: 5px solid @tabs-active-border-color; 73 | } 74 | } 75 | .xf-tabs-button-active .xf-tabs-button-text { 76 | } -------------------------------------------------------------------------------- /styles/xf.theme.dark.less: -------------------------------------------------------------------------------- 1 | /* Overrides specific for Dark theme 2 | ===================================== */ 3 | 4 | .xf-theme-dark { 5 | } 6 | 7 | .xf-header { 8 | .border-box; 9 | border: 1px solid #212325; 10 | .box-shadow(~"inset 0 1px 0 rgba(255,255,255,.2), 0 1px 1px rgba(0,0,0,.55)"); 11 | } -------------------------------------------------------------------------------- /styles/xf.utilities.less: -------------------------------------------------------------------------------- 1 | 2 | /* non-semantic helper classes 3 | ========================================================================== */ 4 | 5 | /* prevent callout */ 6 | .nocallout {-webkit-touch-callout: none;} 7 | 8 | /* A workaround for S60 3.x and 5.0 devices which do not animated gif images if they have been set as display: none */ 9 | .gifhidden {position: absolute; left: -100%;} 10 | 11 | /* For image replacement */ 12 | .ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; } 13 | .ir br { display: none; } 14 | 15 | /* Hide from both screenreaders and browsers: h5bp.com/u */ 16 | .hidden { display: none !important; visibility: hidden; } 17 | 18 | /* Hide only visually, but have it available for screenreaders: h5bp.com/v */ 19 | .visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; } 20 | 21 | /* Extends the .visuallyhidden class to allow the element to be focusable when navigated to via the keyboard: h5bp.com/p */ 22 | .visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto; } 23 | 24 | /* Hide visually and from screenreaders, but maintain layout */ 25 | .invisible { visibility: hidden; } 26 | 27 | /* Contain floats: h5bp.com/q */ 28 | .clearfix:before, .clearfix:after { content: ""; display: table; } 29 | .clearfix:after { clear: both; } 30 | * + html .clearfix { *zoom: 1; } 31 | 32 | -------------------------------------------------------------------------------- /styles/xf.variables.theme.dark.less: -------------------------------------------------------------------------------- 1 | /* ================================================= 2 | Dark theme 3 | ==================================================== */ 4 | 5 | 6 | @app-bg: #404040; 7 | @page-bg: @app-bg; 8 | @text-color: #fff; 9 | @general-border-color: #3C3C3C;//#b2b2b2; 10 | 11 | 12 | // Inputs 13 | @input-bg-color: #fff; 14 | @input-active-bg-color: #669cf1; 15 | @input-inactive-bg-color: #e6e6e6; 16 | @input-text-color: #294d8c; 17 | @input-label-color: #333; 18 | @input-placeholder-color: #c8c8cc; 19 | 20 | 21 | // Buttons 22 | @button-bg-color-start: #fff; 23 | @button-bg-color-end: #bfbfbd; 24 | @button-border-color: rgba(0,0,0,.6); 25 | @button-text-color: #555; 26 | @button-text-shadow-color: rgba(255,255,255,.7);// tbd 27 | 28 | @button-active-bg-color-start: #6ea5fa; 29 | @button-active-bg-color-end: #2e5fb2; 30 | @button-active-border-color: #315eab; 31 | @button-active-text-color: #fff; 32 | @button-active-text-shadow-color: rgba(0,0,0,.7); 33 | 34 | @button-alert-bg-color-start: #fa6e6e; 35 | @button-alert-bg-color-end: #bc0101; 36 | //@button-alert-border-color: @button-border-color; 37 | @button-alert-text-color: #fff; 38 | @button-alert-text-shadow-color: rgba(0,0,0,.7); 39 | 40 | 41 | // Header 42 | @header-bg-color-start: #717a80; 43 | @header-bg-color-end: #383d40; 44 | @header-border-color: #212325; 45 | @header-text-color: #e3e3e3; 46 | @header-text-shadow-color: rgba(0,0,0,.7); 47 | 48 | 49 | /* Footer nav */ 50 | @nav-bg-color: #fff; 51 | @nav-item-bg-color-start: #fff; 52 | @nav-item-bg-color-end: #bfbfbd; 53 | @nav-item-border-color: #d9d9d9; 54 | @nav-item-text-color: #555; 55 | @nav-item-icon-color: #666; 56 | 57 | @nav-item-active-text-color: #262626; 58 | @nav-item-active-icon-color: #333; 59 | @nav-item-active-bg-color-start: rgba(0,0,0,0); 60 | @nav-item-active-bg-color-end: rgba(0,0,0,0); 61 | @nav-item-active-border-color: @nav-item-border-color; 62 | 63 | 64 | // Lists 65 | @list-bg-color: #fff; 66 | @list-icon-color: #969699; 67 | @list-text-color: @input-label-color; 68 | @count-bubble-bg-color: @list-icon-color; 69 | @count-bubble-text-color: @list-bg-color; 70 | 71 | 72 | /* Dialogs */ 73 | @dialog-dim-color: rgba(255,255,255,.4); 74 | @dialog-box-border-color: rgba(0,0,0,0); 75 | @dialog-box-bg-color-start: rgba(0,0,0,.7); 76 | @dialog-box-bg-color-end: rgba(0,0,0,.7); 77 | @dialog-box-text-color: #e3e3e3; 78 | @dialog-box-text-shadow-color: #000; 79 | 80 | @notification-bg-color: rgba(0,0,0,.7); 81 | @notification-text-color: #e3e3e3; 82 | @notification-text-shadow-color: #000; 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /test/components/test.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | XF.defineComponent( 3 | 'test', 4 | XF.Component.extend({ 5 | 6 | View : XF.View.extend({ 7 | useCache : false, 8 | afterRender : function () { 9 | $('#testcomponent').on('tap', function () { 10 | tap = true; 11 | }).on('swipe', function () { 12 | swipe = true; 13 | }).on('swipeLeft', function () { 14 | swipeLeft = true; 15 | }).on('swipeRight', function () { 16 | swipeRight = true; 17 | }).on('swipeUp', function () { 18 | swipeUp = true; 19 | }).on('swipeDown', function () { 20 | swipeDown = true; 21 | }); 22 | 23 | $('button').on('tap', function() { 24 | alert(49); 25 | }); 26 | } 27 | }), 28 | 29 | Model : null, 30 | Collection: XF.Collection.extend({ 31 | url : 'test.json' 32 | }) 33 | 34 | }) 35 | ); 36 | }); -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | XF Tests 7 | 8 | 9 | 10 | 15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 | 23 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /test/lib/run-qunit.js: -------------------------------------------------------------------------------- 1 | function waitFor(testFx, onReady, timeOutMillis) { 2 | var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 30001, //< Default Max Timout is 3s 3 | start = new Date().getTime(), 4 | condition = false, 5 | interval = setInterval(function() { 6 | if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { 7 | // If not time-out yet and condition not yet fulfilled 8 | condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code 9 | } else { 10 | if(!condition) { 11 | // If condition still not fulfilled (timeout but condition is 'false') 12 | console.log("'waitFor()' timeout"); 13 | phantom.exit(1); 14 | } else { 15 | // Condition fulfilled (timeout and/or condition is 'true') 16 | console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); 17 | 18 | if (typeof(onReady) === "string") { 19 | eval(onReady); 20 | } else { 21 | onReady(); //< Do what it's supposed to do once the condition is fulfilled 22 | } 23 | 24 | clearInterval(interval); //< Stop this interval 25 | } 26 | } 27 | }, 100); //< repeat check every 250ms 28 | } 29 | 30 | 31 | if (phantom.args.length === 0 || phantom.args.length > 2) { 32 | console.log('Usage: run-qunit.js URL'); 33 | phantom.exit(1); 34 | } 35 | 36 | var page = require('webpage').create(); 37 | 38 | // Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") 39 | page.onConsoleMessage = function(msg) { 40 | console.log(msg); 41 | }; 42 | 43 | page.open(phantom.args[0], function(status){ 44 | if (status !== "success") { 45 | console.log("Unable to access network"); 46 | phantom.exit(1); 47 | } else { 48 | waitFor(function(){ 49 | return page.evaluate(function(){ 50 | var el = document.getElementById('qunit-testresult'); 51 | if (el && el.innerText.match('completed')) { 52 | return true; 53 | } 54 | return false; 55 | }); 56 | }, function(){ 57 | var failedNum = page.evaluate(function(){ 58 | var el = document.getElementById('qunit-testresult'); 59 | console.log(el.innerText); 60 | try { 61 | return el.getElementsByClassName('failed')[0].innerHTML; 62 | } catch (e) { } 63 | return 10000; 64 | }); 65 | phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0); 66 | }); 67 | } 68 | }); -------------------------------------------------------------------------------- /test/main.js: -------------------------------------------------------------------------------- 1 | require.config({ 2 | urlArgs: 'now=' + Date.now(), 3 | paths: { 4 | jquery: './../bower_modules/jquery/jquery', 5 | underscore: './../bower_modules/underscore/underscore', 6 | backbone: './../bower_modules/backbone/backbone', 7 | sinon: './../bower_modules/sinonjs/sinon', 8 | text: './../bower_modules/requirejs-text/text' 9 | }, 10 | shim: { 11 | 'jquery': { 12 | exports: 'jQuery' 13 | }, 14 | 'backbone': { 15 | //These script dependencies should be loaded before loading 16 | //backbone.js 17 | deps: ['underscore', 'jquery'], 18 | //Once loaded, use the global 'BB' as the 19 | //module value. 20 | exports: 'Backbone' 21 | }, 22 | 'underscore': { 23 | exports: '_' 24 | }, 25 | 'sinon': { 26 | exports: 'sinon' 27 | } 28 | } 29 | }); 30 | 31 | requirejs([ 32 | 'src/app/start', 33 | 'src/dom/dom' 34 | ], function( /* remember: the test modules don't export anything */ ) { 35 | 36 | // All the test files have been loaded, and all the tests have been 37 | // defined--we're ready to start testing! 38 | QUnit.start(); 39 | }); -------------------------------------------------------------------------------- /test/sample-feed.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sample Feed - Favorite RSS Related Software & Resources 5 | Take a look at some of FeedForAll's favorite software and resources for learning more about RSS. 6 | http://www.feedforall.com 7 | Computers/Software/Internet/Site Management/Content Management 8 | Copyright 2004 NotePage, Inc. 9 | http://blogs.law.harvard.edu/tech/rss 10 | en-us 11 | Mon, 1 Nov 2004 13:17:17 -0500 12 | marketing@feedforall.com 13 | Tue, 26 Oct 2004 14:06:44 -0500 14 | webmaster@feedforall.com 15 | FeedForAll Beta1 (0.0.1.8) 16 | 17 | http://www.feedforall.com/feedforall-temp.gif 18 | FeedForAll Sample Feed 19 | http://www.feedforall.com/industry-solutions.htm 20 | FeedForAll Sample Feed 21 | 144 22 | 117 23 | 24 | 25 | RSS Resources 26 | Be sure to take a look at some of our favorite RSS Resources<br> 27 | <a href="http://www.rss-specifications.com">RSS Specifications</a><br> 28 | <a href="http://www.blog-connection.com">Blog Connection</a><br> 29 | <br> 30 | http://www.feedforall.com 31 | Tue, 26 Oct 2004 14:01:01 -0500 32 | 33 | 34 | Recommended Desktop Feed Reader Software 35 | <b>FeedDemon</b> enables you to quickly read and gather information from hundreds of web sites - without having to visit them. Don't waste any more time checking your favorite web sites for updates. Instead, use FeedDemon and make them come to you. <br> 36 | More <a href="http://store.esellerate.net/a.asp?c=1_SKU5139890208_AFL403073819">FeedDemon Information</a> 37 | http://www.feedforall.com/feedforall-partners.htm 38 | Tue, 26 Oct 2004 14:03:25 -0500 39 | 40 | 41 | Recommended Web Based Feed Reader Software 42 | <b>FeedScout</b> enables you to view RSS/ATOM/RDF feeds from different sites directly in Internet Explorer. You can even set your Home Page to show favorite feeds. Feed Scout is a plug-in for Internet Explorer, so you won't have to learn anything except for how to press 2 new buttons on Internet Explorer toolbar. <br> 43 | More <a href="http://www.bytescout.com/feedscout.html">Information on FeedScout</a><br> 44 | <br> 45 | <br> 46 | <b>SurfPack</b> can feature search tools, horoscopes, current weather conditions, LiveJournal diaries, humor, web modules and other dynamically updated content. <br> 47 | More <a href="http://www.surfpack.com/">Information on SurfPack</a><br> 48 | http://www.feedforall.com/feedforall-partners.htm 49 | Tue, 26 Oct 2004 14:06:44 -0500 50 | 51 | 52 | -------------------------------------------------------------------------------- /test/src/app/start.js: -------------------------------------------------------------------------------- 1 | define([ 2 | '../../../xf/src/app/start', 3 | '../../../xf/src/xf.core', 4 | 'sinon' 5 | ], function(AppStart, XF, sinon) { 6 | 7 | var storageMock; 8 | var deviceMock; 9 | var touchMock; 10 | var uiMock; 11 | var routerExtend; 12 | var routerStartSpy; 13 | var pagesMock; 14 | var xfMock; 15 | 16 | module('AppStart', { 17 | setup: function() { 18 | delete XF.device.type.defaultAnimation; 19 | 20 | storageMock = sinon.mock(XF.storage); 21 | deviceMock = sinon.mock(XF.device); 22 | touchMock = sinon.mock(XF.touch); 23 | uiMock = sinon.mock(XF.ui); 24 | pagesMock = sinon.mock(XF.pages); 25 | xfMock = sinon.mock(XF); 26 | 27 | // Common expectations. 28 | storageMock.expects('init').withExactArgs().once(); 29 | touchMock.expects('init').withExactArgs().once(); 30 | uiMock.expects('init').withExactArgs().once(); 31 | xfMock.expects('loadChildComponents').once(); 32 | xfMock.expects('on').withArgs('xf:loadChildComponents'); 33 | xfMock.expects('trigger').withExactArgs('app:started').once(); 34 | 35 | // Mocks constructor of XF.router. 36 | XF.router = undefined; 37 | routerStartSpy = sinon.spy(); 38 | routerExtend = XF.Router.extends; 39 | XF.Router.extend = function() { 40 | return function() { 41 | // We need go deeper! 42 | return {start: routerStartSpy} 43 | } 44 | }; 45 | }, 46 | teardown: function() { 47 | storageMock.verify(); 48 | deviceMock.verify(); 49 | pagesMock.verify(); 50 | touchMock.verify(); 51 | uiMock.verify(); 52 | xfMock.verify(); 53 | 54 | storageMock.restore(); 55 | deviceMock.restore(); 56 | pagesMock.restore(); 57 | touchMock.restore(); 58 | uiMock.restore(); 59 | xfMock.restore(); 60 | XF.Router.extend = routerExtend; 61 | } 62 | }); 63 | 64 | 65 | test('With default values if empty object passed.', function() { 66 | deviceMock.expects('init').withExactArgs(undefined).once(); 67 | pagesMock.expects('init').withExactArgs({standardAnimation: ''}).once(); 68 | 69 | 70 | AppStart({}); 71 | 72 | 73 | ok(routerStartSpy.calledOnce, 'XF.router.start has been called.'); 74 | ok(routerStartSpy.calledWith({pushState: false}), 75 | 'XF.router.start has been called with right param.'); 76 | }); 77 | 78 | 79 | test('Default values do not clean out passed options.', function() { 80 | deviceMock.expects('init').withExactArgs([{name : 'tablet'}]).once(); 81 | pagesMock.expects('init').withExactArgs({ 82 | standardAnimation: 'slideleft', 83 | types: {}}).once(); 84 | 85 | 86 | AppStart({ 87 | animations: {standardAnimation: 'slideleft', types: {}}, 88 | device: {types : [{ 89 | name : 'tablet' 90 | }]}, 91 | history: {pushState: true}, 92 | router: {'/': 'start'} 93 | }); 94 | 95 | 96 | ok(routerStartSpy.calledOnce, 'XF.router.start has been called.'); 97 | ok(routerStartSpy.calledWith({pushState: true}), 98 | 'XF.router.start has been called with right param.'); 99 | }); 100 | 101 | }); 102 | -------------------------------------------------------------------------------- /test/src/dom/data.html: -------------------------------------------------------------------------------- 1 |
4 | Hello 5 | World 6 |
7 | -------------------------------------------------------------------------------- /test/src/xf.app.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | XF.router = null; 4 | Backbone.History.started = false; 5 | 6 | var App = new XF.App({ 7 | settings: { 8 | applicationVersion: '1.1', 9 | noCache: true, 10 | componentUrlPrefix: './components/', 11 | templateUrlPrefix: './' 12 | 13 | }, 14 | animations: { 15 | standardAnimation: 'slideleft', 16 | types: { 17 | 18 | } 19 | }, 20 | device: { 21 | types : [{ 22 | name : 'tablet', 23 | range : { 24 | max : null, 25 | min : 569 26 | }, 27 | templatePath : '', 28 | fallBackTo : 'default', 29 | defaultAnimation: 'fade' 30 | }, 31 | { 32 | name : 'mobile', 33 | range : { 34 | max : 568, 35 | min : null 36 | }, 37 | templatePath : '', 38 | fallBackTo : 'default' 39 | }] 40 | } 41 | }); 42 | 43 | module("XF.App", { 44 | setup: function () { 45 | App = _.extend({ 46 | testing : 101, 47 | initialize : function(options) { 48 | this.testing = options.testing; 49 | } 50 | }); 51 | tap = false; 52 | swipeLeft = false; 53 | swipeRight = false; 54 | swipeUp = false; 55 | swipeDown = false; 56 | swipe = false; 57 | } 58 | }); 59 | 60 | test("initialize", 1, function () { 61 | equal(App.testing, 101); 62 | }); 63 | 64 | asyncTest("start", 1, function () { 65 | XF.on('app:started', function () { 66 | ok(true); 67 | start(); 68 | }); 69 | XF.trigger('app:started'); 70 | }); 71 | 72 | }); -------------------------------------------------------------------------------- /test/src/xf.collection.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | module("XF.Collection", { 3 | setup: function () { 4 | ajaxSettingsWorks = false; 5 | 6 | emptyCollection = new XF.Collection(); 7 | predefCollection = new XF.Collection(); 8 | predefCollection.url = './test.json'; 9 | 10 | predefCollectionCallback = _.extend(predefCollection, {}); 11 | } 12 | }); 13 | 14 | test("empty and parse", 6, function() { 15 | 16 | equal(emptyCollection.url, '/'); 17 | equal(emptyCollection.options, undefined); 18 | equal(emptyCollection.component, null); 19 | equal(emptyCollection.status.loading, false); 20 | equal(emptyCollection.status.loaded, false); 21 | equal(emptyCollection.status.loadingFailed, false); 22 | }); 23 | 24 | asyncTest("predefined and parse", 4, function() { 25 | predefCollection.on('fetched', function () { 26 | ok(true); 27 | equal(ajaxSettingsWorks, false); 28 | equal(predefCollection.status.loading, false); 29 | equal(predefCollection.status.loaded, true); 30 | start(); 31 | }); 32 | 33 | predefCollection.fetch(); 34 | 35 | }); 36 | 37 | asyncTest("predefined and custom callback", 7, function() { 38 | ajaxSettingsWorks = true; 39 | 40 | predefCollectionCallback.on('fetched', function () { 41 | 42 | predefCollectionCallback.off('fetched').on('fetched', function () { 43 | ok(true); 44 | equal(ajaxSettingsWorks, true); 45 | equal(predefCollectionCallback.status.loading, false); 46 | equal(predefCollectionCallback.status.loaded, true); 47 | start(); 48 | }); 49 | 50 | predefCollectionCallback.refresh(); 51 | equal(predefCollectionCallback.status.loading, true); 52 | equal(predefCollectionCallback.status.loaded, false); 53 | equal(predefCollectionCallback.ajaxSettings.silent, false); 54 | }); 55 | 56 | predefCollectionCallback.fetch(); 57 | }); 58 | }); -------------------------------------------------------------------------------- /test/src/xf.component.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | $('body').append('
'); 4 | 5 | 6 | XF.on('component:test:constructed', function () { 7 | 8 | module("XF.Component", { 9 | setup: function () { 10 | 11 | } 12 | }); 13 | 14 | test('defaults', 5, function () { 15 | equal(XF.getComponentByID('test').id, 'test'); 16 | equal('test' in XF.getRegisteredComponents(), true); 17 | equal(XF.getComponentByID('test').defaults.autoload, true); 18 | equal(XF.getComponentByID('test').defaults.autorender, true); 19 | equal(XF.getComponentByID('test').defaults.updateOnShow, false); 20 | }); 21 | 22 | test('model', 2, function () { 23 | equal(XF.getComponentByID('test').model, null); 24 | equal(XF.getComponentByID('test').Model, null); 25 | }); 26 | 27 | test('collection', 2, function () { 28 | equal(XF.getComponentByID('test').Collection.isPrototypeOf(XF.Collection), false); 29 | equal(XF.getComponentByID('test').collection.url, 'test.json'); 30 | }); 31 | 32 | test('view', 2, function () { 33 | equal(XF.getComponentByID('test').View.isPrototypeOf(XF.View), false); 34 | equal(XF.getComponentByID('test').view.useCache, false); 35 | }); 36 | 37 | test('load', 1, function () { 38 | equal($('#testcomponent').length, 1, 'Component add: ' + ($('#testcomponent').length === 1 ? true : false)); 39 | }); 40 | 41 | test('ajax', 1, function () { 42 | equal(XF.getComponentByID('test').collection.models[0].attributes.status, "success"); 43 | }); 44 | }); 45 | }); -------------------------------------------------------------------------------- /test/src/xf.core.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | XF.router = null; 4 | Backbone.History.started = false; 5 | 6 | var App = new XF.App({ 7 | settings: { 8 | applicationVersion: '1.1', 9 | noCache: true, 10 | componentUrlPrefix: './components/', 11 | templateUrlPrefix: './' 12 | 13 | }, 14 | animations: { 15 | standardAnimation: 'slideleft', 16 | types: { 17 | 18 | } 19 | }, 20 | device: { 21 | types : [{ 22 | name : 'tablet', 23 | range : { 24 | max : null, 25 | min : 569 26 | }, 27 | templatePath : '', 28 | fallBackTo : 'default', 29 | defaultAnimation: 'fade' 30 | }, 31 | { 32 | name : 'mobile', 33 | range : { 34 | max : 568, 35 | min : null 36 | }, 37 | templatePath : '', 38 | fallBackTo : 'default' 39 | }] 40 | } 41 | }); 42 | 43 | module("XF.Core", { 44 | setup: function () { 45 | required = false; 46 | } 47 | }); 48 | 49 | test("common", 1, function() { 50 | 51 | equal('XF' in window, true); 52 | }); 53 | 54 | test("history", 2, function() { 55 | 56 | equal(XF.history.options.pushState, false); 57 | equal(XF.history.root, "/"); 58 | }); 59 | 60 | test("required component", 1, function() { 61 | 62 | XF.require('test', function () { 63 | required = true; 64 | }); 65 | 66 | equal(required, true); 67 | }); 68 | }); -------------------------------------------------------------------------------- /test/src/xf.device.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | module("XF.device", {}); 4 | 5 | test('window size', 2, function () { 6 | XF.device.init(); 7 | var size = { 8 | width: $(window).width(), 9 | height: $(window).height() 10 | }; 11 | equal(XF.device.size.width, size.width, 'Width passed'); 12 | equal(XF.device.size.height, size.height, 'Height passed'); 13 | }); 14 | 15 | test('device mobile', 1, function () { 16 | var mobile = true; 17 | ok(XF.device.isMobile !== mobile, 'Mobile: ' + XF.device.isMobile); 18 | }); 19 | 20 | test('device iOS', 1, function () { 21 | var ios = true; 22 | ok(XF.device.isIOS !== ios, 'iOS: ' + XF.device.isIOS); 23 | }); 24 | 25 | test('supports', 1, function () { 26 | var touches = true, 27 | pointer = true, 28 | cssAnims = true; 29 | // ok(XF.device.supports.touchEvents !== touches, 'TouchEvents: ' + XF.device.supports.touchEvents); 30 | ok(XF.device.supports.pointerEvents !== pointer, 'PointerEvents: ' + (XF.device.supports.pointerEvents ? true : false)); 31 | }); 32 | }); -------------------------------------------------------------------------------- /test/src/xf.model.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | module("XF.Model", { 4 | setup: function () { 5 | ajaxSettingsModelWorks = false; 6 | 7 | emptyModel = new XF.Collection(); 8 | predefModel = new XF.Collection(); 9 | predefModel.url = './test.json'; 10 | 11 | predefModelCallback = _.extend(predefModel, {}); 12 | } 13 | }); 14 | 15 | test("empty and parse", 6, function() { 16 | 17 | equal(emptyModel.url, '/'); 18 | equal(emptyModel.options, undefined); 19 | equal(emptyModel.component, null); 20 | equal(emptyModel.status.loading, false); 21 | equal(emptyModel.status.loaded, false); 22 | equal(emptyModel.status.loadingFailed, false); 23 | }); 24 | 25 | asyncTest("predefined and parse", 2, function() { 26 | predefModel.on('fetched', function () { 27 | console.log('PREFERED TEST'); 28 | ok(true); 29 | equal(ajaxSettingsModelWorks, false); 30 | start(); 31 | }); 32 | 33 | predefModel.fetch(); 34 | 35 | }); 36 | 37 | asyncTest("predefined and custom callback", 4, function() { 38 | ajaxSettingsModelWorks = true; 39 | 40 | predefModelCallback.on('fetched', function () { 41 | 42 | predefModelCallback.off('fetched').on('fetched', function () { 43 | ok(true); 44 | equal(ajaxSettingsModelWorks, true); 45 | equal(predefModelCallback.status.loading, false); 46 | equal(predefModelCallback.status.loaded, true); 47 | start(); 48 | }); 49 | 50 | predefModelCallback.refresh(); 51 | }); 52 | 53 | predefCollectionCallback.fetch(); 54 | }); 55 | }); -------------------------------------------------------------------------------- /test/src/xf.pages.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | module("XF.Pages", { 3 | setup: function () { 4 | pagesBoolean = false; 5 | } 6 | }); 7 | 8 | test("common", 11, function() { 9 | 10 | equal(XF.pages.status.started, false); 11 | equal(XF.pages.pageClass, 'xf-page'); 12 | equal(XF.pages.activePageClass, 'xf-page-active'); 13 | 14 | equal(XF.pages.animations.next, null); 15 | equal(XF.pages.animations.activePage, null); 16 | equal(XF.pages.animations.activePageName, null); 17 | equal(XF.pages.animations.standardAnimation, 'slideleft'); 18 | equal('slideleft' in XF.pages.animations.types, true); 19 | equal('slideright' in XF.pages.animations.types, true); 20 | equal('fade' in XF.pages.animations.types, true); 21 | equal('none' in XF.pages.animations.types, true); 22 | }); 23 | 24 | test("methods", 3, function() { 25 | 26 | XF.pages.setDefaultAnimationType('none'); 27 | equal(XF.pages.animations.standardAnimation, 'none'); 28 | 29 | XF.pages.setNextAnimationType('fade'); 30 | equal(XF.pages.animations.next, 'fade'); 31 | 32 | equal(XF.pages.show(), undefined); 33 | }); 34 | }); -------------------------------------------------------------------------------- /test/src/xf.router.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | var lastUrl = null, 4 | lastRoute = null, 5 | bar = null; 6 | 7 | var onRoute = function (router, route, args) { 8 | lastRoute = route; 9 | lastArgs = args; 10 | alert(router); 11 | }; 12 | 13 | var Location = function(href) { 14 | this.replace(href); 15 | }; 16 | 17 | module("XF.router", { 18 | setup: function () { 19 | XF.router.on('route', onRoute); 20 | } 21 | }); 22 | 23 | test('getPageNameFromFragment', 1, function () { 24 | location.replace('#test/2'); 25 | XF.history.checkUrl(); 26 | equal(XF.router.getPageNameFromFragment(XF.history.fragment), 'test'); 27 | }); 28 | 29 | test('bindAnyRoute', 1, function () { 30 | bar = XF.router.bindAnyRoute; 31 | XF.router.bindAnyRoute = (function () { 32 | location.replace('#bind'); 33 | })(); 34 | XF.history.checkUrl(); 35 | equal(XF.router.getPageNameFromFragment(XF.history.fragment), 'bind'); 36 | XF.router.bindAnyRoute = bar; 37 | }); 38 | 39 | }); -------------------------------------------------------------------------------- /test/src/xf.settings.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | XF.router = null; 4 | Backbone.History.started = false; 5 | 6 | var App = new XF.App({ 7 | settings: { 8 | applicationVersion: '1.1', 9 | noCache: true, 10 | componentUrlPrefix: './components/', 11 | templateUrlPrefix: './' 12 | 13 | }, 14 | animations: { 15 | standardAnimation: 'slideleft', 16 | types: { 17 | 18 | } 19 | }, 20 | device: { 21 | types : [{ 22 | name : 'tablet', 23 | range : { 24 | max : null, 25 | min : 569 26 | }, 27 | templatePath : '', 28 | fallBackTo : 'default', 29 | defaultAnimation: 'fade' 30 | }, 31 | { 32 | name : 'mobile', 33 | range : { 34 | max : 568, 35 | min : null 36 | }, 37 | templatePath : '', 38 | fallBackTo : 'default' 39 | }] 40 | } 41 | }); 42 | 43 | module("XF.settings", {}); 44 | 45 | test('applicationVersion', 1, function () { 46 | equal(XF.settings.applicationVersion, '1.1'); 47 | }); 48 | 49 | test('noCache', 1, function () { 50 | ok(XF.settings.noCache, true); 51 | }); 52 | 53 | test('componentUrlPrefix', 1, function () { 54 | ok(XF.settings.componentUrlPrefix, 'components/'); 55 | }); 56 | 57 | test('templateUrlPrefix', 1, function () { 58 | ok(XF.settings.templateUrlPrefix, "./"); 59 | }); 60 | }); -------------------------------------------------------------------------------- /test/src/xf.storage.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | module("XF.Storage", { 3 | setup: function () { 4 | XF.storage.init(); 5 | setItem = 'test'; 6 | } 7 | }); 8 | 9 | test("common", 2, function() { 10 | 11 | equal(XF.storage.storage, window.localStorage); 12 | equal(XF.storage.isAvailable, true); 13 | }); 14 | 15 | test("methods", 2, function() { 16 | XF.storage.set('set', 'test'); 17 | equal(XF.storage.get('set'), setItem); 18 | 19 | XF.storage.clear(); 20 | equal(XF.storage.get('set'), undefined); 21 | }); 22 | }); -------------------------------------------------------------------------------- /test/src/xf.touch.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | 3 | XF.on('component:test:constructed', function () { 4 | 5 | module("XF.touches", { 6 | setup: function () { 7 | 8 | } 9 | }); 10 | 11 | test("tap", 1, function () { 12 | $('body').prepend($('#testcomponent').data('events')); 13 | $('#testcomponent').trigger('tap'); 14 | equal(tap, true); 15 | }); 16 | 17 | test("swipe", 1, function () { 18 | $('#testcomponent').trigger('swipe'); 19 | equal(swipe, true); 20 | }); 21 | 22 | test("swipeLeft", 1, function () { 23 | $('#testcomponent').trigger('swipeLeft'); 24 | equal(swipeLeft, true); 25 | }); 26 | 27 | test("swipeRight", 1, function () { 28 | $('#testcomponent').trigger('swipeRight'); 29 | equal(swipeRight, true); 30 | }); 31 | 32 | test("swipeUp", 1, function () { 33 | $('#testcomponent').trigger('swipeUp'); 34 | equal(swipeUp, true); 35 | }); 36 | 37 | test("swipeDown", 1, function () { 38 | $('#testcomponent').trigger('swipeDown'); 39 | equal(swipeDown, true); 40 | }); 41 | }); 42 | 43 | }); -------------------------------------------------------------------------------- /test/src/xf.ui.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | XF.on('component:test:constructed', function () { 4 | 5 | module("XF.ui", { 6 | 7 | }); 8 | 9 | test('header', 1, function () { 10 | equal($(XF.ui.header.selector).attr('data-skip-enhance'), 'true'); 11 | $(XF.ui.header.selector).remove(); 12 | }); 13 | 14 | test('footer', 1, function () { 15 | equal($(XF.ui.footer.selector).attr('data-skip-enhance'), 'true'); 16 | $(XF.ui.footer.selector).remove(); 17 | }); 18 | 19 | test('button', 1, function () { 20 | equal($(XF.ui.button.selector).attr('data-skip-enhance'), 'true'); 21 | $(XF.ui.button.selector).remove(); 22 | }); 23 | 24 | test('checkbox & radio', 1, function () { 25 | equal($('#checkbox').attr('data-skip-enhance'), 'true'); 26 | $('#checkbox').parents('.xf-input-checkbox').parent().remove(); 27 | }); 28 | 29 | test('fieldset', 1, function () { 30 | equal($(XF.ui.fieldset.selector).attr('data-skip-enhance'), 'true'); 31 | $(XF.ui.fieldset.selector).remove(); 32 | }); 33 | 34 | test('text input', 1, function () { 35 | equal($(XF.ui.input.selector).attr('data-skip-enhance'), 'true'); 36 | $(XF.ui.input.selector).remove(); 37 | }); 38 | 39 | test('tabs', 1, function () { 40 | equal($(XF.ui.tabs.selector).attr('data-skip-enhance'), 'true'); 41 | $(XF.ui.tabs.selector).remove(); 42 | }); 43 | 44 | test('list', 1, function () { 45 | equal($(XF.ui.list.selector).attr('data-skip-enhance'), 'true'); 46 | $(XF.ui.list.selector).remove(); 47 | }); 48 | 49 | test('loader', 1, function () { 50 | var loader = XF.ui.loader.create(); 51 | XF.ui.loader.show(loader); 52 | equal(loader.attr('data-skip-enhance'), 'true'); 53 | XF.ui.loader.remove(loader); 54 | }); 55 | }); 56 | }); -------------------------------------------------------------------------------- /test/test-files.js: -------------------------------------------------------------------------------- 1 | /*global describe, before, it, beforeEach */ 2 | 'use strict'; 3 | var fs = require('fs'); 4 | var assert = require('assert'); 5 | var path = require('path'); 6 | var util = require('util'); 7 | var generators = require('yeoman-generator'); 8 | var helpers = require('yeoman-generator').test; 9 | var _ = require('underscore.string'); 10 | 11 | describe('XF generator', function () { 12 | var xf; 13 | 14 | it('created xf scripts', function (done) { 15 | var expected = ['js/xf.js', 16 | 'js/xf.min.js' 17 | ]; 18 | helpers.assertFiles(expected); 19 | done(); 20 | }); 21 | 22 | it('created xf styles', function (done) { 23 | var expected = ['styles/xf.css','styles/xf.min.css']; 24 | helpers.assertFiles(expected); 25 | done(); 26 | }); 27 | }); -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | X-Framework Test page 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 31 | 93 | -------------------------------------------------------------------------------- /test/test.json: -------------------------------------------------------------------------------- 1 | {"status":"success"} -------------------------------------------------------------------------------- /test/test.tmpl: -------------------------------------------------------------------------------- 1 |

1111

2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
test
13 | 14 | 15 |
16 | 17 | 18 |

header

19 | 20 | 21 | 22 | 26 | 27 | 28 |
29 | 30 | 31 | 32 | 33 |
34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /test/ui/xf.ui.button.js: -------------------------------------------------------------------------------- 1 | // Phantomjs test for button 2 | 3 | var page = require('webpage').create(), 4 | system = require('system'), 5 | address, output, size, 6 | testname = 'Button'; 7 | 8 | (function() { 9 | console.log('\x1b[0m\x1b[7mStart ' + testname + ' test!'); 10 | 11 | if (system.args.length < 2) { 12 | console.log('Usage: tests/*.js URL [filename] [paperwidth*paperheight|paperformat] [zoom]'); 13 | console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"'); 14 | phantom.exit(1); 15 | } else { 16 | address = system.args[1]; 17 | output = system.args[2]; 18 | page.viewportSize = { width: 1024, height: 780 }; 19 | 20 | if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") { 21 | size = system.args[3].split('*'); 22 | page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } 23 | : { format: system.args[3], orientation: 'portrait', margin: '1cm' }; 24 | } 25 | 26 | if (system.args.length > 4) { 27 | page.zoomFactor = system.args[4]; 28 | } 29 | 30 | page.open(address, function (status) { 31 | 32 | if (status !== 'success') { 33 | console.log('\n\t\x1b[0m\033[31mUnable to load the address.\n'); 34 | phantom.exit(); 35 | } else { 36 | window.setTimeout(function () { 37 | 38 | var result = page.evaluate(function () { 39 | $('body').append(XF.ui.popup.createButton({text: 'Test button text'})); 40 | 41 | if ($('.xf-button').length) { 42 | return $('.xf-button').find('.xf-button-text').text(); 43 | } 44 | }, 'result'); 45 | 46 | if (result === '' || result === null) { 47 | console.log('\n\t\x1b[0m\033[31mError during button creation.\n'); 48 | } else { 49 | console.log('\n\t\x1b[0m\033[32mButton \t\t\033[39m\x1b[1m[ ', result, ' ] \t\t\x1b[0m\033[32mcreated\n'); 50 | } 51 | 52 | if (output) { 53 | page.render(output); 54 | } 55 | 56 | phantom.exit(); 57 | }, 200); 58 | } 59 | }); 60 | } 61 | })(); -------------------------------------------------------------------------------- /test/ui/xf.ui.dialog.js: -------------------------------------------------------------------------------- 1 | // Phantomjs test for dialog 2 | 3 | var page = require('webpage').create(), 4 | system = require('system'), 5 | address, output, size, 6 | testname = 'Dialog'; 7 | 8 | (function() { 9 | console.log('\x1b[0m\x1b[7mStart ' + testname + ' test!'); 10 | 11 | if (system.args.length < 2) { 12 | console.log('Usage: tests/*.js URL [filename] [paperwidth*paperheight|paperformat] [zoom]'); 13 | console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"'); 14 | phantom.exit(1); 15 | } else { 16 | address = system.args[1]; 17 | output = system.args[2]; 18 | page.viewportSize = { width: 1024, height: 780 }; 19 | 20 | if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") { 21 | size = system.args[3].split('*'); 22 | page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } 23 | : { format: system.args[3], orientation: 'portrait', margin: '1cm' }; 24 | } 25 | 26 | if (system.args.length > 4) { 27 | page.zoomFactor = system.args[4]; 28 | } 29 | 30 | page.open(address, function (status) { 31 | 32 | if (status !== 'success') { 33 | console.log('\n\t\x1b[0m\033[31mUnable to load the address.\n'); 34 | phantom.exit(); 35 | } else { 36 | window.setTimeout(function () { 37 | 38 | var result = page.evaluate(function () { 39 | XF.ui.popup.showDialog('Test header', 'Test Dialog'); 40 | 41 | if ($('.xf-dialog').length) { 42 | return $('.xf-dialog').find('.xf-dialog-box-content').text(); 43 | } 44 | }, 'result'); 45 | 46 | if (result === '' || result === null) { 47 | console.log('\n\t\x1b[0m\033[31mError during dialog creation.\n'); 48 | } else { 49 | console.log('\n\t\x1b[0m\033[32mDialog \t\t\033[39m\x1b[1m[ ', result, ' ] \t\t\x1b[0m\033[32mcreated\n'); 50 | } 51 | 52 | if (output) { 53 | page.render(output); 54 | } 55 | 56 | phantom.exit(); 57 | }, 200); 58 | } 59 | }); 60 | } 61 | })(); -------------------------------------------------------------------------------- /test/ui/xf.ui.fieldset.js: -------------------------------------------------------------------------------- 1 | // Phantomjs test for fieldset 2 | 3 | var page = require('webpage').create(), 4 | system = require('system'), 5 | address, output, size, 6 | testname = 'Fieldset'; 7 | 8 | (function() { 9 | console.log('\x1b[0m\x1b[7mStart ' + testname + ' test!'); 10 | 11 | if (system.args.length < 2) { 12 | console.log('Usage: tests/*.js URL [filename] [paperwidth*paperheight|paperformat] [zoom]'); 13 | console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"'); 14 | phantom.exit(1); 15 | } else { 16 | address = system.args[1]; 17 | output = system.args[2]; 18 | page.viewportSize = { width: 1024, height: 780 }; 19 | 20 | if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") { 21 | size = system.args[3].split('*'); 22 | page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } 23 | : { format: system.args[3], orientation: 'portrait', margin: '1cm' }; 24 | } 25 | 26 | if (system.args.length > 4) { 27 | page.zoomFactor = system.args[4]; 28 | } 29 | 30 | page.open(address, function (status) { 31 | 32 | if (status !== 'success') { 33 | console.log('\n\t\x1b[0m\033[31mUnable to load the address.\n'); 34 | phantom.exit(); 35 | } else { 36 | window.setTimeout(function () { 37 | 38 | var result = page.evaluate(function () { 39 | $('body').append('
Legend
'); 40 | XF.trigger('ui:enhance', $('body')); 41 | 42 | if ($('fieldset').length && $('fieldset').hasClass('xf-controlgroup')) { 43 | return $('fieldset').attr('data-description'); 44 | } 45 | }, 'result'); 46 | 47 | if (result === '' || result === null) { 48 | console.log('\n\t\x1b[0m\033[31mError during fieldset creation.\n'); 49 | } else { 50 | console.log('\n\t\x1b[0m\033[32mFieldset \t\033[39m\x1b[1m[ ', result, ' ] \t\t\x1b[0m\033[32mcreated\n'); 51 | } 52 | 53 | if (output) { 54 | page.render(output); 55 | } 56 | 57 | phantom.exit(); 58 | }, 200); 59 | } 60 | }); 61 | } 62 | })(); -------------------------------------------------------------------------------- /test/ui/xf.ui.list.js: -------------------------------------------------------------------------------- 1 | // Phantomjs test for list 2 | 3 | var page = require('webpage').create(), 4 | system = require('system'), 5 | address, output, size, 6 | testname = 'List'; 7 | 8 | (function() { 9 | console.log('\x1b[0m\x1b[7mStart ' + testname + ' test!'); 10 | 11 | if (system.args.length < 2) { 12 | console.log('Usage: tests/*.js URL [filename] [paperwidth*paperheight|paperformat] [zoom]'); 13 | console.log(' paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"'); 14 | phantom.exit(1); 15 | } else { 16 | address = system.args[1]; 17 | output = system.args[2]; 18 | page.viewportSize = { width: 1024, height: 780 }; 19 | 20 | if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") { 21 | size = system.args[3].split('*'); 22 | page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' } 23 | : { format: system.args[3], orientation: 'portrait', margin: '1cm' }; 24 | } 25 | 26 | if (system.args.length > 4) { 27 | page.zoomFactor = system.args[4]; 28 | } 29 | 30 | page.open(address, function (status) { 31 | 32 | if (status !== 'success') { 33 | console.log('\n\t\x1b[0m\033[31mUnable to load the address.\n'); 34 | phantom.exit(); 35 | } else { 36 | window.setTimeout(function () { 37 | 38 | var result = page.evaluate(function () { 39 | $('body').append(''); 40 | XF.trigger('ui:enhance', $('body')); 41 | 42 | if ($('ul').length && $('ul').hasClass('xf-listview')) { 43 | return $('ul').attr('data-description'); 44 | } 45 | }, 'result'); 46 | 47 | if (result === '' || result === null) { 48 | console.log('\n\t\x1b[0m\033[31mError during list creation.\n'); 49 | } else { 50 | console.log('\n\t\x1b[0m\033[32mList \t\t\033[39m\x1b[1m[ ', result, ' ] \t\t\x1b[0m\033[32mcreated\n'); 51 | } 52 | 53 | if (output) { 54 | page.render(output); 55 | } 56 | 57 | phantom.exit(); 58 | }, 200); 59 | } 60 | }); 61 | } 62 | })(); -------------------------------------------------------------------------------- /xf/src/xf.app.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './xf.core', 3 | 'underscore', 4 | 'backbone', 5 | './app/start', 6 | './xf.settings' 7 | ], function(XF, _, BB, AppStart) { 8 | 9 | XF.App = function(options) { 10 | var extOptions; 11 | 12 | options = options || {}; 13 | options.device = options.device || {}; 14 | extOptions = _.clone(options); 15 | 16 | // options.settings 17 | _.extend(XF.settings, options.settings); 18 | 19 | extOptions = _.omit(extOptions, ['settings', 'device', 'animations', 'router', 'debug', 'history']); 20 | _.extend(this, extOptions); 21 | 22 | this.initialize(); 23 | 24 | AppStart(options); 25 | }; 26 | 27 | 28 | _.extend(XF.App.prototype, XF.Events); 29 | 30 | _.extend(XF.App.prototype, /** @lends XF.App.prototype */ { 31 | initialize: function() { 32 | 33 | 34 | } 35 | }); 36 | 37 | /** 38 | This method allows to extend XF.App with saving the whole prototype chain 39 | @function 40 | @static 41 | */ 42 | XF.App.extend = BB.Model.extend; 43 | 44 | return XF; 45 | }); -------------------------------------------------------------------------------- /xf/src/xf.collection.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './xf.core', 3 | 'underscore', 4 | 'backbone', 5 | './dom/dom', 6 | './xf.settings' 7 | ], function(XF, _, BB, Dom) { 8 | 9 | XF.Collection = BB.Collection.extend({ 10 | 11 | _initProperties: function() { 12 | this.status = { 13 | loaded: false, 14 | loading: false, 15 | loadingFailed: false 16 | }; 17 | 18 | if (!_.has(this, 'root')) { 19 | this.root = null; 20 | } 21 | if (typeof this['ajaxSettings'] == 'undefined') { 22 | this.ajaxSettings = null; 23 | } 24 | this.component = null; 25 | }, 26 | 27 | _bindListeners: function() { 28 | //this.on('change reset sync add', this.onDataChanged, this); 29 | this.on('refresh', this.refresh, this); 30 | }, 31 | 32 | constructor: function(models, options) { 33 | this._initProperties(); 34 | this._bindListeners(); 35 | 36 | if (!options) { 37 | options = {}; 38 | } 39 | 40 | if (options.component) { 41 | this.component = options.component; 42 | } 43 | _.omit(options, 'component'); 44 | 45 | this.url = this.url || 46 | XF.settings.property('dataUrlPrefix').replace(/(\/$)/g, '') + '/' + (_.has(this, 'component') && this.component !== null && _.has(this.component, 'name') ? this.component.name + '/' : ''); 47 | 48 | if (_.has(this, 'component') && this.component !== null && this.component.options.updateOnShow) { 49 | Dom(this.component.selector()).bind('show', _.bind(this.refresh, this)); 50 | } 51 | 52 | this.ajaxSettings = this.ajaxSettings || _.defaults({}, XF.settings.property('ajaxSettings')); 53 | 54 | if (_.has(this.ajaxSettings, 'success') && _.isFunction(this.ajaxSettings.success)) { 55 | var onDataLoaded = _.bind(this._onDataLoaded, this), 56 | onSuccess = this.ajaxSettings.success; 57 | 58 | this.ajaxSettings.success = function() { 59 | onDataLoaded(); 60 | onSuccess(); 61 | }; 62 | } else { 63 | this.ajaxSettings.success = _.bind(this._onDataLoaded, this); 64 | } 65 | 66 | BB.Collection.apply(this, arguments); 67 | }, 68 | 69 | /** 70 | Constructs model instance 71 | @private 72 | */ 73 | initialize: function() { 74 | 75 | }, 76 | 77 | construct: function() { 78 | 79 | }, 80 | 81 | /** 82 | Refreshes data from backend if necessary 83 | @private 84 | */ 85 | refresh: function() { 86 | this.status.loaded = false; 87 | this.status.loading = true; 88 | 89 | this.reset(); 90 | this.ajaxSettings.silent = false; 91 | this.fetch(this.ajaxSettings); 92 | }, 93 | 94 | fetch: function(options) { 95 | options = _.defaults(options || {}, this.ajaxSettings); 96 | 97 | return Backbone.Collection.prototype.fetch.call(this, options); 98 | }, 99 | 100 | _onDataLoaded: function() { 101 | this.status.loaded = true; 102 | this.status.loading = false; 103 | 104 | this.trigger('fetched'); 105 | } 106 | 107 | }); 108 | 109 | return XF; 110 | }); -------------------------------------------------------------------------------- /xf/src/xf.framework.js: -------------------------------------------------------------------------------- 1 | /* ExcludeStart */ 2 | require.config({ 3 | paths: { 4 | jquery: './../bower_modules/jquery/jquery', 5 | underscore: './../bower_modules/underscore/underscore', 6 | backbone: './../bower_modules/backbone/backbone' 7 | }, 8 | shim: { 9 | 'jquery': { 10 | exports: 'jQuery' 11 | }, 12 | 'backbone': { 13 | //These script dependencies should be loaded before loading 14 | //backbone.js 15 | deps: ['underscore', 'jquery'], 16 | //Once loaded, use the global 'BB' as the 17 | //module value. 18 | exports: 'Backbone' 19 | }, 20 | 'underscore': { 21 | exports: '_' 22 | } 23 | } 24 | }); 25 | /* ExcludeEnd */ 26 | 27 | define([ 28 | './xf.core', 29 | './xf.log', 30 | './xf.app', 31 | './xf.touch', 32 | './xf.router', 33 | './xf.utils', 34 | './xf.pages', 35 | './xf.settings', 36 | './xf.storage', 37 | './xf.device', 38 | './xf.collection', 39 | './xf.model', 40 | './xf.view', 41 | './xf.component', 42 | './xf.ui', 43 | 'underscore' 44 | ], function(XF) { 45 | 46 | return XF; 47 | }); -------------------------------------------------------------------------------- /xf/src/xf.log.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './xf.core', 3 | 'underscore' 4 | ], function(XF, _) { 5 | 6 | var logMap = ['log', 'warn', 'error', 'info'], 7 | logPrefix = 'XF >> ', 8 | console = window.console || {}; 9 | 10 | XF.log = function(log) { 11 | if (_.isFunction(console['log']) && XF.log.enabled) { 12 | console.log(logPrefix + log); 13 | } 14 | }; 15 | 16 | XF.log.enabled = true; 17 | 18 | _.each(logMap, function(type) { 19 | 20 | XF.log[type] = function(log) { 21 | if (_.isFunction(console[type]) && XF.log.enabled) { 22 | console[type](logPrefix + log); 23 | } 24 | }; 25 | 26 | }); 27 | 28 | return XF; 29 | }); -------------------------------------------------------------------------------- /xf/src/xf.model.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './xf.core', 3 | 'underscore', 4 | 'backbone', 5 | './dom/dom', 6 | './xf.settings' 7 | ], function(XF, _, BB, Dom) { 8 | 9 | XF.Model = BB.Model.extend({ 10 | 11 | _initProperties: function() { 12 | this.status = { 13 | loaded: false, 14 | loading: false, 15 | loadingFailed: false 16 | }; 17 | 18 | if (!_.has(this, 'root')) { 19 | this.root = null; 20 | } 21 | if (typeof this['ajaxSettings'] == 'undefined') { 22 | this.ajaxSettings = null; 23 | } 24 | this.component = null; 25 | }, 26 | 27 | _bindListeners: function() { 28 | this.on('refresh', this.refresh, this); 29 | }, 30 | 31 | constructor: function(attributes, options) { 32 | this._initProperties(); 33 | this._bindListeners(); 34 | 35 | if (!options) { 36 | options = {}; 37 | } 38 | 39 | if (options.component) { 40 | this.component = options.component; 41 | } 42 | _.omit(options, 'component'); 43 | 44 | this.urlRoot = this.urlRoot || XF.settings.property('dataUrlPrefix').replace(/(\/$)/g, '') + '/' + (_.has(this, 'component') && this.component !== null && _.has(this.component, 'name') ? this.component.name + '/' : ''); 45 | 46 | if (_.has(this, 'component') && this.component !== null && this.component.options.updateOnShow) { 47 | Dom(this.component.selector()).bind('show', _.bind(this.refresh, this)); 48 | } 49 | 50 | this.ajaxSettings = this.ajaxSettings || XF.settings.property('ajaxSettings'); 51 | 52 | if (_.has(this.ajaxSettings, 'success') && _.isFunction(this.ajaxSettings.success)) { 53 | var onSuccess = this.ajaxSettings.success, 54 | onDataLoaded = _.bind(this._onDataLoaded, this); 55 | this.ajaxSettings.success = function() { 56 | onDataLoaded(); 57 | onSuccess(); 58 | }; 59 | } else { 60 | this.ajaxSettings.success = _.bind(this._onDataLoaded, this); 61 | } 62 | 63 | BB.Model.apply(this, arguments); 64 | }, 65 | 66 | /** 67 | Constructs model instance 68 | @private 69 | */ 70 | initialize: function() { 71 | 72 | }, 73 | 74 | construct: function() { 75 | 76 | }, 77 | 78 | /** 79 | Refreshes data from backend if necessary 80 | @private 81 | */ 82 | refresh: function() { 83 | this.status.loaded = false; 84 | this.status.loading = true; 85 | 86 | this.fetch(this.ajaxSettings); 87 | }, 88 | 89 | fetch: function(options) { 90 | options = _.defaults(options || {}, this.ajaxSettings); 91 | 92 | return Backbone.Collection.prototype.fetch.call(this, options); 93 | }, 94 | 95 | _onDataLoaded: function() { 96 | this.status.loaded = true; 97 | this.status.loading = false; 98 | 99 | this.trigger('fetched'); 100 | } 101 | 102 | }); 103 | 104 | return XF; 105 | }); -------------------------------------------------------------------------------- /xf/src/xf.router.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './xf.core', 3 | 'underscore', 4 | 'backbone', 5 | './dom/dom', 6 | './xf.pages' 7 | ], function(XF, _, BB, Dom) { 8 | 9 | /** 10 | Instance of {@link XF.RouterClass} 11 | @static 12 | @type {XF.router} 13 | */ 14 | XF.router = null; 15 | 16 | /** 17 | Implements Routing. 18 | @class 19 | @static 20 | @augments XF.Events 21 | @param {Object} routes routes has map 22 | @param {Object} handlers handlers has map 23 | */ 24 | XF.Router = BB.Router; 25 | 26 | _.extend(XF.Router.prototype, /** @lends XF.Router.prototype */ { 27 | 28 | 29 | /** 30 | Initiates Rounting & history listening 31 | @private 32 | */ 33 | start: function(options) { 34 | this.bindAnyRoute(); 35 | XF.history.start(options); 36 | 37 | // TODO pass Dom.root element instead of $(body) 38 | XF.trigger('ui:enhance', $('body')); 39 | }, 40 | 41 | 42 | /** 43 | Binds a callback to any route 44 | @param {Function} callback A function to be called when any route is visited 45 | */ 46 | bindAnyRoute: function() { 47 | this.on('route', function(e) { 48 | XF.log('router: navigating to "' + this.getPageNameFromFragment(XF.history.fragment) + '"'); 49 | if (XF.pages) { 50 | XF.pages.show(this.getPageNameFromFragment(XF.history.fragment)); 51 | } 52 | }); 53 | }, 54 | 55 | /** 56 | Returns page name string by fragment 57 | @param String fragment 58 | @return String 59 | */ 60 | getPageNameFromFragment: function(fragment) { 61 | var parts = fragment.replace(/^\/+/, '').replace(/\/+$/, '').split('/'); 62 | return parts[0]; 63 | } 64 | }); 65 | 66 | return XF; 67 | }); -------------------------------------------------------------------------------- /xf/src/xf.settings.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './xf.core' 3 | ], function(XF) { 4 | 5 | /** 6 | {@link XF.settings} 7 | @static 8 | @type {Object} 9 | */ 10 | XF.settings = { 11 | /** 12 | Used for {@link XF.storage} clearance when new version released 13 | @memberOf XF.settings.prototype 14 | @default '1.0.0' 15 | @type String 16 | */ 17 | appVersion: '1.0.0', 18 | /** 19 | Deactivates cache usage for the whole app (usefull for developement) 20 | @memberOf XF.settings.prototype 21 | @default false 22 | @type String 23 | */ 24 | noCache: false, 25 | /** 26 | Used by default Component URL formatter: prefix + component_name + postfix 27 | @memberOf XF.settings.prototype 28 | @default '' 29 | @type String 30 | */ 31 | componentUrlPrefix: 'js/components/', 32 | /** 33 | Used by default Component URL formatter: prefix + component_name + postfix 34 | @memberOf XF.settings.prototype 35 | @default '.js' 36 | @type String 37 | */ 38 | componentUrlPostfix: '.js', 39 | /** 40 | Default Component URL formatter: prefix + component_name + postfix 41 | @param {String} compName Component name 42 | @memberOf XF.settings.prototype 43 | @returns {String} Component URL 44 | @type Function 45 | */ 46 | componentUrl: function(compName) { 47 | return XF.settings.property('componentUrlPrefix') + compName + XF.settings.property('componentUrlPostfix'); 48 | }, 49 | 50 | /** 51 | Used by default Template URL formatter: prefix + component_name + postfix 52 | @memberOf XF.settings.prototype 53 | @default '' 54 | @type String 55 | */ 56 | templateUrlPrefix: 'tmpl/', 57 | /** 58 | Used by default Template URL formatter: prefix + component_name + postfix 59 | @memberOf XF.settings.prototype 60 | @default '.tmpl' 61 | @type String 62 | */ 63 | templateUrlPostfix: '.tmpl', 64 | 65 | templateCollectionName: 'xf-templates', 66 | templateCollectionSeparator: ',', 67 | 68 | /** 69 | Used by default Data URL formatter: prefix + component_name + postfix 70 | @memberOf XF.settings.prototype 71 | @default '' 72 | @type String 73 | */ 74 | dataUrlPrefix: '', 75 | 76 | 77 | ajaxSettings: { 78 | 79 | }, 80 | 81 | /** 82 | Gets or sets property value (depending on whether the 'value' parameter was passed or not) 83 | @param {String} propName 84 | @param {Object} [value] new value of the property 85 | */ 86 | property: function(propName, value) { 87 | if(value === undefined) { 88 | return this[propName]; 89 | } else { 90 | this[propName] = value; 91 | } 92 | } 93 | }; 94 | 95 | return XF; 96 | }); 97 | -------------------------------------------------------------------------------- /xf/src/xf.ui.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './xf.core', 3 | '../ui/xf.ui.core', 4 | '../ui/xf.ui.button', 5 | '../ui/xf.ui.checkboxradio', 6 | '../ui/xf.ui.fieldset', 7 | '../ui/xf.ui.footer', 8 | '../ui/xf.ui.header', 9 | '../ui/xf.ui.list', 10 | '../ui/xf.ui.loader', 11 | '../ui/xf.ui.popup', 12 | '../ui/xf.ui.scrollable', 13 | '../ui/xf.ui.slidemenu', 14 | '../ui/xf.ui.tabs', 15 | '../ui/xf.ui.textinput' 16 | ], function(XF) { 17 | 18 | return XF; 19 | }); 20 | -------------------------------------------------------------------------------- /xf/src/xf.utils.js: -------------------------------------------------------------------------------- 1 | define([ 2 | './xf.core', 3 | 'underscore', 4 | './xf.device' 5 | ], function(XF, _) { 6 | 7 | /** 8 | @namespace Holds all the reusable util functions 9 | */ 10 | XF.utils = { 11 | uniqueID: function() { 12 | return 'xf-' + Math.floor(Math.random() * 100000); 13 | } 14 | }; 15 | 16 | return XF; 17 | }); -------------------------------------------------------------------------------- /xf/src/xf.zepto.support.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | if (!_.isFunction($.fn.detach)) { 4 | $.fn.detach = function(a) { 5 | return this.remove(a,!0); 6 | }; 7 | } 8 | 9 | if (!_.isFunction($.fn.wrapInner)) { 10 | $.fn.wrapInner = function( html ) { 11 | if ( _.isFunction( html ) ) { 12 | return this.each(function(i) { 13 | $(this).wrapInner( html.call(this, i) ); 14 | }); 15 | } 16 | 17 | return this.each(function() { 18 | var self = $( this ), 19 | contents = self.contents(); 20 | 21 | if ( contents.length ) { 22 | contents.wrapAll( html ); 23 | 24 | } else { 25 | self.append( html ); 26 | } 27 | }); 28 | }; 29 | } 30 | 31 | var _olddetach = $.fn.detach; 32 | /** @ignore */ 33 | $.fn.detach = function() { 34 | var parent = $(this).parent(); 35 | var res = _olddetach.apply(this,arguments); 36 | parent.trigger('detach'); 37 | return res; 38 | }; 39 | 40 | /** @ignore */ 41 | /** Cannot use $.fn.extend because of Zepto support **/ 42 | $.fn.outerHtml = function(replacement) { 43 | 44 | if(replacement) { 45 | return this.each(function(){ 46 | $(this).replaceWith(replacement); 47 | }); 48 | } 49 | 50 | var tmp_node = $('
').append( $(this).clone() ); 51 | var markup = tmp_node.html(); 52 | 53 | tmp_node.remove(); 54 | return markup; 55 | }; 56 | -------------------------------------------------------------------------------- /xf/ui/xf.ui.checkboxradio.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | 'underscore', 4 | '../src/xf.core', 5 | '../src/xf.utils', 6 | '../ui/xf.ui.core' 7 | ], function($, _, XF) { 8 | 9 | /** 10 | Enhances checkbox or radio button input view 11 | */ 12 | XF.ui.checkboxRadio = { 13 | 14 | // Selectors will be used to detect checkboxes' and radios' on the page 15 | selector : 'INPUT[type=checkbox], INPUT[type=radio]', 16 | 17 | render : function(chbRbInput) { 18 | 19 | var jQChbRbInput = $(chbRbInput), 20 | options = { 21 | id : '', 22 | input : '', 23 | wrapperClass : '', 24 | labelClass : '', 25 | labelFor : '', 26 | isSwitch : false, 27 | label : '' 28 | }; 29 | 30 | if (!chbRbInput || !(jQChbRbInput instanceof $) || jQChbRbInput.attr('data-skip-enhance') == 'true') { 31 | return; 32 | } 33 | 34 | jQChbRbInput.attr({'data-skip-enhance':true}); 35 | options.id = jQChbRbInput.attr('id') || XF.utils.uniqueID(); 36 | options.input = jQChbRbInput.wrap("").parent().html(); 37 | jQChbRbInput.attr('id', options.id); 38 | var chbRbInputLabel = $('label[for=' + options.id + ']'); 39 | 40 | // If the input doesn't have an associated label, quit 41 | if (chbRbInputLabel.length) { 42 | 43 | var typeValue = jQChbRbInput.attr('type').toLowerCase(), 44 | wrapper = $('
'), 45 | isSwitch = options.isSwitch = jQChbRbInput.attr('data-role') == 'switch'; 46 | 47 | if (!isSwitch) { 48 | options.wrapperClass = 'xf-input-' + typeValue; 49 | options.labelClass = 'xf-input-positioner'; 50 | chbRbInputLabel.addClass('xf-input-label'); 51 | } else { 52 | options.wrapperClass = 'xf-switch'; 53 | options.labelClass = 'xf-switch-control'; 54 | chbRbInputLabel.addClass('xf-switch-label'); 55 | } 56 | wrapper.append(chbRbInputLabel); 57 | options.labelFor = chbRbInputLabel.wrap("").parent().html(); 58 | 59 | // Underscore template for label and element 60 | var _template = _.template( 61 | '
<%= options.labelFor %>
' 68 | ); 69 | jQChbRbInput.parent().html(_template({options : options})); 70 | } 71 | } 72 | }; 73 | 74 | }); 75 | -------------------------------------------------------------------------------- /xf/ui/xf.ui.core.js: -------------------------------------------------------------------------------- 1 | define([ 2 | '../src/xf.core', 3 | 'jquery', 4 | 'underscore' 5 | ], function(XF, $, _) { 6 | 7 | /** 8 | @namespace Holds all the logic related to ui elements enhancement 9 | */ 10 | XF.ui = {}; 11 | 12 | _.extend(XF.ui, /** @lends XF.ui */ { 13 | 14 | init: function () { 15 | XF.on('ui:enhance', _.bind(XF.ui.enhance, XF.ui)); 16 | }, 17 | 18 | /** 19 | Reworks markup of a givven $ object 20 | @param jqObj $ item 21 | */ 22 | 23 | enhance : function (jqObj) { 24 | if (!(jqObj instanceof $)) { 25 | jqObj = $(jqObj); 26 | 27 | if (!jqObj instanceof $) { 28 | return; 29 | } 30 | } 31 | 32 | _.each(XF.ui, function (enhancement, index) { 33 | 34 | if (typeof enhancement === 'object' && enhancement.hasOwnProperty('selector')) { 35 | 36 | jqObj.find(enhancement.selector).not('[data-skip-enhance=true]').each(function (){ 37 | var skip = false; 38 | 39 | _.each(XF.ui.enhanced, function (elem, index) { 40 | 41 | if (XF.ui.enhanced[index] === this) { 42 | skip = true; 43 | } 44 | }); 45 | 46 | if (!skip & $(this).attr('data-skip-enhance') != 'true') { 47 | var options = $(this).data(); 48 | XF.ui.enhanced.push(this); 49 | enhancement.render(this, options); 50 | } 51 | }); 52 | } 53 | }); 54 | 55 | }, 56 | 57 | /** 58 | A list of objects already enhanced (used to skip them while iterating through DOM) 59 | @type Array 60 | @private 61 | */ 62 | enhanced : [], 63 | 64 | issetElements : [], 65 | 66 | checkInIsset : function (type, id) { 67 | type = type || ''; 68 | id = id || ''; 69 | var result = []; 70 | 71 | for (var i in this.issetElements) { 72 | 73 | if (id === '') { 74 | 75 | if (this.issetElements[i].type === type) { 76 | result.push(this.issetElements[i].id); 77 | } 78 | } else { 79 | 80 | if (this.issetElements[i].type === type && this.issetElements[i].id === id) { 81 | result.push(this.issetElements[i].id); 82 | } 83 | } 84 | } 85 | 86 | return result; 87 | }, 88 | 89 | removeFromIsset : function (type, id) { 90 | type = type || ''; 91 | id = id || ''; 92 | var result = []; 93 | 94 | for (var i in this.issetElements) { 95 | 96 | if (id === '') { 97 | 98 | if (this.issetElements[i].type !== type) { 99 | result.push(this.issetElements[i]); 100 | } 101 | } else { 102 | 103 | if (this.issetElements[i].type !== type && this.issetElements[i].id !== id) { 104 | result.push(this.issetElements[i]); 105 | } 106 | } 107 | } 108 | 109 | this.issetElements = result; 110 | } 111 | 112 | }); 113 | 114 | return XF; 115 | }); 116 | -------------------------------------------------------------------------------- /xf/ui/xf.ui.fieldset.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | 'underscore', 4 | '../src/xf.core', 5 | '../src/xf.utils', 6 | '../ui/xf.ui.core' 7 | ], function($, _, XF) { 8 | 9 | /** 10 | Enhances fieldset view 11 | */ 12 | XF.ui.fieldset = { 13 | 14 | // Selectors will be used to detect filedsets on the page 15 | selector : 'fieldset[data-role=controlgroup]', 16 | 17 | render : function(fieldset, options) { 18 | var jQFieldset = $(fieldset); 19 | 20 | if (!fieldset || !(jQFieldset instanceof $) || jQFieldset.attr('data-skip-enhance') == 'true') { 21 | return; 22 | } 23 | 24 | var id = jQFieldset.attr('id') || XF.utils.uniqueID(); 25 | 26 | jQFieldset.attr({'data-skip-enhance': true, 'id' : id}); 27 | 28 | // If the inputs have a parent fieldset[data-role=controlgroup], the fieldset 29 | // is assigned a class xf-controlgroup, 30 | 31 | jQFieldset.addClass('xf-controlgroup'); 32 | 33 | // If there's a legend element inside the fieldset, it becomes div.xf-label 34 | var legend = jQFieldset.children('legend').detach(); 35 | 36 | // the inputs are also wrapped in a div.xf-controlgroup-controls 37 | jQFieldset.wrapInner('
'); 38 | jQFieldset.prepend(legend); 39 | 40 | if (legend.length) { 41 | var legendDiv = $('
'); 42 | var newLegendAttrs = {}; 43 | 44 | _.each(legend[0].attributes, function (attribute) { 45 | newLegendAttrs[attribute.name] = attribute.value; 46 | }); 47 | legendDiv.attr(newLegendAttrs).addClass('xf-label').html(legend.html()); 48 | if (legend.hasOwnProperty('outerHTML')) { 49 | legend.outerHtml(legendDiv.outerHtml()); 50 | } 51 | } 52 | } 53 | }; 54 | 55 | }); 56 | -------------------------------------------------------------------------------- /xf/ui/xf.ui.footer.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | 'underscore', 4 | '../src/xf.core', 5 | '../src/xf.utils', 6 | '../ui/xf.ui.core' 7 | ], function($, _, XF) { 8 | 9 | /** 10 | Enhances footers view 11 | */ 12 | XF.ui.footer = { 13 | 14 | // Selectors will be used to detect footer's element on the page 15 | selector : 'footer, [data-role=footer]', 16 | 17 | render : function (footer, options) { 18 | var jQFooter = $(footer), 19 | _self = this; 20 | 21 | if (!footer || !(jQFooter instanceof $) || jQFooter.attr('data-skip-enhance') == 'true') { 22 | return; 23 | } 24 | 25 | options.id = options.id || XF.utils.uniqueID(); 26 | 27 | jQFooter.attr({ 28 | 'data-id': options.id, 29 | 'id': options.id, 30 | 'data-role' : 'footer', 31 | 'data-skip-enhance' : 'true' 32 | }); 33 | 34 | // detect if data-fixed is true 35 | options.fixed = options.fixed === true ? true : false; 36 | options.buttons = options.buttons || []; 37 | 38 | var parentPages = $(this.selector).parents('.xf-page'), 39 | siblingPages = $(this.selector).siblings('.xf-page'); 40 | 41 | if (!_.isEmpty(parentPages) && options.isFixed) { 42 | parentPages.addClass('xf-has-footer'); 43 | } 44 | 45 | if (!_.isEmpty(siblingPages)) { 46 | siblingPages.addClass('xf-has-footer'); 47 | } 48 | 49 | // selects buttons inside of footer 50 | var buttons = jQFooter.find(XF.ui.button.selector); 51 | options.buttonsClass = 'xf-grid-unit-1of' + buttons.length; 52 | 53 | for (var i = 0; i < buttons.length; ++i) { 54 | var button = buttons.eq(i); 55 | var butOpts = { 56 | iconClass : button.attr('data-icon') ? 'xf-icon-' + button.attr('data-icon') : '', 57 | dataHrefString : button.attr('data-href') ? button.attr('data-href') : '', 58 | textClass : button.attr('data-text-class') ? button.attr('data-text-class') : '', 59 | id : button.attr('data-id') ? button.attr('data-id') : options.id + '-item' + i, 60 | text : button.val() || button.text() || '' 61 | }; 62 | options.buttons.push(butOpts); 63 | } 64 | 65 | XF.router.on('route', function () { 66 | XF.ui.footer.selectButton(jQFooter); 67 | }); 68 | 69 | // Underscore template for footer 70 | var _template = _.template( 71 | '' 83 | ); 84 | 85 | jQFooter.html(_template(options)); 86 | 87 | XF.ui.footer.selectButton(jQFooter); 88 | }, 89 | 90 | // detect if button is active 91 | selectButton : function (el) { 92 | var page = XF.history.fragment; 93 | el.find('.xf-nav a').removeClass('xf-nav-item-active'); 94 | el.find('.xf-nav a[data-href="#' + page + '"]').addClass('xf-nav-item-active'); 95 | } 96 | }; 97 | 98 | }); 99 | -------------------------------------------------------------------------------- /xf/ui/xf.ui.header.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | 'underscore', 4 | '../src/xf.core', 5 | '../src/xf.utils', 6 | '../ui/xf.ui.core' 7 | ], function($, _, XF) { 8 | 9 | /** 10 | Enhances headers view 11 | */ 12 | XF.ui.header = { 13 | 14 | // Selectors will be used to detect header's element on the page 15 | selector : '[data-role=header]', 16 | 17 | render : function (header, options) { 18 | var jQHeader = $(header); 19 | 20 | if (!header || !(jQHeader instanceof $) || jQHeader.attr('data-skip-enhance') == 'true') { 21 | return; 22 | } 23 | 24 | // Detect if we have title 25 | var headerTitle = jQHeader.find('h1'); 26 | if (headerTitle.length > 0) { 27 | headerTitle.addClass('xf-header-title'); 28 | } 29 | 30 | // Set up options 31 | options.id = options.id || XF.utils.uniqueID(); 32 | options.title = options.title || ''; 33 | options.html = jQHeader.html(); 34 | options.isFixed = (options.fixed && options.fixed === true) ? true : false; 35 | 36 | var parentPages = $(this.selector).parents('.xf-page'), 37 | siblingPages = $(this.selector).siblings('.xf-page'); 38 | 39 | // Add additional class for parent node 40 | if (!_.isEmpty(parentPages) && options.isFixed) { 41 | parentPages.addClass('xf-has-header'); 42 | } 43 | 44 | // Add additional class for siblings 45 | if (!_.isEmpty(siblingPages)) { 46 | siblingPages.addClass('xf-has-header'); 47 | } 48 | 49 | jQHeader.attr({ 50 | 'data-id': options.id, 51 | 'id': options.id, 52 | 'data-skip-enhance' : 'true' 53 | }); 54 | 55 | // Underscore template for header 56 | var _template = _.template( 57 | '
' + 58 | '<%= html %>' + 59 | '
' 60 | ); 61 | 62 | jQHeader.html(_template(options)); 63 | } 64 | }; 65 | 66 | }); 67 | -------------------------------------------------------------------------------- /xf/ui/xf.ui.loader.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | '../src/xf.core', 4 | '../src/xf.utils', 5 | '../ui/xf.ui.core' 6 | ], function($, XF) { 7 | 8 | /** 9 | Enhances loaders view 10 | */ 11 | XF.ui.loader = { 12 | 13 | // Selectors will be used to detect loader's element on the page 14 | selector : '[data-role=loader]', 15 | 16 | render : function (loader, options) { 17 | 18 | var jqLoader = $(loader), 19 | _self = this; 20 | 21 | if (!options) { 22 | options = {}; 23 | } 24 | 25 | if (!loader || !(jqLoader instanceof $) || jqLoader.attr('data-skip-enhance') == 'true') { 26 | return; 27 | } 28 | 29 | 30 | var id = jqLoader.attr('id') || XF.utils.uniqueID(), 31 | idStack = XF.ui.checkInIsset('loader'), 32 | newId = false; 33 | 34 | // Check if locader with the same ID was created before 35 | for (var i in idStack) { 36 | 37 | if (newId) { 38 | 39 | if (!$('#' + idStack[i]).length) { 40 | id = idStack[i]; 41 | newId = true; 42 | } 43 | } 44 | } 45 | 46 | // If 'no', add new ID to the stack 47 | if (!newId) { 48 | XF.ui.issetElements.push({type : 'loader', id : id}); 49 | } 50 | 51 | jqLoader.attr({'id': id, 'data-skip-enhance' : 'true'}); 52 | 53 | if (!$('#' + id).hasClass('xf-loader')) { 54 | $('#' + id).addClass('xf-loader'); 55 | } 56 | 57 | return jqLoader; 58 | }, 59 | 60 | // Show loader or create newone and show it 61 | show : function (jqLoader) { 62 | jqLoader = jqLoader || this.create(); 63 | jqLoader.show(); 64 | }, 65 | 66 | // Hide loader or hide all 67 | hide : function (jqLoader) { 68 | jqLoader = jqLoader || null; 69 | if (jqLoader === null) { 70 | $('.xf-loader').hide(); 71 | } else { 72 | jqLoader.hide(); 73 | } 74 | }, 75 | 76 | // Remove loader's dom-element 77 | remove : function (jqLoader) { 78 | jqLoader.detach(); 79 | XF.ui.removeFromIsset('popup', jqLoader.attr('id')); 80 | }, 81 | 82 | // Add new loader to the page 83 | create : function () { 84 | var jqLoader = $('
'); 85 | XF.device.getViewport().append(jqLoader); 86 | return this.render(jqLoader[0]); 87 | } 88 | }; 89 | 90 | }); 91 | -------------------------------------------------------------------------------- /xf/ui/xf.ui.scrollable.js: -------------------------------------------------------------------------------- 1 | define([ 2 | 'jquery', 3 | '../src/xf.core', 4 | '../src/xf.utils', 5 | '../ui/xf.ui.core' 6 | ], function($, XF) { 7 | 8 | /** 9 | Add scrolling functionality 10 | */ 11 | XF.ui.scrollable = { 12 | 13 | selector : '[data-scrollable=true]', 14 | 15 | render : function (scrollable) { 16 | 17 | var jQScrollable = $(scrollable); 18 | if (!scrollable || !(jQScrollable instanceof $) || jQScrollable.attr('data-skip-enhance') == 'true') { 19 | return; 20 | } 21 | 22 | var id = jQScrollable.attr('id') || XF.utils.uniqueID(); 23 | 24 | jQScrollable.attr({'data-skip-enhance':true, 'id' : id}); 25 | 26 | var children = jQScrollable.children(); 27 | 28 | // Always create wrapper 29 | if (children.length == 1 && false) { 30 | children.addClass('xf-scrollable-content'); 31 | } else { 32 | jQScrollable.append( 33 | $('
') 34 | .addClass('xf-scrollable-content') 35 | .append(children) 36 | ); 37 | } 38 | 39 | var wrapperId = jQScrollable.attr('id'); 40 | 41 | if (!wrapperId || wrapperId === '') { 42 | wrapperId = 'xf_scrollable_' + new Date().getTime(); 43 | jQScrollable.attr({'id':wrapperId}); 44 | } 45 | 46 | // Use iScroll 47 | var ISItem = jQScrollable.data('iscroll', new iScroll(wrapperId)); 48 | var wrapperChanged = false; 49 | 50 | var doRefreshIScroll = function () { 51 | 52 | if (wrapperChanged) { 53 | wrapperChanged = false; 54 | ISItem.data('iscroll').refresh(); 55 | bindHanlders(); 56 | } 57 | }; 58 | 59 | var needRefreshIScroll = function (){ 60 | 61 | if ($.contains($('#' + wrapperId)[0], this)) { 62 | wrapperChanged = true; 63 | setTimeout(doRefreshIScroll, 100); 64 | } 65 | }; 66 | 67 | // Bind hadlers to the scrollable element 68 | var bindHanlders = function () { 69 | $('#' + wrapperId + ' *') 70 | .bind('detach', needRefreshIScroll) 71 | .bind('hide', needRefreshIScroll) 72 | .bind('show', needRefreshIScroll) 73 | .bind('append', needRefreshIScroll) 74 | .bind('prepend', needRefreshIScroll) 75 | .bind('html', needRefreshIScroll) 76 | .bind('resize', needRefreshIScroll); 77 | }; 78 | bindHanlders(); 79 | } 80 | }; 81 | 82 | }); 83 | --------------------------------------------------------------------------------