├── .gitignore
├── .jscsrc
├── .jshintrc
├── LICENSE
├── README.md
├── cordova
├── config.xml
└── res
│ ├── android
│ ├── mipmap-hdpi
│ │ └── ic_launcher.png
│ ├── mipmap-ldpi
│ │ └── ic_launcher.png
│ ├── mipmap-mdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ │ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ │ └── ic_launcher.png
│ └── playstore-icon.png
│ └── ios
│ ├── README.md
│ └── icons
│ ├── Contents.json
│ ├── Icon-40.png
│ ├── Icon-40@2x.png
│ ├── Icon-40@3x.png
│ ├── Icon-60.png
│ ├── Icon-60@2x.png
│ ├── Icon-60@3x.png
│ ├── Icon-72.png
│ ├── Icon-72@2x.png
│ ├── Icon-76.png
│ ├── Icon-76@2x.png
│ ├── Icon-83.5@2x.png
│ ├── Icon-Small-50.png
│ ├── Icon-Small-50@2x.png
│ ├── Icon-Small.png
│ ├── Icon-Small@2x.png
│ ├── Icon-Small@3x.png
│ ├── Icon.png
│ ├── Icon@2x.png
│ ├── iTunesArtwork.png
│ └── iTunesArtwork@2x.png
├── images
├── icons
│ ├── android
│ │ ├── external_link@1x.png
│ │ ├── external_link@2x.png
│ │ ├── info@1x.png
│ │ ├── info@2x.png
│ │ ├── more@1x.png
│ │ ├── more@2x.png
│ │ ├── share@1x.png
│ │ └── share@2x.png
│ └── ios
│ │ ├── external_link@1x.png
│ │ ├── external_link@2x.png
│ │ ├── home@1x.png
│ │ ├── home@2x.png
│ │ ├── home_full@1x.png
│ │ ├── home_full@2x.png
│ │ ├── info@1x.png
│ │ ├── info@2x.png
│ │ ├── info_full@1x.png
│ │ ├── info_full@2x.png
│ │ ├── like@1x.png
│ │ ├── like@2x.png
│ │ ├── more@1x.png
│ │ ├── more@2x.png
│ │ ├── search@1x.png
│ │ ├── search@2x.png
│ │ ├── share@1x.png
│ │ ├── share@2x.png
│ │ ├── user@1x.png
│ │ └── user@2x.png
├── tabrisjs_logo@1.5x.png
├── tabrisjs_logo@1x.png
├── tabrisjs_logo@2x.png
└── tabrisjs_logo@3x.png
├── index.js
├── package.json
└── src
├── app
├── components
│ ├── feed_showcase.js
│ ├── image_slider.js
│ └── item_list.js
├── helpers
│ ├── icon.js
│ ├── img_resize.js
│ ├── link.js
│ └── sizing.js
├── pages
│ ├── about.js
│ ├── item_details.js
│ ├── item_list.js
│ └── main.js
└── styles
│ └── theme.js
├── config.js
└── services
├── rss
├── feed_helpers.js
├── feeds.js
└── index.js
├── shop
├── feeds.js
└── index.js
├── shop_fashion
├── feeds.js
└── index.js
├── wordpress_eclipsesource
├── feed_helpers.js
├── feeds.js
└── index.js
└── wordpress_pets
├── feeds.js
└── index.js
/.gitignore:
--------------------------------------------------------------------------------
1 | ### Node template
2 | # Logs
3 | logs
4 | *.log
5 | npm-debug.log*
6 |
7 | # Runtime data
8 | pids
9 | *.pid
10 | *.seed
11 |
12 | # Directory for instrumented libs generated by jscoverage/JSCover
13 | lib-cov
14 |
15 | # Coverage directory used by tools like istanbul
16 | coverage
17 |
18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
19 | .grunt
20 |
21 | # node-waf configuration
22 | .lock-wscript
23 |
24 | # Compiled binary addons (http://nodejs.org/api/addons.html)
25 | build/Release
26 |
27 | # Dependency directory
28 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git
29 | node_modules
30 | ### Example user template template
31 | ### Example user template
32 |
33 | # IntelliJ project files
34 | .idea
35 | *.iml
36 | out
37 | gen
38 | # Created by .ignore support plugin (hsz.mobi)
39 |
--------------------------------------------------------------------------------
/.jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "maximumLineLength": {
3 | "value": 120,
4 | "allowComments": true,
5 | "allowRegex": true
6 | },
7 | "validateIndentation": 2,
8 | "validateLineBreaks": "LF",
9 | "validateQuoteMarks": { "mark": "\"", "escape": true },
10 | "requireLineFeedAtFileEnd": true,
11 | "disallowTrailingWhitespace": true,
12 | "disallowMultipleLineStrings": true,
13 | "disallowMixedSpacesAndTabs": true,
14 | "disallowSpaceAfterPrefixUnaryOperators": true,
15 | "disallowSpaceBeforePostfixUnaryOperators": true,
16 | "validateParameterSeparator": ", ",
17 | "requireCurlyBraces": [
18 | "if",
19 | "else",
20 | "for",
21 | "while",
22 | "do",
23 | "try",
24 | "catch"
25 | ],
26 | "disallowKeywords": ["with"],
27 | "requireOperatorBeforeLineBreak": true,
28 | "requireSpaceAfterKeywords": [
29 | "if",
30 | "else",
31 | "for",
32 | "while",
33 | "do",
34 | "switch",
35 | "return",
36 | "try",
37 | "catch"
38 | ],
39 | "requireSpaceBeforeBinaryOperators": true,
40 | "requireSpaceAfterBinaryOperators": true,
41 | "requireSpacesInConditionalExpression": true,
42 | "requireSpaceBeforeBlockStatements": true,
43 | "requireSpaceBeforeObjectValues": true,
44 | "disallowSpaceAfterObjectKeys": true,
45 | "requireSpacesInFunctionExpression": {
46 | "beforeOpeningCurlyBrace": true
47 | },
48 | "disallowSpacesInAnonymousFunctionExpression": {
49 | "beforeOpeningRoundBrace": true
50 | },
51 | "disallowSpacesInsideObjectBrackets": "all",
52 | "disallowSpacesInsideArrayBrackets": "all",
53 | "disallowSpacesInsideParentheses": true,
54 | "disallowMultipleLineBreaks": true,
55 | "disallowKeywordsOnNewLine": ["else"],
56 | "disallowTrailingComma": true,
57 | "requireBlocksOnNewline": 1
58 | }
59 |
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 |
3 | // Enforcing Options
4 |
5 | "curly": true, // enforce curly braces around blocks in loops and conditionals
6 | "eqeqeq": true, // prohibit the use of == and != in favor of === and !==
7 | "freeze": true, // prohibit overwriting prototypes of native objects such as Array and Date
8 | "immed": true, // prohibit the use of immediate function invocations without wrapping them in parentheses
9 | "latedef": "nofunc", // prohibit the use of a variable before it was defined
10 | "newcap": true, // enforce capitalized names of constructor functions
11 | "noarg": true, // prohibit the use of arguments.caller and arguments.callee
12 | "nonew": true, // prohibit the use of constructor functions for side-effects
13 | "undef": true, // prohibit the use of explicitly undeclared variables
14 | "unused": true, // prohibit the declaration of variables that are never used
15 |
16 | // Relaxing Options
17 |
18 | "shadow": true, // tolerate variable shadowing, i.e. re-define variables that have already be defined
19 | "eqnull": true, // suppress warnings about == null comparisons
20 |
21 | // Globals
22 |
23 | "browser": true, // globals exposed by modern browsers e.g. `window`, `document`, etc.
24 | "globals": {
25 | "console": false,
26 | "tabris": false,
27 | "util": false,
28 | "device": false,
29 | "NativeBridgeSpy": false,
30 | "Promise": false,
31 |
32 | // modules
33 | "module": false,
34 | "exports": false,
35 | "require": false,
36 |
37 | "cordova": false,
38 |
39 |
40 |
41 | // Node
42 | "__dirname" : false,
43 | "global" : false,
44 | "Buffer" : false
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Eclipsesource
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Tabris.js template app
2 |
3 |
4 | [*Tabris.js*](https://tabrisjs.com) is an amazing way to create native mobile apps in the new age. It combines native platform widgets – performance plus look & feel, with the simplicity, elegance, and power of a single JavaScript codebase for iOS, Android and Windows 10 (coming soon). It is the only native platform UI, javascript runtime framework that lets you use Cordova plugins to tap into the device capabilities.
5 |
6 | > Watch this intro video that explains the easy set up and features:
7 | >
8 |
9 | Moreover, Tabris.js also offers some unique advantages to developers, like a cloud build service and developer apps for iOS and Android. This means you can develop iOS apps without owning a Mac or installing Xcode (but need an iPhone / iPad). Likewise, you can develop Android apps without downloading Android studio and the Android SDKs (but need an Android phone / tablet).
10 |
11 | # About
12 |
13 | This repo serves as a template for starting a Tabris.js application.
14 | The same UI can accept different data sources and configurations, this repo has 7 apps in 1 codebase:
15 |
16 | * rss,
17 | * rss_showcase,
18 | * shop,
19 | * shop_showcase,
20 | * shop_fashion,
21 | * wordpress_pets,
22 | * wordpress_eclipsesource,
23 |
24 | Expect many updates and improvements soon!
25 |
26 | The examples apps work in phones and tablets - both for iOS and Android. The example visuals are with and iPhone 6s and a 10" Android tablet:
27 |
28 |
29 |
RSS Example:
30 |
31 |
32 |
33 |
34 |
35 | Ecommerce Example:
36 |
37 |
38 |
39 |
40 |
41 | # Setup
42 | Seting up is really easy (you just need node installed):
43 |
44 | ```shell
45 | # Make http-server available globally
46 | $ npm install -g http-server
47 |
48 | # Clone this repo
49 | $ git clone git@github.com:eclipsesource/tabris-js-template-app.git
50 | $ cd tabris-js-template-app
51 |
52 | # Npm install dependencies
53 | $ npm install
54 |
55 | # Fire up the server that your device will connect to
56 | $ npm start
57 | ```
58 |
59 | Then, connect to your code from the Tabris.js Developer App ([*Play Store*](https://play.google.com/store/apps/details?id=com.eclipsesource.tabris.js) / [*App Store*](https://itunes.apple.com/us/app/tabris.js/id939600018?ls=1&mt=8)).
60 |
61 |
62 | # Contribute
63 | Add Some fancy behaviour like:
64 |
65 | * [ ] Localstorage for favorites
66 | * [x] Sharing with cordova plugin
67 | * [ ] Youtube / Vimeo data service
68 | * [ ] Login functionality
69 | * [ ] Push notifications
70 | * [ ] Rate app functionality
71 |
72 | For any questions ping me: shai@eclipsesource.com
73 |
74 | # Thanks
75 |
76 | - [Carlos Lopez](https://github.com/carloslopez1990/tabrisjs-rss-reader-example) - For the initial RSS idea and codebase
77 | - [Eddy Verbruggen](https://github.com/EddyVerbruggen) - For the Social Sharing & Actionsheet Cordova plugins used here
78 |
--------------------------------------------------------------------------------
/cordova/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
66 |
--------------------------------------------------------------------------------
/cordova/res/android/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/android/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/cordova/res/android/mipmap-ldpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/android/mipmap-ldpi/ic_launcher.png
--------------------------------------------------------------------------------
/cordova/res/android/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/android/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/cordova/res/android/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/android/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/cordova/res/android/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/android/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/cordova/res/android/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/android/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/cordova/res/android/playstore-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/android/playstore-icon.png
--------------------------------------------------------------------------------
/cordova/res/ios/README.md:
--------------------------------------------------------------------------------
1 | ## iTunesArtwork & iTunesArtwork@2x (App Icon) file extension:
2 |
3 | PNG extension is prepended to these two files -
4 |
5 | While Apple suggested to omit the extension for these files,
6 | the '.png' extension is actually required for iTunesConnect submission.
7 |
8 | This is done for you so you don't have to.
9 |
10 | However, for Ad_hoc or Enterprise distirbution, the extension should be removed
11 | from the files before adding to XCode to avoid error.
12 |
13 | refs: https://developer.apple.com/library/ios/qa/qa1686/_index.html
14 |
15 | ## iTunesArtwork & iTunesArtwork@2x (App Icon) transparency handling:
16 |
17 | As images with alpha channels or transparencies cannot be set as an application's icon on
18 | iTunesConnect, all transparent pixels in your images will be converted into
19 | solid blacks.
20 |
21 | To achieve the best result, you're advised to adjust the transparency settings
22 | in your source files before converting them with makeAppIcon.
23 |
24 | refs: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/AppIcons.html
25 |
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x",
7 | "filename" : "Icon-Small@2x.png"
8 | },
9 | {
10 | "idiom" : "iphone",
11 | "size" : "29x29",
12 | "scale" : "3x",
13 | "filename" : "Icon-Small@3x.png"
14 | },
15 | {
16 | "idiom" : "iphone",
17 | "size" : "40x40",
18 | "scale" : "2x",
19 | "filename" : "Icon-40@2x.png"
20 | },
21 | {
22 | "idiom" : "iphone",
23 | "size" : "40x40",
24 | "scale" : "3x",
25 | "filename" : "Icon-40@3x.png"
26 | },
27 | {
28 | "idiom" : "iphone",
29 | "size" : "60x60",
30 | "scale" : "2x",
31 | "filename" : "Icon-60@2x.png"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "size" : "60x60",
36 | "scale" : "3x",
37 | "filename" : "Icon-60@3x.png"
38 | },
39 | {
40 | "idiom" : "ipad",
41 | "size" : "29x29",
42 | "scale" : "1x",
43 | "filename" : "Icon-Small.png"
44 | },
45 | {
46 | "idiom" : "ipad",
47 | "size" : "29x29",
48 | "scale" : "2x",
49 | "filename" : "Icon-Small@2x.png"
50 | },
51 | {
52 | "idiom" : "ipad",
53 | "size" : "40x40",
54 | "scale" : "1x",
55 | "filename" : "Icon-40.png"
56 | },
57 | {
58 | "idiom" : "ipad",
59 | "size" : "40x40",
60 | "scale" : "2x",
61 | "filename" : "Icon-40@2x.png"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "size" : "76x76",
66 | "scale" : "1x",
67 | "filename" : "Icon-76.png"
68 | },
69 | {
70 | "idiom" : "ipad",
71 | "size" : "76x76",
72 | "scale" : "2x",
73 | "filename" : "Icon-76@2x.png"
74 | },
75 | {
76 | "idiom" : "ipad",
77 | "size" : "83.5x83.5",
78 | "scale" : "2x",
79 | "filename" : "Icon-83.5@2x.png"
80 | }
81 | ],
82 | "info" : {
83 | "version" : 1,
84 | "author" : "makeappicon"
85 | }
86 | }
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-40.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-40@2x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-40@3x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-60.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-60@2x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-60@3x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-72.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-72@2x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-76.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-76@2x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-83.5@2x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-Small-50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-Small-50.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-Small-50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-Small-50@2x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-Small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-Small.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-Small@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-Small@2x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon-Small@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon-Small@3x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/Icon@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/Icon@2x.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/iTunesArtwork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/iTunesArtwork.png
--------------------------------------------------------------------------------
/cordova/res/ios/icons/iTunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/cordova/res/ios/icons/iTunesArtwork@2x.png
--------------------------------------------------------------------------------
/images/icons/android/external_link@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/android/external_link@1x.png
--------------------------------------------------------------------------------
/images/icons/android/external_link@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/android/external_link@2x.png
--------------------------------------------------------------------------------
/images/icons/android/info@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/android/info@1x.png
--------------------------------------------------------------------------------
/images/icons/android/info@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/android/info@2x.png
--------------------------------------------------------------------------------
/images/icons/android/more@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/android/more@1x.png
--------------------------------------------------------------------------------
/images/icons/android/more@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/android/more@2x.png
--------------------------------------------------------------------------------
/images/icons/android/share@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/android/share@1x.png
--------------------------------------------------------------------------------
/images/icons/android/share@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/android/share@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/external_link@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/external_link@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/external_link@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/external_link@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/home@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/home@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/home@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/home@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/home_full@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/home_full@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/home_full@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/home_full@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/info@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/info@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/info@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/info@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/info_full@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/info_full@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/info_full@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/info_full@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/like@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/like@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/like@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/like@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/more@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/more@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/more@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/more@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/search@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/search@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/search@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/search@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/share@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/share@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/share@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/share@2x.png
--------------------------------------------------------------------------------
/images/icons/ios/user@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/user@1x.png
--------------------------------------------------------------------------------
/images/icons/ios/user@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/icons/ios/user@2x.png
--------------------------------------------------------------------------------
/images/tabrisjs_logo@1.5x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/tabrisjs_logo@1.5x.png
--------------------------------------------------------------------------------
/images/tabrisjs_logo@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/tabrisjs_logo@1x.png
--------------------------------------------------------------------------------
/images/tabrisjs_logo@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/tabrisjs_logo@2x.png
--------------------------------------------------------------------------------
/images/tabrisjs_logo@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/eclipsesource/tabris-js-template-app/6d46cf1670eb6334695a3d04ec1bb113950c194a/images/tabrisjs_logo@3x.png
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Tabris.js RSS Reader
2 | // This project was originally started by the awesome Tabris.js power user:
3 | // @author: Carlos Ernesto López
4 | // @contact: facebook.com/c.ernest.1990
5 | //
6 | // It was since modified in structure, and added extra functionality (like themes, custom image resolver, content sanitizers and more) by.
7 | // Shai Alon
8 | // https://github.com/shaialon
9 |
10 | // Add applauncher
11 | tabris.registerWidget("AppLauncher", {
12 | _type: "tabris.AppLauncher",
13 | openUrl: function(url) {
14 | this._nativeCall("openUrl", {url: url});
15 | }
16 | });
17 |
18 | // Run the first page
19 | require('./src/app/pages/main').open();
20 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Tabris.js Template App",
3 | "description": "7 apps in 1 with examples for multiple use cases.",
4 | "main": "index.js",
5 | "scripts": {
6 | "start": "http-server -c-1",
7 | "jshint": "jshint src test",
8 | "jscs": "jscs src test"
9 | },
10 | "dependencies": {
11 | "tabris": "^1.8.0",
12 | "lodash": "^4.6.1"
13 | },
14 | "devDependencies": {
15 | "jscs": "^2.11.0",
16 | "jshint": "^2.9.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/app/components/feed_showcase.js:
--------------------------------------------------------------------------------
1 | var getThemeStyle = require('./../styles/theme').getThemeStyle;
2 | var detailScreen = require('./../pages/item_details');
3 | var itemListPage = require('./../pages/item_list');
4 | var resizeImageURLByWidth = require('./../../app/helpers/img_resize').resizeImageURLByWidth;
5 | var updateUIColors = require('./../styles/theme').updateUIColors;
6 | var sizing = require('./../helpers/sizing');
7 |
8 | var config = require('./../../config.js').config;
9 | var getItems = config.dataService.getItems;
10 |
11 | var _ = require("lodash");
12 |
13 | var isTablet = sizing.isTablet;
14 | var imageWidth = Math.floor( isTablet ? tabris.device.get("screenWidth") * config.imgShowcaseScreenWidthRatio.tablet : tabris.device.get("screenWidth") * config.imgShowcaseScreenWidthRatio.phone );
15 | var imageHeightRatio = isTablet ? config.imgShowcaseSizeHeightToWidthRatio.tablet : config.imgShowcaseSizeHeightToWidthRatio.phone;
16 | var imageHeight = Math.floor(imageHeightRatio * imageWidth);
17 |
18 | var ITEMS_MARGIN = 16;
19 | var SHOWCASE_ITEMS = 10;
20 |
21 | module.exports = function( feedConfig , tab) {
22 | var style = cellStyle(feedConfig);
23 | var container = tabris.create("Composite", { left: 0, right: 0, top: "prev()", height: ( 103 + imageHeight )}).appendTo(tab);
24 |
25 | // Showcase header
26 | var header = tabris.create("Composite", style.header ).appendTo(container);
27 | tabris.create('TextView', style.headerText).appendTo(header);
28 | tabris.create('TextView', style.headerSeeAll).appendTo(header);
29 | header.on('touchend',function(){
30 | itemListPage.open(feedConfig.name,feedConfig);
31 | });
32 |
33 | // Showcase scroll
34 | var itemShowcase = tabris.create("ScrollView", style.itemShowcase).appendTo(container);
35 | tabris.create("Composite", style.itemShowcaseScrollHider).appendTo(container);
36 |
37 | tabris.create("Composite", style.divider).appendTo(container);
38 | refreshItems(itemShowcase);
39 |
40 | return container;
41 | };
42 |
43 |
44 | function cellStyle(feedConfig){
45 | var themeStyle = getThemeStyle(feedConfig.color);
46 | return {
47 | divider: { opacity: 0.1, background: themeStyle.showcase.textColor, layoutData: {left: ITEMS_MARGIN, height:1, bottom: 0, right: 0 }},
48 | itemShowcaseScrollHider: { layoutData: {left: 0, height:8, bottom: 0, right: 0}, background: "white"},
49 | itemShowcase: { layoutData: {left: 0, top:46, bottom: 0, right: 0}, direction: "horizontal", _feed: feedConfig},
50 | header: {left: 0, right: 0, height: 45, top:0, background: themeStyle.showcase.background},
51 | headerText: { maxLines: 1, font: '18px', left: ITEMS_MARGIN, right: 0, bottom: 10, text:feedConfig.name, textColor: themeStyle.showcase.textColor , alignment:'left' },
52 | headerSeeAll: { maxLines: 1, font: 'bold 10px', width: 100, right: ITEMS_MARGIN, bottom: 10, opacity: 0.7, text: "See All >", textColor: themeStyle.showcase.textColor , alignment:'right' }
53 | };
54 | }
55 |
56 |
57 | function refreshItems( widget ) {
58 | //updateWidgetLoading ( widget, true);
59 | var feedConfig = widget.get('_feed');
60 |
61 | getItems( feedConfig ).then( function(results){
62 |
63 | results.items.slice(0,Math.min(SHOWCASE_ITEMS,results.items.length)).forEach(function(feedItem, index){
64 | appendItemBox(widget,feedItem, tabris.device.get("screenWidth") < ((index) * (imageWidth + ITEMS_MARGIN)));
65 | });
66 | appendSeeAllBox(widget);
67 |
68 | }).catch(function(err){
69 | console.log("Failed fetching items for: "+ widget.get('_feed'));
70 | console.log(err);
71 | try {
72 | console.log(JSON.stringify(err));
73 | } catch (e){
74 |
75 | }
76 | });
77 | }
78 |
79 | function updateWidgetLoading(widget,loading){
80 | widget.set({
81 | refreshIndicator: loading,
82 | refreshMessage: loading ? "loading feed..." : ""
83 | });
84 | }
85 |
86 | function appendItemBox(widget , feedItem, delayed){
87 | var imageUrl = resizeImageURLByWidth(feedItem.image, imageWidth);
88 |
89 | if(imageUrl){
90 | var imgCell;
91 | var boxContainer = tabris.create('Composite', { left: ["prev()", ITEMS_MARGIN], width: imageWidth, top: 0, bottom: 0}).appendTo(widget);
92 | imgCell = { left: 0, right: 0, top: 0, height: imageHeight, scaleMode: 'fill' , background: "rgb(220, 220, 220)"};
93 | if(!delayed){
94 | imgCell.image = imageUrl;
95 | }
96 | var imgView = tabris.create('ImageView', imgCell).appendTo(boxContainer);
97 | if(delayed){
98 | setTimeout(function(){
99 | imgView.set({image:imageUrl});
100 | },1000);
101 | }
102 |
103 | if(feedItem.price_display){
104 | tabris.create('TextView', { text: feedItem.price_display,left: 0, right: 0, bottom: 13, height: 30, textColor: "#aaa", alignment:'center', maxLines:1}).appendTo(boxContainer);
105 | }
106 | else {
107 | tabris.create('TextView', { text: feedItem.title, left: 0, right: 0, bottom: 6, height: 40, textColor: "#aaa", alignment:'left', maxLines:2}).appendTo(boxContainer);
108 |
109 | }
110 | boxContainer.on('tap',function(){
111 | updateUIColors(widget.get('_feed').color);
112 | detailScreen.open(feedItem.title, feedItem);
113 |
114 | });
115 | }
116 | }
117 |
118 |
119 | function appendSeeAllBox(widget){
120 | var feedConfig = widget.get('_feed');
121 |
122 | var boxContainer = tabris.create('Composite', { left: ["prev()", 10], width: imageWidth + 40, top: 0, bottom: 0 }).appendTo(widget);
123 | var seeAllBox = tabris.create('Composite', { left: 20, right: 20, top: 0, height: imageHeight, background: feedConfig.color}).appendTo(boxContainer);
124 | tabris.create('TextView', { text: ''+ '' + "See All '" +feedConfig.name +"'" , maxLines: 2, font: '14px', left: 0, right: 20, bottom: 0, top: 0, textColor: "white", alignment:'center', markupEnabled:false}).appendTo(seeAllBox);
125 | tabris.create('TextView', { text: '>' , maxLines: 1, font: '20px', width: 14, right: 6, bottom: 0, top: 0, textColor: "white", alignment:'center'}).appendTo(seeAllBox);
126 |
127 | seeAllBox.on('tap',function(){
128 | itemListPage.open(feedConfig.name,feedConfig);
129 | });
130 | }
131 |
--------------------------------------------------------------------------------
/src/app/components/image_slider.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | var DOT_RADIUS = 6;
3 | var DOT_COLOR = 'black';
4 | var DOT_ROUND = true;
5 | var DEFAULT_OPTIONS = {
6 | DOT_RADIUS : DOT_RADIUS,
7 | DOT_COLOR : 'black',
8 | DOT_ROUND : true,
9 | DOT_CENTER : true,
10 | IMAGE_HEIGHT: 152
11 | };
12 |
13 |
14 | module.exports = function( items , options) {
15 |
16 | var config = _.extend(DEFAULT_OPTIONS, options);
17 | config.DOT_RADIUS_HALF = Math.floor(config.DOT_RADIUS / 2);
18 |
19 | var container = tabris.create("Composite", {
20 | left: 0, top: "prev()", right: 0, height: config.IMAGE_HEIGHT,
21 | });
22 | function itemSelected(item){
23 | container.trigger("itemSelected",item);
24 | }
25 |
26 | var tabFolder = tabris.create("TabFolder", {
27 | left: 0, top: "prev()", right: 0, bottom: 0,
28 | paging: true,
29 | background:'#ccc',
30 | tabBarLocation: "hidden"
31 | }).appendTo(container);
32 |
33 | var dotContainer = createDotContainer(config).appendTo(container);
34 |
35 | tabFolder.on("change:selection", function(widget, tab) {
36 | updateDots(tab.get('_index'),dotContainer);
37 | });
38 |
39 | items.forEach(function(item, index){
40 | addImageTab(item, tabFolder, itemSelected, index, config);
41 | addDot(dotContainer , config);
42 | });
43 |
44 | updateDots(0,dotContainer);
45 |
46 | return container;
47 | };
48 |
49 | function addImageTab(item, tabFolder, itemSelected, index, config) {
50 | var tab = tabris.create("Tab",{_index:index}).on("tap",function(){
51 | itemSelected(item);
52 | }).appendTo(tabFolder);
53 | tabris.create("ImageView", {
54 | centerX: 0, centerY: 0,
55 | height: config.IMAGE_HEIGHT,
56 | background:'#ccc',
57 | scaleMode:'fit',
58 | image:{src:item.image}
59 | }).appendTo(tab);
60 | }
61 |
62 |
63 | function createDotContainer(config) {
64 | var composite = {
65 | height: config.DOT_RADIUS,
66 | bottom: config.DOT_RADIUS
67 | };
68 | if(config.DOT_CENTER) {
69 | composite.centerX = 0;
70 | }
71 | else {
72 | composite.left = config.DOT_RADIUS_HALF;
73 | }
74 | return tabris.create("Composite", composite);
75 | }
76 |
77 | function addDot(container , config) {
78 | tabris.create("Composite", {
79 | class: "itemDot",
80 | left: ["prev()", config.DOT_RADIUS_HALF],
81 | height: config.DOT_RADIUS, width: config.DOT_RADIUS,
82 | cornerRadius: config.DOT_RADIUS_HALF,
83 | background: config.DOT_COLOR,
84 | }).appendTo(container);
85 | }
86 |
87 | function updateDots(activeIndex, container) {
88 | container.children(".itemDot").forEach(function(dot,index){
89 | if(index === activeIndex){
90 | dot.set('opacity',1);
91 | }
92 | else {
93 | dot.set('opacity',0.3);
94 | }
95 | });
96 | }
97 |
--------------------------------------------------------------------------------
/src/app/components/item_list.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 |
3 | var getThemeStyle = require('./../styles/theme').getThemeStyle;
4 | var detailScreen = require('./../pages/item_details');
5 | var resizeImageURLByWidth = require('./../../app/helpers/img_resize').resizeImageURLByWidth;
6 | var sizing = require('./../helpers/sizing');
7 |
8 |
9 | var config = require('./../../config.js').config;
10 | var getItems = config.dataService.getItems;
11 |
12 | var DEFAULTS = {
13 | CELLS_PER_ROW: 1,
14 | };
15 |
16 | module.exports = function( feedConfig , tab) {
17 |
18 | var CELLS_PER_ROW = calculateCellsPerRow(feedConfig);
19 | var CELL_SIZES = calculateCellSizes(feedConfig, CELLS_PER_ROW);
20 |
21 | var widget = tabris.create("CollectionView", {
22 | layoutData: {left: 0, top: 0, bottom: 0},
23 | elevation: 20,
24 | items: [],
25 | right: 0,
26 | itemHeight: CELL_SIZES.cellHeight,
27 | refreshEnabled: config.pullToRefresh,
28 | columnCount: CELLS_PER_ROW,
29 | _feed: feedConfig,
30 | _tab: tab,
31 | initializeCell: function(cell){
32 | var style = cellStyle(feedConfig);
33 | var elementsList = {};
34 | elementsList.container = tabris.create('Composite', style.container).appendTo(cell)
35 | elementsList.image = tabris.create('ImageView', style.image).appendTo(elementsList.container);
36 | elementsList.overlay = tabris.create('Composite', style.overlay).appendTo(elementsList.container);
37 | elementsList.title = tabris.create('TextView', style.title).appendTo(elementsList.container);
38 |
39 | cell.on("change:item", function(widget, feedItem) {
40 | feedItem._elementsList = elementsList;
41 | updateCellItemElements(feedItem, elementsList, CELL_SIZES);
42 | });
43 | }
44 | })
45 | .on("scroll", function(widget, scroll) {
46 | if( widget.get('_loadingNext') || widget.get('_loadedAll') ) { return; }
47 | if (scroll.deltaY > 0) {
48 | var remaining = widget.get("items").length - widget.get("lastVisibleIndex");
49 | if (remaining < 8) {
50 | loadMoreItems(widget, CELLS_PER_ROW);
51 | }
52 |
53 | }
54 | })
55 | .on("select", function(widget, feedItem) {
56 | detailScreen.open(feedItem.title, feedItem);
57 | });
58 | if (config.pullToRefresh ){
59 | widget.on('refresh', function(widget){
60 | refreshItems( widget , true, CELLS_PER_ROW);
61 | });
62 | }
63 | refreshItems(widget, false, CELLS_PER_ROW);
64 | return widget;
65 | };
66 |
67 |
68 | /**************************
69 | * Size calculations to fit many displays
70 | *************************/
71 |
72 | function calculateCellsPerRow(feedConfig){
73 |
74 | // Manually defined!
75 | if (feedConfig.layout && feedConfig.layout.cellWidth){
76 | return Math.max(1, Math.floor( tabris.device.get("screenWidth") / feedConfig.layout.cellWidth ));
77 | }
78 |
79 | // Default
80 | var cellsPerRow = DEFAULTS.CELLS_PER_ROW;
81 | var isTablet = sizing.isTablet;
82 | if(isTablet){
83 | cellsPerRow *= 3;
84 | }
85 | return cellsPerRow;
86 | }
87 |
88 |
89 | function calculateCellSizes(feedConfig, CELLS_PER_ROW){
90 | var cellWidth = Math.floor(tabris.device.get("screenWidth")/CELLS_PER_ROW);
91 | var cellHeightRatio = config.imgSizeHeightToWidthRatio;
92 | if(feedConfig.layout && feedConfig.layout.imgSizeHeightToWidthRatio){
93 | cellHeightRatio = feedConfig.layout.imgSizeHeightToWidthRatio;
94 | }
95 | var cellHeight = Math.floor(cellHeightRatio * cellWidth);
96 | return {
97 | cellWidth : cellWidth,
98 | cellHeight: cellHeight,
99 | }
100 | }
101 |
102 | /**************************
103 | * Cell Styling and updating
104 | *************************/
105 |
106 | function cellStyle(feedConfig){
107 | var themeStyle = getThemeStyle(feedConfig.color);
108 | var scaleMode = 'fill';
109 | if(feedConfig.layout && feedConfig.layout.scaleMode){
110 | scaleMode = feedConfig.layout.scaleMode;
111 | }
112 | return {
113 | container : { left: 0,right:0, top: 0, bottom: 0 , background: themeStyle.background},
114 | image: { left: 0, right: 0, top: 1, bottom: 1, scaleMode: scaleMode , background: "white"},
115 | overlay: { left: 0, right: 0, height: 46, bottom: 1 ,background: themeStyle.overlayBG, opacity: 0.8},
116 | title: { maxLines: 2, font: '16px', left: 10, right: 10, bottom: 5, textColor: themeStyle.textColor }
117 | };
118 | }
119 |
120 | function updateWidgetLoading(widget,loading){
121 | widget.set({
122 | refreshIndicator: loading,
123 | refreshMessage: loading ? "loading feed..." : ""
124 | });
125 | }
126 |
127 |
128 | /**************************
129 | * Data fetching
130 | *************************/
131 |
132 | function refreshItems( widget , forceFetch, CELLS_PER_ROW) {
133 | updateWidgetLoading ( widget, true);
134 | getItems( widget.get('_feed') , {forceFetch: forceFetch} ).then( function(results){
135 | var arr = [].concat(results.items);
136 | if (results.state && results.state.hasMore) {
137 | for (var i=0 ; i< CELLS_PER_ROW; i++){
138 | arr = arr.concat({loadingNext: true});
139 | }
140 | widget.set('_loadedAll', false);
141 | }
142 | else {
143 | widget.set('_loadedAll', true);
144 | }
145 | widget.set('items', arr );
146 | widget.set('_loadedPage', 1);
147 | updateWidgetLoading ( widget, false );
148 |
149 | }).catch(function(err){
150 | console.log("Failed fetching items for: "+ widget.get('_feed').name);
151 | console.log(err);
152 | try {
153 | console.log(JSON.stringify(err));
154 | } catch (e){
155 |
156 | }
157 | });
158 | }
159 |
160 |
161 | function loadMoreItems( widget , CELLS_PER_ROW) {
162 | widget.set('_loadingNext', true);
163 | var newPage = widget.get('_loadedPage')+1;
164 | getItems( widget.get('_feed') , {page: newPage } ).then( function(results){
165 |
166 | var arr = results.items;
167 | widget.insert(arr, -( CELLS_PER_ROW ));
168 | widget.set('_loadedPage', newPage );
169 | widget.set('_loadingNext', false);
170 |
171 | if (results.state && results.state.hasMore) {
172 | widget.set('_loadedAll', false);
173 | }
174 | else {
175 | widget.set('_loadedAll', true);
176 | widget.remove(-CELLS_PER_ROW);
177 | }
178 |
179 | }).catch(function(err){
180 | console.log("Failed fetching items for: "+ widget.get('_feed').name);
181 | console.log(err);
182 | try {
183 | console.log(JSON.stringify(err));
184 | } catch (e){
185 |
186 | }
187 | });
188 | }
189 |
190 |
191 |
192 |
193 | /**************************
194 | * CollectionView Cell updates
195 | *************************/
196 |
197 | function updateCellItemElements(feedItem, elements , CELL_SIZES){
198 | elements.container.set("_feedItem",feedItem);
199 | if(!feedItem || !feedItem.title) {
200 | hideElements(elements);
201 | return;
202 | }
203 | elements.container.set({opacity:1});
204 | var imageUpdate = {opacity: 1};
205 | var imageUrl = resizeImageURLByWidth(feedItem.image, CELL_SIZES.cellWidth);
206 |
207 | // Image update
208 | if(!imageUrl || imageUrl.length === 0) {
209 | imageUpdate.opacity = 0;
210 | }
211 | else if( !(elements.image.get('image') && elements.image.get('image').src === imageUrl)){
212 | // Image actually changed
213 | elements.image.set( {image: undefined });
214 | imageUpdate.image = {src: imageUrl};
215 | }
216 | setTimeout(function(){
217 | elements.image.set( imageUpdate );
218 | },1);
219 |
220 | // Title + Overlay update
221 | if(feedItem.price_display){
222 | elements.overlay.set({ top: undefined, height:23 });
223 | elements.title.set({ text: feedItem.price_display, maxLines: 1, bottom: 2, alignment:'center'});
224 | }
225 | else {
226 | elements.title.set({text: feedItem.title});
227 | if(!imageUrl || imageUrl.length === 0) {
228 | elements.overlay.set({ top: 1, height:undefined });
229 | elements.title.set({ maxLines: 5});
230 | } else {
231 | elements.overlay.set({ top: undefined, height:46 });
232 | elements.title.set({ maxLines: 2});
233 | }
234 | }
235 |
236 | }
237 |
238 | function hideElements(elements){
239 | elements.container.set({opacity:0});
240 | }
241 |
--------------------------------------------------------------------------------
/src/app/helpers/icon.js:
--------------------------------------------------------------------------------
1 | var platform = tabris.device.get("platform").toLowerCase();
2 |
3 | function getIcon(name, size) {
4 | var path = 'images/icons/' + platform + '/';
5 | size = size || Math.floor( tabris.device.get("scaleFactor") );
6 | //console.log(path + name + '@' + size + 'x.png');
7 | return path + name + '@' + size + 'x.png';
8 | };
9 |
10 | function getIconSrc(name, size) {
11 | size = size || Math.min(Math.floor( tabris.device.get("scaleFactor") ) , 2);
12 | var uri = getIcon(name,size);
13 | return {src: uri, scale: size * 2}
14 | };
15 |
16 |
17 | module.exports = {
18 | getIcon : getIcon,
19 | getIconSrc: getIconSrc
20 | }
21 |
--------------------------------------------------------------------------------
/src/app/helpers/img_resize.js:
--------------------------------------------------------------------------------
1 | var googleResize = 'http://images1-focus-opensocial.googleusercontent.com/gadgets/proxy';
2 | var weservResize = 'https://images.weserv.nl/';
3 | var imgResizeService = require('./../../config.js').config.imgResizeService;
4 |
5 | var handlers = {
6 | google : function(url, actualWidth){
7 | return googleResize+'?url='+encodeURIComponent(url)+'&resize_w='+ ( actualWidth ) +'&container=focus';
8 | },
9 | weserv : function(url, actualWidth){
10 | var newUrl;
11 | if(url.indexOf('https://') === 0){
12 | newUrl = url.slice(8);
13 | }
14 | else if(url.indexOf('http://') === 0){
15 | newUrl = url.slice(7);
16 | }
17 | return weservResize+'?url='+encodeURIComponent(newUrl)+'&w='+ ( actualWidth );
18 | },
19 | };
20 |
21 | function resizeImageURLByWidth(url, width){
22 | var handler;
23 | var actualWidth = Math.floor( width * tabris.device.get("scaleFactor") );
24 | if(!url || url.length === 0 ){
25 | return url;
26 | }
27 | handler = handlers[imgResizeService];
28 | if(!handler) { return url; }
29 | return handler(url,actualWidth);
30 |
31 | }
32 |
33 | module.exports = {
34 | resizeImageURLByWidth: resizeImageURLByWidth
35 | };
36 |
--------------------------------------------------------------------------------
/src/app/helpers/link.js:
--------------------------------------------------------------------------------
1 | var LINK_COLOR = "#48a8f4";
2 | var _ = require("lodash");
3 |
4 | exports.create = function(configuration) {
5 | var link = tabris.create("Composite", _.extend({highlightOnTouch: true}, configuration));
6 | var textViewConfiguration = {
7 | left: 0, top: 0, right: 0,
8 | textColor: LINK_COLOR
9 | };
10 |
11 | ["font", "text", "alignment", "height"].forEach(function(prop){
12 | maybeSetTextViewProperty(textViewConfiguration, configuration, prop);
13 | });
14 |
15 | tabris.create("TextView", textViewConfiguration).appendTo(link);
16 | link.on("tap", function() {
17 | var appLauncher = tabris.create("AppLauncher");
18 | appLauncher.openUrl(link.get("url"));
19 | });
20 | return link;
21 | };
22 |
23 | function maybeSetTextViewProperty(textViewConfiguration, configuration, property) {
24 | if (configuration[property]) {
25 | textViewConfiguration[property] = configuration[property];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/app/helpers/sizing.js:
--------------------------------------------------------------------------------
1 | function isTablet(){
2 | return (tabris.device.get("screenWidth") >= 800 && tabris.device.get("screenHeight") >= 600) || (tabris.device.get("screenHeight") >= 800 && tabris.device.get("screenWidth") >= 600);
3 | }
4 |
5 | function isLanscape(){
6 | return tabris.device.get("orientation").indexOf('landscape')===0;
7 | }
8 |
9 | function isTabletLandscape(){
10 | return isLanscape() && isTablet();
11 | }
12 |
13 | exports.isTabletLandscape = isTabletLandscape;
14 | exports.isTablet = isTablet();
15 |
--------------------------------------------------------------------------------
/src/app/pages/about.js:
--------------------------------------------------------------------------------
1 | var Link = require('./../helpers/link.js');
2 | var MARGIN_LARGE= 16;
3 |
4 | function init(){
5 | var page = tabris.create("Page", { title: "Built with Tabris.js", topLevel: false});
6 | createTabrisJsAttribution().appendTo(page);
7 | createProjectAttribution().appendTo(page);
8 |
9 | createAttributionsList([
10 | {
11 | subject: "Icons ",
12 | author: {name: "Icons8", url: "https://icons8.com/"},
13 | information: {label: "LICENSE", url: "https://creativecommons.org/licenses/by-nd/3.0/"}
14 | }
15 | ]).appendTo(page);
16 |
17 | return page;
18 | }
19 |
20 | function open(pageTitle, feedItem) {
21 | var p = init(pageTitle, feedItem);
22 | return p.open();
23 | }
24 |
25 | module.exports = {
26 | open: open,
27 | init: init,
28 | };
29 |
30 |
31 |
32 | function createTabrisJsAttribution() {
33 | var tabrisJsAttribution = tabris.create("Composite", {left: 0, top: "28%", right: 0});
34 | var container = tabris.create("Composite", {centerX: 0, top: 0, height: 48}).appendTo(tabrisJsAttribution);
35 | tabris.create("ImageView", {
36 | left: 0, top: 0, width: 48, height: 48,
37 | image: {src: "images/tabrisjs_logo@"+Math.round(tabris.device.get("scaleFactor"))+"x.png"}
38 | }).appendTo(container);
39 | tabris.create("TextView", {
40 | left: "prev()", centerY: 0,
41 | textColor: "#222",
42 | text: "Built with "
43 | }).appendTo(container);
44 | Link.create({left: "prev()", centerY: 0, url: "http://www.tabrisjs.com", text: "Tabris.js"}).appendTo(container);
45 | return tabrisJsAttribution;
46 | }
47 |
48 | function createProjectAttribution() {
49 | var projectAttribution = tabris.create("Composite", {
50 | id: "projectAttribution",
51 | left: MARGIN_LARGE, top: ["prev()", MARGIN_LARGE*2], height: 50, right: MARGIN_LARGE,
52 | });
53 | var firstLine = tabris.create("TextView", {
54 | left: 0, top: 0, right: 0,
55 | alignment: "center",
56 | textColor: "#222",
57 | text: "This app is open source."
58 | }).appendTo(projectAttribution);
59 | var secondLine = tabris.create("Composite", {centerX: 0, top: firstLine}).appendTo(projectAttribution);
60 | var seeSourceText = tabris.create("TextView", {
61 | left: 0, top: 0,
62 | textColor: "#222",
63 | text: "Fork it on "
64 | }).appendTo(secondLine);
65 | Link.create({text: "GitHub", url: "https://github.com/eclipsesource/tabris-js-starter-apps", left: seeSourceText, top: 0}).appendTo(secondLine);
66 | return projectAttribution;
67 | }
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | function createAttributionsList(attributions) {
77 | var attributionsList = tabris.create("Composite", {
78 | id: "attributionsList",
79 | left: 16, bottom: 8, right: 16
80 | });
81 | attributions.forEach(function(attribution) {
82 | createAttributionRow(attribution).appendTo(attributionsList);
83 | if (attributions.indexOf(attribution) !== attributions.length - 1) {
84 | createAttributionListSeparator().appendTo(attributionsList);
85 | }
86 | });
87 | return attributionsList;
88 | }
89 |
90 | function createAttributionRow(attribution) {
91 | var row = tabris.create("Composite", {left: 0, top: "prev()", right: 0, height: 24});
92 | tabris.create("TextView", {
93 | left: 0, centerY: 0,
94 | textColor: "rgba(0, 0, 0, 0.54)",
95 | text: attribution.subject + " by "
96 | }).appendTo(row);
97 | Link.create({
98 | left: "prev()", centerY: 0, text: attribution.author.name, url: attribution.author.url,
99 | }).appendTo(row);
100 | Link.create({
101 | right: 0, centerY: 0,
102 | text: attribution.information.label,
103 | page: attribution.information.page,
104 | url: attribution.information.url,
105 | }).appendTo(row);
106 | return row;
107 | }
108 | function createAttributionListSeparator() {
109 | return tabris.create("Composite", {
110 | left: 0, top: "prev()", right: 0, height: 1,
111 | background: "#d9d9d9"
112 | });
113 | }
114 |
--------------------------------------------------------------------------------
/src/app/pages/item_details.js:
--------------------------------------------------------------------------------
1 | var getItemDetails = require('./../../config.js').config.dataService.getItemDetails;
2 | var getIconSrc = require('./../helpers/icon').getIconSrc;
3 |
4 | function init(pageTitle, feedItem){
5 | var page = tabris.create("Page", { title: "Loading...", topLevel: false, _feedItem: feedItem });
6 | addItemWebView(page,feedItem, pageTitle);
7 | registerPageActions(page, feedItem);
8 |
9 | return page;
10 | }
11 |
12 |
13 | /*************************
14 | * Add the webview with the feed content.
15 | **************************/
16 |
17 | var handlers = {
18 | html : function(webView, itemDetails, container, titleOnLoad){
19 | webView.set("html", itemDetails.content );
20 | if(titleOnLoad){
21 | container.set({title:titleOnLoad});
22 | }
23 | },
24 | url : function(webView, itemDetails, container, titleOnLoad){
25 | webView.set("url", itemDetails.content );
26 | if(titleOnLoad){
27 | webView.on("load",function(){
28 | container.set({title:titleOnLoad});
29 | });
30 | }
31 | },
32 | };
33 |
34 | function addItemWebView(container, feedItem, titleOnLoad){
35 | var itemWebView = appendItemWebViewToContainer(container);
36 | var itemDetails = getItemDetails(feedItem);
37 |
38 | handlers[itemDetails.type] (itemWebView, itemDetails, container, titleOnLoad);
39 |
40 | return itemWebView;
41 | }
42 |
43 | function appendItemWebViewToContainer(container){
44 | var itemWebView = tabris.create('WebView',{ left: 0, right: 0, top: 0, bottom: 0}).appendTo(container);
45 | container.set('_itemWebView', itemWebView);
46 | return itemWebView;
47 | }
48 |
49 |
50 | function open(pageTitle, feedItem) {
51 | var p = init(pageTitle, feedItem);
52 | return p.open();
53 | }
54 |
55 | function registerPageActions(page, feedItem){
56 |
57 | var openURLAction = tabris.create("Action", {
58 | placementPriority: "high",
59 | title: " ",
60 | image: getIconSrc("external_link")
61 | }).on("select", function() {
62 | openExternal(feedItem)
63 | });
64 |
65 | var openShareAction = tabris.create("Action", {
66 | placementPriority: tabris.device.get("platform") === "iOS" ? "low": "high",
67 | title: " ",
68 | image: getIconSrc("share")
69 | }).on("select", function() {
70 | share(feedItem);
71 | });
72 |
73 | var openMoreAction = tabris.create("Action", {
74 | placementPriority: tabris.device.get("platform") === "iOS" ? "normal": "high",
75 | title: " ",
76 | image: getIconSrc("more")
77 | }).on("select", function() {
78 | var itemDetails = getItemDetails(feedItem);
79 | var actions = ["Share", "Open in Browser"];
80 | var handlers = [share, openExternal];
81 |
82 | // Add another action when in url mode (RSS reader).
83 | if(itemDetails.type === "html" && !page.get("_isInURL")){
84 | actions.push("Read Full Article");
85 | handlers.push(function(){
86 | page.get('_itemWebView').dispose();
87 | appendItemWebViewToContainer(page).set("url", itemDetails.link );
88 | page.set("_isInURL",true);
89 | });
90 | }
91 |
92 | var options = {
93 | androidTheme: window.plugins.actionsheet.ANDROID_THEMES.THEME_HOLO_LIGHT,
94 | title: "What to do with this item",
95 | buttonLabels: actions,
96 | androidEnableCancelButton: true,
97 | winphoneEnableCancelButton: true,
98 | addCancelButtonWithLabel: "Cancel",
99 | //addDestructiveButtonWithLabel: "Remove from watchlist"
100 | };
101 |
102 | window.plugins.actionsheet.show(options,function(buttonIndex) {
103 |
104 | setTimeout(function() {
105 | // like other Cordova plugins (prompt, confirm) the buttonIndex is 1-based (first button is index 1)
106 | buttonIndex = buttonIndex - 1;
107 | if( handlers[buttonIndex] ){
108 | handlers[buttonIndex](feedItem);
109 | }
110 | });
111 |
112 | });
113 | });
114 |
115 |
116 | page.on("disappear", function(){
117 | openMoreAction.dispose();
118 | openURLAction.dispose();
119 | openShareAction.dispose();
120 | });
121 | }
122 |
123 |
124 | function openExternal(feedItem){
125 | var itemDetails = getItemDetails(feedItem);
126 | var appLauncher = tabris.create("AppLauncher");
127 | appLauncher.openUrl(itemDetails.link || itemDetails.content);
128 | }
129 |
130 | function share(feedItem){
131 | var itemDetails = getItemDetails(feedItem);
132 | window.plugins.socialsharing.share("Check out this awesome thing", feedItem.title,
133 | feedItem.image,
134 | itemDetails.link || itemDetails.content);
135 | }
136 |
137 |
138 | module.exports = {
139 | open: open,
140 | init: init,
141 | addItemWebView: addItemWebView,
142 | };
143 |
--------------------------------------------------------------------------------
/src/app/pages/item_list.js:
--------------------------------------------------------------------------------
1 | var itemListComponent = require('./../components/item_list');
2 | var updateUIColors = require('./../styles/theme').updateUIColors;
3 |
4 | function init(pageTitle, feedConfig){
5 | var page = tabris.create("Page", { title: pageTitle, topLevel: false, _feed: feedConfig });
6 | itemListComponent( feedConfig , page ).appendTo(page);
7 | updateUIColors(feedConfig.color);
8 | return page;
9 | }
10 |
11 | function open(pageTitle, feedItem) {
12 | var p = init(pageTitle, feedItem);
13 | return p.open();
14 | }
15 |
16 | module.exports = {
17 | open: open,
18 | init: init,
19 | };
20 |
--------------------------------------------------------------------------------
/src/app/pages/main.js:
--------------------------------------------------------------------------------
1 | var config = require('./../../config.js').config;
2 | var itemListComponent = require('./../components/item_list');
3 | var feedShowcase = require('./../components/feed_showcase');
4 | var updateUIColors = require('./../styles/theme').updateUIColors;
5 | var getThemeStyle = require('./../styles/theme').getThemeStyle;
6 | var aboutPage = require('./about.js');
7 | var getIconSrc = require('./../helpers/icon').getIconSrc;
8 | var imageSlider = require('./../components/image_slider');
9 | var detailScreen = require('./../pages/item_details');
10 |
11 |
12 | // Sizing helpers.
13 | var sizing = require('./../helpers/sizing');
14 | var isTablet = sizing.isTablet;
15 | var imageWidth = Math.floor( isTablet ? tabris.device.get("screenWidth") * config.imgShowcaseScreenWidthRatio.tablet : tabris.device.get("screenWidth") * config.imgShowcaseScreenWidthRatio.phone );
16 | var imageHeightRatio = isTablet ? config.imgShowcaseSizeHeightToWidthRatio.tablet : config.imgShowcaseSizeHeightToWidthRatio.phone;
17 | var imageHeight = Math.floor(imageHeightRatio * imageWidth);
18 |
19 |
20 | function init() {
21 | // Ok we need a page to contain all the application UI
22 | var page = tabris.create("Page", { title: config.appName , topLevel : true}) ;
23 |
24 | //var Navigation = tabris.create('TabFolder', { left: 0, top: 0, right: 0, bottom:0 , elevation: 8 , tabBarLocation: "bottom", paging: false, textColor: "#ff8400",}).appendTo(page);
25 | //page.set("_navigation", Navigation);
26 |
27 | // Now we will create a tab per source and add to the container
28 |
29 | //var HomeTab = tabris.create( 'Tab', { title: "Home", background: 'white', image: getIconSrc('home'), _imgName: 'home' } ).appendTo(Navigation);
30 | ////tabris.create( 'Tab', { title: "Discover", background: 'white', image: getIconSrc('compass'), } ).appendTo(Navigation);
31 | //tabris.create( 'Tab', { title: "Search", background: 'white', image: getIconSrc('search'), _imgName: 'search'} ).appendTo(Navigation);
32 | //tabris.create( 'Tab', { title: "Favourites", background: 'white', image: getIconSrc('like'), _imgName: 'like' } ).appendTo(Navigation);
33 | //tabris.create( 'Tab', { title: "My account", background: 'white', image: getIconSrc('user'), _imgName: 'user' } ).appendTo(Navigation);
34 | //tabris.create( 'Tab', { title: "More", background: 'white', image: getIconSrc('info') , _imgName: 'info'} ).appendTo(Navigation);
35 | //
36 | //// When the user changes the tab, change the app visuals
37 | //Navigation.on("change:selection", function(widget, tab) {
38 | // //colorUpdates (tab.get('_feed').color );
39 | // console.log(tab.get('_imgName'));
40 | // tab.set('image', getIconSrc(tab.get('_imgName') + '_full') )
41 | // tab.trigger("appear",tab);
42 | //});
43 | //
44 | //var HometabContent = tabris.create('Composite', { left: 0, top: 0, right: 0, bottom:0}).appendTo(HomeTab);
45 | //var MainContent = HometabContent;
46 |
47 | var MainContent = page;
48 |
49 | if(config.mainPage === "tabs"){
50 | /**********************
51 | * Tabs
52 | ******************/
53 | // So we need a Tab Container
54 | var TabFolder = tabris.create('TabFolder', { left: 0, top: 0, right: 0, bottom:0 , elevation: 8 , tabBarLocation: "top", paging: tabris.device.get("platform") === "iOS" ? false : true} ).appendTo(MainContent);
55 | page.set("_tabs", TabFolder);
56 |
57 | // Now we will create a tab per source and add to the container
58 | config.feeds.forEach(function( feed ){
59 | var tab = tabris.create( 'Tab', { title: feed.name, background: 'white', _feed: feed} ).appendTo(TabFolder);
60 | itemListComponent( feed , tab ).appendTo(tab);
61 | });
62 |
63 | // When the user changes the tab, change the app visuals
64 | TabFolder.on("change:selection", function(widget, tab) {
65 | colorUpdates (tab.get('_feed').color , TabFolder);
66 | });
67 |
68 | // Update the UI based on the theme and active tab.
69 | colorUpdates (config.feeds[0].color , TabFolder);
70 | }
71 | else if(config.mainPage === "showcase" || true){
72 | /**********************
73 | * Showcase
74 | ******************/
75 | var container = tabris.create("ScrollView", { left: 0, right: 0, top: 0, bottom: 0 , direction:"vertical"}).appendTo(MainContent);
76 |
77 | if (config.slider){
78 | imageSlider(config.slider).on("itemSelected",function(item){
79 | detailScreen.open(item.title, item);
80 | }).appendTo(container);
81 | }
82 |
83 | // Now we will showcase per source and add to the container
84 | config.feeds.forEach(function( feed ){
85 | feedShowcase( feed , container ).appendTo(container);
86 | });
87 |
88 | container.on("scroll",function(widget, offset){
89 | var activeFeedIndex = Math.min (Math.max (Math.floor (offset.y / (imageHeight + 90)) , 0 ) , config.feeds.length-1);
90 | colorUpdates (config.feeds[activeFeedIndex].color);
91 | });
92 |
93 | // Update the UI based on the theme and active tab.
94 | colorUpdates (config.feeds[0].color);
95 | }
96 |
97 |
98 | /*************************
99 | * Add an action to the nav bar
100 | **************************/
101 | page.on("appear", function(){
102 | addViewAction(page);
103 | })
104 | .on("disappear", function(){
105 | page.get('_openLinkAction').dispose();
106 | });
107 |
108 | return page;
109 | }
110 |
111 | function open(){
112 | var p = init();
113 | return p.open();
114 | }
115 |
116 | module.exports = {
117 | init: init,
118 | open: open
119 | };
120 |
121 |
122 | function colorUpdates(color, TabFolder){
123 | var styles = getThemeStyle(color);
124 | updateUIColors(color);
125 | if(TabFolder){
126 | TabFolder.set(styles.tabs);
127 | }
128 | }
129 |
130 | function addViewAction(page){
131 | var openLinkAction = tabris.create("Action", {
132 | placementPriority: "high",
133 | title: " ",
134 | image: getIconSrc("info")
135 | }).on("select", function() {
136 | aboutPage.open();
137 | });
138 | page.set('_openLinkAction',openLinkAction);
139 | }
140 |
--------------------------------------------------------------------------------
/src/app/styles/theme.js:
--------------------------------------------------------------------------------
1 | var config = require('./../../config').config;
2 |
3 | /*************************
4 | * refresh the ui styling based on the theme (and color passed).
5 | **************************/
6 |
7 | function updateUIColors(color){
8 | var style = getThemeStyle(color);
9 | if(style.ui){
10 | tabris.ui.set(style.ui);
11 | }
12 | // If the theme is other then just fall back to system defaults.
13 | }
14 |
15 |
16 | /*************************
17 | * How the rssItem cells look in each of the different themes.
18 | **************************/
19 |
20 | function getThemeStyle(color){
21 | if (config.theme === 'light'){
22 | return {
23 | background: 'white',
24 | overlayBG: 'white',
25 | textColor: color,
26 | showcase: {
27 | background: 'white',
28 | textColor: color,
29 | },
30 | ui: {
31 | background: 'white',
32 | textColor: color,
33 | statusBarTheme: 'light'
34 | },
35 | tabs: {
36 | background: 'white',
37 | textColor: color
38 | },
39 | };
40 | }
41 | else if (config.theme === 'normal'){
42 | return {
43 | background: 'white',
44 | overlayBG: color,
45 | textColor: 'white',
46 | showcase: {
47 | background: 'white',
48 | textColor: color,
49 | },
50 | ui: {
51 | background: color,
52 | textColor: 'white',
53 | statusBarTheme: 'dark'
54 | },
55 | tabs: {
56 | background: 'white',
57 | textColor: color
58 | },
59 | };
60 | }
61 | else if (config.theme === 'full'){
62 | return {
63 | background: color,
64 | overlayBG: color,
65 | textColor: 'white',
66 | showcase: {
67 | background: color,
68 | textColor: 'white',
69 | },
70 | ui: {
71 | background: color,
72 | textColor: 'white',
73 | statusBarTheme: 'dark'
74 | },
75 | tabs: {
76 | background: color,
77 | textColor: 'white'
78 | },
79 | };
80 | }
81 | return {};
82 | }
83 |
84 |
85 | module.exports = {
86 | updateUIColors: updateUIColors,
87 | getThemeStyle:getThemeStyle
88 | };
89 |
--------------------------------------------------------------------------------
/src/config.js:
--------------------------------------------------------------------------------
1 | var _ = require("lodash");
2 | var apps = {
3 | rss: {
4 | appName: 'Tabris.js Open Source RSS Reader',
5 | dataService: require('./services/rss'),
6 | feeds: require('./services/rss/feeds').slice(0,3),
7 | },
8 | rss_showcase: {
9 | appName: 'Tabris.js RSS Reader Example',
10 | dataService: require('./services/rss'),
11 | feeds: require('./services/rss/feeds'),
12 | mainPage: "showcase",
13 | },
14 | shop: {
15 | appName: 'Tabris.js Open Source Ecommerce Shop',
16 | dataService: require('./services/shop'),
17 | feeds: require('./services/shop/feeds'),
18 |
19 | // Overrides...
20 | pullToRefresh:false,
21 | imgSizeHeightToWidthRatio : 0.8,
22 |
23 | imgShowcaseSizeHeightToWidthRatio: {
24 | phone: 1.3,
25 | tablet: 1.1,
26 | },
27 | imgShowcaseScreenWidthRatio: {
28 | phone: 0.25,
29 | tablet: 0.16,
30 | },
31 | },
32 | shop_showcase: {
33 | appName: 'Tabris.js Open Source Ecommerce Shop',
34 | dataService: require('./services/shop'),
35 | feeds: require('./services/shop/feeds'),
36 | mainPage: "showcase",
37 | // Overrides...
38 | pullToRefresh:false,
39 | imgSizeHeightToWidthRatio: 0.8,
40 | imgShowcaseSizeHeightToWidthRatio: {
41 | phone: 1.3,
42 | tablet: 1.1,
43 | },
44 | imgShowcaseScreenWidthRatio: {
45 | phone: 0.25,
46 | tablet: 0.16,
47 | }
48 | },
49 | shop_fashion: {
50 | appName: 'Tabris.js Open Source Ecommerce Shop',
51 | dataService: require('./services/shop_fashion'),
52 | feeds: require('./services/shop_fashion/feeds'),
53 | mainPage: "showcase",
54 | theme: tabris.device.get("platform") === "iOS" ? 'light' : 'normal',
55 | // Overrides...
56 | pullToRefresh:false,
57 | imgSizeHeightToWidthRatio: 2.4,
58 | imgShowcaseSizeHeightToWidthRatio: {
59 | phone: 1.3,
60 | tablet: 1.1,
61 | },
62 | imgShowcaseScreenWidthRatio: {
63 | phone: 0.25,
64 | tablet: 0.16,
65 | },
66 | slider: [
67 | {title: 'great bag 1', image:'https://resources.shopstyle.com/pim/9d/c2/9dc260913e015b4f2d0b0d17822938b3_best.jpg',clickUrl: 'http://api.shopstyle.com/action/apiVisitRetailer?id=512695420&pid=uid4961-26577031-68'},
68 | {title: 'great bag 2', image:'https://resources.shopstyle.com/pim/d1/f1/d1f1fe6f59f2f5a17195f22aa1fceb99_best.jpg',clickUrl: 'http://api.shopstyle.com/action/apiVisitRetailer?id=505229452&pid=uid4961-26577031-68'},
69 | {title: 'great bag 3', image:'https://resources.shopstyle.com/pim/a3/e0/a3e06a910fc662efa055e372404530bc_best.jpg', clickUrl: 'http://api.shopstyle.com/action/apiVisitRetailer?id=512695420&pid=uid4961-26577031-68'}
70 | ]
71 | },
72 | wordpress_pets: {
73 | appName: 'Tabris.js Wordpress example',
74 | dataService: require('./services/wordpress_pets'),
75 | feeds: require('./services/wordpress_pets/feeds'),
76 | pullToRefresh:false,
77 | },
78 | wordpress_eclipsesource: {
79 | appName: 'Tabris.js Wordpress example',
80 | dataService: require('./services/wordpress_eclipsesource'),
81 | feeds: require('./services/wordpress_eclipsesource/feeds'),
82 | }
83 | // TODO: Youtube / Vimeo ?
84 | };
85 |
86 |
87 | var baseConfig = {
88 | /******************************************
89 | * This project comes with 7 starter apps That show off different abilities and components:
90 | *
91 | * rss,
92 | * rss_showcase,
93 | * shop,
94 | * shop_showcase,
95 | * shop_fashion,
96 | * wordpress_pets,
97 | * wordpress_eclipsesource,
98 | *
99 | **********************************/
100 | app: apps.shop_fashion , // can be apps.rss, apps.rss_showcase, apps.shop, apps.shop_showcase, apps.shop_fashion, apps.wordpress_pets, apps.wordpress_eclipsesource,
101 |
102 |
103 |
104 | /******************************************
105 | * The default configuration is below...
106 | ***********************/
107 | //theme: 'normal',
108 | //theme: 'light',
109 | //theme: 'full',
110 | theme: tabris.device.get("platform") === "iOS" ? 'light' : 'full', // Define a certain theme for iOS and different for Android
111 |
112 | mainPage: "tabs", // can be "showcase" or "tabs"
113 |
114 | imgResizeService: 'weserv', // can be 'weserv', 'google', or 'none'.
115 |
116 | pullToRefresh:true,
117 | imgSizeHeightToWidthRatio :0.6,
118 |
119 | imgShowcaseScreenWidthRatio: {
120 | phone: 0.6,
121 | tablet: 0.4,
122 | },
123 |
124 | imgShowcaseSizeHeightToWidthRatio: {
125 | phone: 0.4,
126 | tablet: 0.5,
127 | },
128 | };
129 |
130 | function buildConfig(){
131 | var config = _.cloneDeep(baseConfig);
132 | var app = baseConfig.app;
133 | for (var prop in app){
134 | config[prop] = app[prop];
135 | }
136 | return config;
137 | }
138 |
139 | exports.config = buildConfig();
140 |
141 |
142 |
--------------------------------------------------------------------------------
/src/services/rss/feed_helpers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by shaialon on 16/02/2016.
3 | */
4 | function sanitizeFeedItems(feedItems, customContentSanitizer){
5 | var results = [];
6 | feedItems.forEach(function(item){
7 | if(item.title && item.title.length>0){
8 | item.cleanContent = customContentSanitizer ? customContentSanitizer (item.content) : sanitizeHTMLfromFeedBloat(item.content);
9 | delete item.content;
10 | results.push(item);
11 | }
12 | });
13 | return results;
14 | }
15 |
16 | function sanitizeHTMLfromFeedBloat(html){
17 | var tmp = html.replace(//ig,'') // remove feedburner crap
19 | .replace(/
/igm,''); // ads in table (smashing magazine)
21 | return tmp;
22 |
23 | }
24 |
25 | function extractFirstImageFromHtml(html) {
26 | var m,
27 | rex = /
]+src="?([^"\s]+)"?[^>]*\>/g;
28 |
29 | m = rex.exec( html );
30 | if(m && m[1]) { return m[1]; }
31 | return null;
32 | }
33 |
34 | function resolveImageForFeedItem(feedItem, customImageResolver) {
35 | var enclosure = feedItem.enclosure || {};
36 | var img = enclosure.link;
37 | if(customImageResolver){
38 | // In case a custom resolver was set for a certain feed.
39 | img = customImageResolver(feedItem);
40 | }
41 | else if(!img){
42 | // Fallback, extract image from the content
43 | img = extractFirstImageFromHtml(feedItem.cleanContent);
44 | }
45 | return img || '';
46 | }
47 |
48 | module.exports = {
49 | sanitizeFeedItems: sanitizeFeedItems,
50 | sanitizeHTMLfromFeedBloat: sanitizeHTMLfromFeedBloat,
51 | extractFirstImageFromHtml: extractFirstImageFromHtml,
52 | resolveImageForFeedItem: resolveImageForFeedItem
53 | };
54 |
--------------------------------------------------------------------------------
/src/services/rss/feeds.js:
--------------------------------------------------------------------------------
1 | /******************************************
2 | * Here, are some common technology RSS feeds, with their respective:
3 | *
4 | * name - The title of the tab
5 | * color - The color identified with the feed. Play around with the theme and see how this affects the UI.
6 | * feed - The url of the feed. If it is in XML use the rss2json function.
7 | * contentSanitizer - A custom function that can modify the html of the feed to remove all kind of bloat (ads, trackers, and ugly sharing buttons)
8 | * imageResolver - A custom function since each feed has a certain placement for the images or the ability to load lightweight images with a certain parameter (see TechCrunch or FayerWayer)
9 | *
10 | *************************************/
11 |
12 | function rss2json(feedUrl){
13 | return 'http://rss2json.com/api.json?rss_url='+encodeURIComponent(feedUrl);
14 | }
15 |
16 | module.exports = [
17 | { // GOOD
18 | name: 'LifeHacker',
19 | color: '#709602',
20 | feed: rss2json('http://lifehacker.com/rss'),
21 | layout: {
22 | cellWidth: 300
23 | },
24 | },
25 | {
26 | name: 'TechRadar',
27 | color: '#2F6E91',
28 | feed: rss2json('http://www.techradar.com/rss'),
29 | layout: {
30 | cellWidth: 300
31 | },
32 | contentSanitizer: function(html){
33 | return html.replace(/
/igm,'');
34 | },
35 |
36 | },
37 | {
38 | name: 'TechCrunch',
39 | color: '#0A9E01',
40 | feed: rss2json('http://feeds.feedburner.com/Techcrunch'),
41 | layout: {
42 | cellWidth: 300
43 | },
44 | imageResolver: function(feedItem){
45 | if(feedItem.enclosure && feedItem.enclosure.link){
46 | // TODO: device width!
47 | return feedItem.enclosure.link + '?w=' + ( tabris.device.get("screenWidth") * tabris.device.get("scaleFactor") ); //request a custom size of img. Supported only on techcrunch
48 | }
49 | return './images/notfound.png';
50 | }
51 | },
52 |
53 | { // GOOD
54 | name: 'Gizmodo',
55 | color: '#333',
56 | feed: rss2json('http://gizmodo.com/rss'),
57 | },
58 |
59 | { // Good
60 | name: 'SmashingMagazine',
61 | color: '#E53F2C',
62 | feed: rss2json('http://www.smashingmagazine.com/feed'),
63 | },
64 |
65 | { // Good (but small)
66 | name: 'SpeckyBoy',
67 | color: '#658DB5',
68 | feed: rss2json('http://speckyboy.com/feed'),
69 | },
70 |
71 |
72 | //{
73 | // name: 'Fayerwayer',
74 | // color: '#333',
75 | // feed: rss2json('http://feeds.feedburner.com/fayerwayer'),
76 | // imageResolver: function(feedItem){
77 | // if(feedItem.enclosure && feedItem.enclosure.link){
78 | // // TODO: device width!
79 | // return feedItem.enclosure.link.replace('https://', 'http://').replace('.jpg', '-320x210.jpg')
80 | // }
81 | // return './images/notfound.png';
82 | // }
83 | //},
84 | //
85 | //{ // no images..
86 | // name: 'ScienceDaily',
87 | // color: '#004276',
88 | // feed: rss2json('http://www.sciencedaily.com/rss/top/technology.xml'),
89 | //},
90 |
91 | //{ // Needs image in detail
92 | // name: 'PCWorld',
93 | // color: '#8f0d10',
94 | // feed: rss2json('http://www.pcworld.com/index.rss'),
95 | //},
96 |
97 | //{ // Needs work!
98 | // name: 'Eclipsesource',
99 | // color: '#207A93',
100 | // feed: rss2json('https://eclipsesource.com/blogs/feed/'),
101 | //},
102 |
103 | //{ // no images..
104 | // name: 'Economist',
105 | // color: '#8f0d10',
106 | // feed: rss2json('http://www.economist.com/topics/computer-technology/index.xml'),
107 | //},
108 | //{
109 | // name: 'React Native',
110 | // color: '#05A9D6',
111 | // feed: rss2json('http://www.reactnative.com/rss/'),
112 | //},
113 | ];
114 |
--------------------------------------------------------------------------------
/src/services/rss/index.js:
--------------------------------------------------------------------------------
1 | /***************************
2 | * For the most part, services are independent of the platform the are running on.
3 | * This means they could function similarly in a browser, or a node.js server.
4 | *
5 | ****************************/
6 |
7 | var feedHelpers = require('./feed_helpers');
8 | var platformStylingWebView = {
9 | iOS : {
10 | font:"font-size: 290%; font-family:'Helvetica Neue';",
11 | padding: 'padding: 10px 30px 0px 30px;',
12 | img: 'width:100%;'
13 |
14 | },
15 | Android: {
16 | font: 'font-size: 140%; ',
17 | padding: 'padding: 30px 30px 30px 30px;',
18 | img: 'max-width: 100%;'
19 | }
20 | };
21 | var _ = require("lodash");
22 | var requestCache = {};
23 |
24 | function getItems(feedConfig, overideConfig){
25 | overideConfig = overideConfig ? overideConfig : {};
26 | return new Promise(function(resolve, reject) {
27 | if(requestCache[feedConfig.feed] && !overideConfig.forceFetch){
28 | // This has been requested before
29 | setTimeout(function(){
30 | resolve(_.cloneDeep(requestCache[feedConfig.feed]));
31 | },1);
32 | }
33 | else {
34 | fetch(feedConfig.feed).then(function (res) {
35 | return res.json();
36 | }).then(function (res) {
37 | var finalResult;
38 | var itemsProcessed = feedHelpers.sanitizeFeedItems(res.items, feedConfig.contentSanitizer);
39 | itemsProcessed.forEach(function (item) {
40 | item.image = feedHelpers.resolveImageForFeedItem(item, feedConfig.imageResolver);
41 | });
42 |
43 | finalResult = {items: itemsProcessed};
44 | requestCache[feedConfig.feed] = finalResult;
45 | resolve(_.cloneDeep(finalResult));
46 |
47 | }).catch(function (err) {
48 | reject(err);
49 | });
50 | }
51 | });
52 | }
53 |
54 |
55 | function getItemDetails(item) {
56 | return {
57 | type: 'html', // can be 'html', 'url', 'component'
58 | content: rssItemWebViewHTML(item),
59 | link: item.link || item.url
60 | };
61 | }
62 |
63 |
64 | /*************************
65 | * An internal css configuration for webviews per platform
66 | **************************/
67 |
68 | function WebViewInternalCSS (){
69 | var styles = platformStylingWebView[tabris.device.get("platform")];
70 | // var reset = "article,aside,details,figcaption,figure,footer,header,hgroup,hr,menu,nav,section{display:block}a,hr{padding:0}abbr,address,article,aside,audio,b,blockquote,body,canvas,caption,cite,code,dd,del,details,dfn,div,dl,dt,em,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,p,pre,q,samp,section,small,span,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,ul,var,video{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:0 0}ins,mark{background-color:#ff9;color:#000}body{line-height:1}nav ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}a{margin:0;font-size:100%;vertical-align:baseline;background:0 0}ins{text-decoration:none}mark{font-style:italic;font-weight:700}del{text-decoration:line-through}abbr[title],dfn[title]{border-bottom:1px dotted;cursor:help}table{border-collapse:collapse;border-spacing:0}hr{height:1px;border:0;border-top:1px solid #ccc;margin:1em 0}input,select{vertical-align:middle}";
71 | var strArr = [''
77 | ];
78 | return ''+ strArr.join('');
79 | }
80 |
81 |
82 |
83 | function rssItemWebViewHTML(feedItem){
84 | return [ '',
85 | '',
86 | WebViewInternalCSS(),
87 | '',
88 | '',
89 | ''+ feedItem.title +'
',
90 | ''+ feedItem.pubDate +'
',
91 | feedItem.cleanContent,
92 | '',
93 | ''
94 | ].join('');
95 | }
96 |
97 | module.exports = {
98 | getItems: getItems,
99 | getItemDetails: getItemDetails
100 | };
101 |
--------------------------------------------------------------------------------
/src/services/shop/feeds.js:
--------------------------------------------------------------------------------
1 | /******************************************
2 | * Here, are some common technology RSS feeds, with their respective:
3 | *
4 | * name - The title of the tab
5 | * color - The color identified with the feed. Play around with the theme and see how this affects the UI.
6 | * feed - The url of the feed. If it is in XML use the rss2json function.
7 | *
8 | *************************************/
9 |
10 | module.exports = [
11 | // Watches
12 |
13 | {
14 | name: 'G-shock',
15 | color: '#333',
16 | config: {
17 | catalog: "672oqm0oqpyrc4ullpfeclz66",
18 | account:"bbhntrjt16yvunll9iyayufn4",
19 | keyword: "G-shock watch",
20 | category: 1,
21 | include_discounts: "true",
22 | results_per_page: 50,
23 | },
24 | },
25 | {
26 | name: 'Fossil',
27 | color: '#333',
28 | config: {
29 | catalog: "672oqm0oqpyrc4ullpfeclz66",
30 | account:"bbhntrjt16yvunll9iyayufn4",
31 | keyword: "Fossil Watch Men",
32 | category: 1,
33 | include_discounts: "true",
34 | results_per_page: 50,
35 | },
36 | },
37 | {
38 | name: 'Premium',
39 | color: '#333',
40 | config: {
41 | catalog: "672oqm0oqpyrc4ullpfeclz66",
42 | account:"bbhntrjt16yvunll9iyayufn4",
43 | keyword: "Men watch",
44 | category: 1,
45 | include_discounts: "true",
46 | results_per_page: 50,
47 | price_min: 1000
48 | },
49 | },
50 | {
51 | name: 'Breitling',
52 | color: '#333',
53 | config: {
54 | catalog: "672oqm0oqpyrc4ullpfeclz66",
55 | account:"bbhntrjt16yvunll9iyayufn4",
56 | keyword: "Breitling watch",
57 | category: 1,
58 | include_discounts: "true",
59 | results_per_page: 50,
60 | },
61 | },
62 | {
63 | name: 'Rolex',
64 | color: '#333',
65 | config: {
66 | catalog: "672oqm0oqpyrc4ullpfeclz66",
67 | account:"bbhntrjt16yvunll9iyayufn4",
68 | keyword: "Rolex Watch Men",
69 | category: 1,
70 | include_discounts: "true",
71 | results_per_page: 50,
72 | },
73 | },
74 | {
75 | name: 'Pink',
76 | color: '#333',
77 | config: {
78 | catalog: "672oqm0oqpyrc4ullpfeclz66",
79 | account:"bbhntrjt16yvunll9iyayufn4",
80 | keyword: "Pink watch",
81 | category: 1,
82 | include_discounts: "true",
83 | results_per_page: 50,
84 | },
85 | },
86 |
87 |
88 |
89 |
90 |
91 | // Gifts
92 | //{
93 | // name: 'Flowers',
94 | // color: '#D088CA',
95 | // config: {
96 | // catalog: "0135pruepnxsbh6gw2ve714rv",
97 | // account:"bbhntrjt16yvunll9iyayufn4",
98 | // keyword: "Flowers",
99 | // category: 1,
100 | // include_discounts: "true",
101 | // results_per_page: 50,
102 | // },
103 | //},
104 | //{
105 | // name: 'Chocolates',
106 | // color: '#7B5252',
107 | // config: {
108 | // catalog: "0135pruepnxsbh6gw2ve714rv",
109 | // account:"bbhntrjt16yvunll9iyayufn4",
110 | // keyword: "Chocolates",
111 | // category: 1,
112 | // include_discounts: "true",
113 | // results_per_page: 50,
114 | // },
115 | //},
116 | //{
117 | // name: 'Teddys',
118 | // color: '#05A9D6',
119 | // config: {
120 | // catalog: "0135pruepnxsbh6gw2ve714rv",
121 | // account:"bbhntrjt16yvunll9iyayufn4",
122 | // keyword: "Teddy bear",
123 | // category: 1,
124 | // include_discounts: "true",
125 | // results_per_page: 50,
126 | // },
127 | //},
128 |
129 |
130 | //{
131 | // name: 'Hammers',
132 | // color: '#333',
133 | // config: {
134 | // catalog: "09ljxn1kvf6w57ess6smd6xcq",
135 | // account:"bbhntrjt16yvunll9iyayufn4",
136 | // keyword: "hammer",
137 | // category: 1,
138 | // include_discounts: "true",
139 | // results_per_page: 50,
140 | // },
141 | //},
142 | //
143 | //{
144 | // name: 'Wrenches',
145 | // color: '#333',
146 | // config: {
147 | // catalog: "09ljxn1kvf6w57ess6smd6xcq",
148 | // account:"bbhntrjt16yvunll9iyayufn4",
149 | // keyword: "Wrench",
150 | // category: 1,
151 | // include_discounts: "true",
152 | // results_per_page: 50,
153 | // },
154 | //},
155 | //
156 | //{
157 | // name: 'Saw',
158 | // color: '#333',
159 | // config: {
160 | // catalog: "09ljxn1kvf6w57ess6smd6xcq",
161 | // account:"bbhntrjt16yvunll9iyayufn4",
162 | // keyword: "chainsaw",
163 | // category: 1,
164 | // include_discounts: "true",
165 | // results_per_page: 50,
166 | // },
167 | //},
168 | ];
169 |
--------------------------------------------------------------------------------
/src/services/shop/index.js:
--------------------------------------------------------------------------------
1 | /***************************
2 | * For the most part, services are independent of the platform the are running on.
3 | * This means they could function similarly in a browser, or a node.js server.
4 | *
5 | ****************************/
6 | var requestCache = {};
7 | var _ = require("lodash");
8 |
9 | function getItems(feedConfig , overideConfig){
10 | return new Promise(function(resolve, reject) {
11 |
12 | var targetFeed = _.cloneDeep(feedConfig.config);
13 | if(overideConfig && overideConfig.page){
14 | targetFeed.page = overideConfig.page;
15 | }
16 | else {
17 | targetFeed.page = 1;
18 | }
19 |
20 | var queryParamsStr = '';
21 | var tmp = [];
22 | for (var key in targetFeed){
23 | tmp.push(key + "=" + encodeURIComponent( targetFeed[key] ));
24 | }
25 | queryParamsStr = tmp.join("&");
26 |
27 | if(requestCache[queryParamsStr]){
28 | // This has been requested before
29 | setTimeout(function(){
30 | resolve(_.cloneDeep(requestCache[queryParamsStr]));
31 | },1);
32 | }
33 | else {
34 | fetch( "https://www.popshops.com/v3/products.json?" + queryParamsStr ).then(function( res ){
35 | return res.json();
36 | }).then(function( res ){
37 | var itemsProcessed, state = {};
38 | var finalResult;
39 | if(!res.results) {
40 | resolve([]);
41 | }
42 | else {
43 |
44 | itemsProcessed = res.results.products.product;
45 | itemsProcessed.forEach(function(item){
46 | item.title = item.name;
47 | item.image = item.image_url_large;
48 | item.price = item.price_min;
49 | });
50 |
51 | state.count = res.results.products.count;
52 | state.fetched = (((targetFeed.page - 1) * targetFeed.results_per_page) + itemsProcessed.length);
53 | state.hasMore = state.count > state.fetched;
54 |
55 | finalResult = {items:itemsProcessed, state: state};
56 | requestCache[queryParamsStr] = finalResult;
57 | resolve(_.cloneDeep(finalResult));
58 | }
59 | }).catch(function (err){
60 | reject(err);
61 | });
62 | }
63 |
64 | });
65 | }
66 |
67 |
68 | function getItemDetails(item) {
69 | return {
70 | type: 'url', // can be 'html', 'url', 'component'
71 | content: item.offers.offer[0].url
72 | };
73 | }
74 |
75 | module.exports = {
76 | getItems: getItems,
77 | getItemDetails: getItemDetails
78 | };
79 |
--------------------------------------------------------------------------------
/src/services/shop_fashion/feeds.js:
--------------------------------------------------------------------------------
1 | /******************************************
2 | * Here, are some common technology RSS feeds, with their respective:
3 | *
4 | * name - The title of the tab
5 | * color - The color identified with the feed. Play around with the theme and see how this affects the UI.
6 | * feed - The url of the feed. If it is in XML use the rss2json function.
7 | *
8 | *************************************/
9 |
10 | module.exports = [
11 | {
12 | name: 'Jeans',
13 | color: '#111',
14 | layout: {
15 | cellWidth: 150,
16 | imgSizeHeightToWidthRatio: 2.2,
17 | },
18 |
19 | config: {
20 | fts: "Jeans",
21 | },
22 | },
23 | {
24 | name: 'Dresses',
25 | color: '#111',
26 | layout: {
27 | cellWidth: 150,
28 | imgSizeHeightToWidthRatio: 2.2,
29 | },
30 | config: {
31 | fts: "Dress",
32 | },
33 | },
34 | {
35 | name: 'Hats',
36 | color: '#111',
37 | layout: {
38 | cellWidth: 150,
39 | imgSizeHeightToWidthRatio: 1,
40 | scaleMode: 'fit'
41 | },
42 |
43 | config: {
44 | fts: "summer hat",
45 | },
46 | },
47 | {
48 | name: 'watches',
49 | color: '#111',
50 | layout: {
51 | cellWidth: 110,
52 | imgSizeHeightToWidthRatio: 1.4,
53 | scaleMode: 'fit'
54 | },
55 | config: {
56 | fts: "watch",
57 | },
58 | },
59 | {
60 | name: 'Bags',
61 | color: '#111',
62 | layout: {
63 | cellWidth: 150,
64 | imgSizeHeightToWidthRatio: 1,
65 | scaleMode: 'fit'
66 | },
67 |
68 | config: {
69 | fts: "bag",
70 | },
71 | },
72 |
73 | // Just watches
74 | //{
75 | // name: 'G-shock',
76 | // color: '#333',
77 | // config: {
78 | // fts: "G-shock watch",
79 | // },
80 | //},
81 | //{
82 | // name: 'Fossil',
83 | // color: '#333',
84 | // config: {
85 | // fts: "Fossil Watch Men",
86 | // },
87 | //},
88 | //{
89 | // name: 'Breitling',
90 | // color: '#333',
91 | // config: {
92 | // fts: "Breitling watch",
93 | // },
94 | //},
95 | //{
96 | // name: 'Rolex',
97 | // color: '#333',
98 | // config: {
99 | // fts: "Rolex Watch Men",
100 | // },
101 | //},
102 | //{
103 | // name: 'Pink',
104 | // color: '#333',
105 | // config: {
106 | // fts: "Pink watch",
107 | // },
108 | //},
109 |
110 | //{
111 | // name: 'Earrings',
112 | // color: '#333',
113 | // config: {
114 | // fts: "Earrings",
115 | // },
116 | //},
117 | //{
118 | // name: 'Necklaces',
119 | // color: '#333',
120 | // config: {
121 | // fts: "Necklace",
122 | // },
123 | //},
124 | //{
125 | // name: 'Rings',
126 | // color: '#333',
127 | // config: {
128 | // fts: "ring",
129 | // },
130 | //},
131 | //{
132 | // name: 'watches',
133 | // color: '#333',
134 | // config: {
135 | // fts: "watch",
136 | // },
137 | //},
138 | //{
139 | // name: 'Bags',
140 | // color: '#333',
141 | // config: {
142 | // fts: "bag",
143 | // },
144 | //},
145 |
146 | ];
147 |
--------------------------------------------------------------------------------
/src/services/shop_fashion/index.js:
--------------------------------------------------------------------------------
1 | /***************************
2 | * Shopstyle Collective Api
3 | *
4 | * For the most part, services are independent of the platform the are running on.
5 | * This means they could function similarly in a browser, or a node.js server.
6 | *
7 | ****************************/
8 | var API_KEY = 'uid4961-26577031-68' ;
9 | var PAGESIZE = 60;
10 |
11 | var requestCache = {};
12 | var _ = require("lodash");
13 |
14 | function getItems(feedConfig , overideConfig){
15 | return new Promise(function(resolve, reject) {
16 |
17 | var targetFeed = _.cloneDeep(feedConfig.config);
18 | if(overideConfig && overideConfig.page){
19 | targetFeed.page = overideConfig.page;
20 | }
21 | else {
22 | targetFeed.page = 1;
23 | }
24 |
25 | var queryParamsStr = "limit="+ PAGESIZE +"&offset="+ ( PAGESIZE * (targetFeed.page-1) ) +"&";
26 | var tmp = [];
27 | for (var key in targetFeed){
28 | tmp.push(key + "=" + encodeURIComponent( targetFeed[key] ));
29 | }
30 | queryParamsStr += tmp.join("&");
31 |
32 | if(requestCache[queryParamsStr]){
33 | // This has been requested before
34 | setTimeout(function(){
35 | resolve(_.cloneDeep(requestCache[queryParamsStr]));
36 | },1);
37 | }
38 | else {
39 | fetch( "http://api.shopstyle.com/api/v2/products?pid="+ API_KEY +"&" + queryParamsStr ).then(function( res ){
40 | return res.json();
41 |
42 | }).then(function( res ){
43 | var itemsProcessed, state = {};
44 | var finalResult;
45 |
46 | if(!res.products) {
47 | resolve([]);
48 | }
49 | else {
50 | itemsProcessed = res.products;
51 | itemsProcessed.forEach(function(item){
52 | item.title = item.name;
53 | item.image = item.image.sizes.Original.url;
54 | item.price_display = '$'+numberWithCommas((Math.round(item.price)));
55 | });
56 |
57 | state.count = res.metadata.total;
58 | state.fetched = (((targetFeed.page - 1) * PAGESIZE) + itemsProcessed.length);
59 | state.hasMore = state.count > state.fetched;
60 |
61 | finalResult = {items:itemsProcessed, state: state};
62 | requestCache[queryParamsStr] = finalResult;
63 | resolve(_.cloneDeep(finalResult));
64 | }
65 | }).catch(function (err){
66 | reject(err);
67 | });
68 | }
69 |
70 | });
71 | }
72 |
73 |
74 | function getItemDetails(item) {
75 | return {
76 | type: 'url', // can be 'html', 'url', 'component'
77 | content: item.clickUrl
78 | };
79 | }
80 |
81 | function numberWithCommas(x) {
82 | return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
83 | }
84 |
85 | module.exports = {
86 | getItems: getItems,
87 | getItemDetails: getItemDetails
88 | };
89 |
--------------------------------------------------------------------------------
/src/services/wordpress_eclipsesource/feed_helpers.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Created by shaialon on 16/02/2016.
3 | */
4 |
5 | function extractFirstImageFromHtml(html) {
6 | var m,
7 | rex = /
]+src="?([^"\s]+)"?[^>]*\>/g;
8 |
9 | m = rex.exec( html );
10 | if(m && m[1]) { return m[1]; }
11 | return null;
12 | }
13 |
14 | function resolveImageForFeedItem(feedItem) {
15 | var img = extractFirstImageFromHtml(feedItem.content.rendered);
16 | return img || '';
17 | }
18 |
19 | module.exports = {
20 | extractFirstImageFromHtml: extractFirstImageFromHtml,
21 | resolveImageForFeedItem: resolveImageForFeedItem
22 | };
23 |
--------------------------------------------------------------------------------
/src/services/wordpress_eclipsesource/feeds.js:
--------------------------------------------------------------------------------
1 | /******************************************
2 | * Here, are some common technology RSS feeds, with their respective:
3 | *
4 | * name - The title of the tab
5 | * color - The color identified with the feed. Play around with the theme and see how this affects the UI.
6 | * feed - The url of the feed.
7 | *
8 | *************************************/
9 |
10 |
11 | module.exports = [
12 | //{
13 | // name: 'Eclipsesource',
14 | // color: '#207A93',
15 | // feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts',
16 | //},
17 |
18 | {
19 | name: 'Releases',
20 | color: '#207A93',
21 | feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?search=release',
22 | },
23 | {
24 | name: 'Tabris.js',
25 | color: '#207A93',
26 | feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?search=tabris.js',
27 | },
28 | {
29 | name: 'RAP',
30 | color: '#207A93',
31 | feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?search=RAP',
32 | },
33 |
34 | //{
35 | // name: 'News',
36 | // color: '#207A93',
37 | // feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?categories=7',
38 | //},
39 | //{
40 | // name: 'Tabris.js',
41 | // color: '#207A93',
42 | // feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?tags=431',
43 | //},
44 | //{
45 | // name: 'RAP',
46 | // color: '#207A93',
47 | // feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?tags=64',
48 | //},
49 |
50 | //{
51 | // name: 'News',
52 | // color: '#207A93',
53 | // feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?filter[cat]=7',
54 | //},
55 | //{
56 | // name: 'Tabris.js',
57 | // color: '#207A93',
58 | // feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?filter[tag_id]=431',
59 | //},
60 | //{
61 | // name: 'RAP',
62 | // color: '#207A93',
63 | // feed: 'http://eclipsesource.com/blogs/wp-json/wp/v2/posts?filter[tag_id]=rap',
64 | //},
65 |
66 | ];
67 |
--------------------------------------------------------------------------------
/src/services/wordpress_eclipsesource/index.js:
--------------------------------------------------------------------------------
1 | /***************************
2 | * For the most part, services are independent of the platform the are running on.
3 | * This means they could function similarly in a browser, or a node.js server.
4 | *
5 | ****************************/
6 |
7 | var feedHelpers = require('./feed_helpers');
8 |
9 | function getItems(feedConfig){
10 | return new Promise(function(resolve, reject) {
11 | fetch( feedConfig.feed ).then(function( res ){
12 | return res.json();
13 | }).then(function( res ){
14 | var items = res;
15 | items.forEach(function(item){
16 | item.title = item.title.rendered;
17 | item.image = feedHelpers.resolveImageForFeedItem(item);
18 | });
19 | resolve({items:items});
20 | }).catch(function (err){
21 | reject(err);
22 | });
23 | });
24 | }
25 |
26 |
27 |
28 | function getItemDetails(item) {
29 | return {
30 | type: 'url', // can be 'html', 'url', 'component'
31 | content: item.link
32 | };
33 | }
34 |
35 |
36 | module.exports = {
37 | getItems: getItems,
38 | getItemDetails: getItemDetails
39 | };
40 |
--------------------------------------------------------------------------------
/src/services/wordpress_pets/feeds.js:
--------------------------------------------------------------------------------
1 | /******************************************
2 | * Here, are some common technology RSS feeds, with their respective:
3 | *
4 | * name - The title of the tab
5 | * color - The color identified with the feed. Play around with the theme and see how this affects the UI.
6 | * feed - The url of the feed.
7 | *
8 | *************************************/
9 |
10 |
11 | module.exports = [
12 | {
13 | name: 'Dogs',
14 | color: '#60bbe2',
15 | feed: 'http://www.adoptapet.ie/wp-json/posts?type=dogs',
16 | },
17 | {
18 | name: 'Cats',
19 | color: '#E53F2C',
20 | feed: 'http://www.adoptapet.ie/wp-json/posts?type=cats', //TODO: & page=2
21 | },
22 | {
23 | name: 'News',
24 | color: '#333',
25 | feed: 'http://www.adoptapet.ie/wp-json/posts',
26 | },
27 |
28 | ];
29 |
--------------------------------------------------------------------------------
/src/services/wordpress_pets/index.js:
--------------------------------------------------------------------------------
1 | /***************************
2 | * For the most part, services are independent of the platform the are running on.
3 | * This means they could function similarly in a browser, or a node.js server.
4 | *
5 | ****************************/
6 |
7 |
8 | function getItems(feedConfig){
9 | return new Promise(function(resolve, reject) {
10 | fetch( feedConfig.feed ).then(function( res ){
11 | return res.json();
12 | }).then(function( res ){
13 | var items = res;
14 | items.forEach(function(item){
15 | item.name = item.title;
16 | item.image = item.featured_image ? (item.featured_image.source || item.featured_image.guid) : null ;
17 | });
18 | resolve({items:items});
19 | }).catch(function (err){
20 | reject(err);
21 | });
22 | });
23 | }
24 |
25 |
26 |
27 | function getItemDetails(item) {
28 | return {
29 | type: 'url', // can be 'html', 'url', 'component'
30 | content: item.link
31 | };
32 | }
33 |
34 |
35 | module.exports = {
36 | getItems: getItems,
37 | getItemDetails: getItemDetails
38 | };
39 |
--------------------------------------------------------------------------------