Repository: unbug/generator-webappstarter
Branch: master
Commit: 2b588e2d6bbb
Files: 149
Total size: 738.3 KB
Directory structure:
gitextract_xg95i7qs/
├── .editorconfig
├── .gitattributes
├── .gitignore
├── .jshintrc
├── .travis.yml
├── .yo-rc.json
├── LICENSE
├── README.md
├── app/
│ ├── index.js
│ └── templates/
│ ├── _package.json
│ ├── app/
│ │ ├── .babelrc
│ │ ├── README.md
│ │ ├── data/
│ │ │ └── user.json
│ │ ├── gulpfile.js
│ │ ├── html/
│ │ │ ├── debug/
│ │ │ │ ├── analytics.html
│ │ │ │ └── index.html
│ │ │ ├── include/
│ │ │ │ ├── cache.manifest
│ │ │ │ ├── components.html
│ │ │ │ ├── download.html
│ │ │ │ ├── footer.html
│ │ │ │ ├── htmlhead.html
│ │ │ │ ├── manifest-version.html
│ │ │ │ ├── msgbox-deja.html
│ │ │ │ ├── msgbox.html
│ │ │ │ ├── scripts-version.html
│ │ │ │ ├── styles-version.html
│ │ │ │ ├── tooltip.html
│ │ │ │ ├── view-home.html
│ │ │ │ ├── view-user.html
│ │ │ │ └── views.html
│ │ │ └── official/
│ │ │ ├── analytics.html
│ │ │ └── index.html
│ │ ├── scss/
│ │ │ ├── _animate.scss
│ │ │ ├── _box.scss
│ │ │ ├── _button.scss
│ │ │ ├── _common.scss
│ │ │ ├── _components.scss
│ │ │ ├── _fonts.scss
│ │ │ ├── _footer.scss
│ │ │ ├── _header.scss
│ │ │ ├── _icon.scss
│ │ │ ├── _list.scss
│ │ │ ├── _loading-spinner.scss
│ │ │ ├── _mixin.scss
│ │ │ ├── _msgbox-android.scss
│ │ │ ├── _msgbox-deja.scss
│ │ │ ├── _msgbox.scss
│ │ │ ├── _section-download.scss
│ │ │ ├── _slide.scss
│ │ │ ├── _sprites.scss
│ │ │ ├── _tooltip.scss
│ │ │ ├── _util.scss
│ │ │ ├── _value.scss
│ │ │ ├── _view-home.scss
│ │ │ ├── _view-user.scss
│ │ │ ├── _view.scss
│ │ │ └── styles.scss
│ │ ├── src/
│ │ │ ├── app/
│ │ │ │ ├── App.js
│ │ │ │ ├── controller/
│ │ │ │ │ ├── Controller.js
│ │ │ │ │ ├── HomeController.js
│ │ │ │ │ └── UserController.js
│ │ │ │ ├── model/
│ │ │ │ │ ├── Model.js
│ │ │ │ │ ├── RequestHelper.js
│ │ │ │ │ ├── StoreHelper.js
│ │ │ │ │ └── UserModel.js
│ │ │ │ ├── resources/
│ │ │ │ │ ├── Actions.js
│ │ │ │ │ ├── Audios.js
│ │ │ │ │ └── i18n/
│ │ │ │ │ ├── en_US/
│ │ │ │ │ │ ├── Msgbox.js
│ │ │ │ │ │ └── index.js
│ │ │ │ │ ├── index.js
│ │ │ │ │ └── zh_CN/
│ │ │ │ │ ├── Msgbox.js
│ │ │ │ │ └── index.js
│ │ │ │ └── view/
│ │ │ │ ├── HomeView.js
│ │ │ │ ├── UserView.js
│ │ │ │ └── View.js
│ │ │ ├── core/
│ │ │ │ ├── Class.js
│ │ │ │ ├── Event.js
│ │ │ │ ├── HashHandler.js
│ │ │ │ ├── MicroTmpl.js
│ │ │ │ ├── NativeBridge.js
│ │ │ │ ├── Navigator.js
│ │ │ │ ├── Pubsub.js
│ │ │ │ ├── Router.js
│ │ │ │ └── Subject.js
│ │ │ ├── lib/
│ │ │ │ ├── Core.js
│ │ │ │ ├── Core.standalone.js
│ │ │ │ ├── diffDOM.js
│ │ │ │ ├── hammer.js
│ │ │ │ ├── impress.js
│ │ │ │ ├── rebound.js
│ │ │ │ ├── swing/
│ │ │ │ │ ├── card.js
│ │ │ │ │ ├── dom.js
│ │ │ │ │ ├── sister.js
│ │ │ │ │ ├── stack.js
│ │ │ │ │ ├── swing.js
│ │ │ │ │ └── vendor-prefix.js
│ │ │ │ ├── velocity.js
│ │ │ │ ├── velocityui.js
│ │ │ │ ├── zepto.js
│ │ │ │ └── zepto.waypoints.js
│ │ │ ├── util/
│ │ │ │ ├── AppCache.js
│ │ │ │ ├── Base64.js
│ │ │ │ ├── DateHandler.js
│ │ │ │ ├── Easing.js
│ │ │ │ ├── FacebookShare.js
│ │ │ │ ├── FormHandler.js
│ │ │ │ ├── GUID.js
│ │ │ │ ├── ImpressGenerator.js
│ │ │ │ ├── LocalHost.js
│ │ │ │ ├── LocalParam.js
│ │ │ │ ├── LocalStorage.js
│ │ │ │ ├── MetaHandler.js
│ │ │ │ ├── Number.js
│ │ │ │ ├── OpenAppInBrowser.js
│ │ │ │ ├── RandomColor.js
│ │ │ │ ├── RandomList.js
│ │ │ │ ├── RequestAnimationFrame.js
│ │ │ │ ├── RequestHandler.js
│ │ │ │ ├── Scratch.js
│ │ │ │ ├── SeededRandom.js
│ │ │ │ ├── Shake.js
│ │ │ │ ├── SimpleSlider.js
│ │ │ │ ├── Slider.js
│ │ │ │ ├── SlotMachine.js
│ │ │ │ ├── TabStatus.js
│ │ │ │ ├── ThirdVendor.js
│ │ │ │ ├── Unveil.js
│ │ │ │ ├── VersionCompare.js
│ │ │ │ ├── VirtualDOMLite.js
│ │ │ │ ├── WaypointsHandler.js
│ │ │ │ ├── WechatShare.js
│ │ │ │ └── YiXinShare.js
│ │ │ └── widget/
│ │ │ ├── Msgbox.js
│ │ │ └── Tooltip.js
│ │ └── webpack.config.js
│ ├── editorconfig
│ ├── ftppass
│ ├── gitignore
│ └── jshintrc
├── model/
│ ├── index.js
│ └── templates/
│ └── Model.js
├── module/
│ ├── index.js
│ └── templates/
│ ├── ModuleController.js
│ ├── ModuleView.js
│ ├── module.html
│ └── module.scss
├── package.json
├── test/
│ └── test-app.js
└── update/
└── index.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitattributes
================================================
* text=auto
================================================
FILE: .gitignore
================================================
.DS_Store
Thumbs.db
.sass-cache
.idea
lib-cov
*.seed
*.log
*.csv
*.dat
*.out
*.pid
*.gz
pids
logs
results
npm-debug.log
node_modules
dist
================================================
FILE: .jshintrc
================================================
{
"node": true,
"esnext": true,
"bitwise": true,
"camelcase": true,
"curly": true,
"eqeqeq": true,
"immed": true,
"indent": 2,
"latedef": true,
"newcap": true,
"noarg": true,
"quotmark": "single",
"undef": true,
"unused": true,
"strict": true
}
================================================
FILE: .travis.yml
================================================
sudo: false
language: node_js
node_js:
- 'iojs'
- '0.12'
- '0.10'
================================================
FILE: .yo-rc.json
================================================
{
"generator-generator": {}
}
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2015 unbug
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
[](https://nodei.co/npm/generator-webappstarter/)
>Note: This generator is supporting a very early stage app,things gonna change very frequently,so please do not fork it or do any pull request.
Readme
=================
webappstarter generator will give you a Simple Mobile Web App Boilerplate and Structure!
>The App will automatically adjusts according to a device’s screen size without any extra work.
Install
========
```shell
npm install -g generator-webappstarter
```
Prereqs and installation requirements
=====================================
1.install [node](https://nodejs.org/) and [Python](https://www.python.org/).
2.install [yeoman](http://yeoman.io/).
```shell
npm install -g yo
```
3.[optional]Clone this git repo to your local,and from the root of the repo,run
```shell
npm link
```
to developing the generator locally.
Generator commands
==================
1.generate a new project,run
```shell
mkdir myProject
cd myProject
yo webappstarter
```
or run with `--skip-install` option to skip install dependencies
```shell
mkdir myProject
cd myProject
yo webappstarter --skip-install
```
install dependencies manually with `npm install` or just copy `node_modules` folder from another project which was generated by webappstarter.
2.generate a new module,run
```shell
//this command will do:
//add "html/include/view-modulename.html" and inlude it to "html/include/views.html"
//add "scss/_view-modulename.scss" and import it to "scss/_view.scss"
//add "src/app/view/ModuleNameView.js"
//add "src/app/controller/ModuleNameController.js" and require it in src/app/App.js
yo webappstarter:module ModuleName
```
3.generate a new model,run
```shell
//this command will do:
//add "src/app/model/ModelNameModel.js"
yo webappstarter:model ModelName
```
4.update your project's boilerplate and structure
```shell
//this command will update
//"./src/core" directory
//"./src/lib" directory
//"./src/util" directory
//"./src/widget" directory
//some files in "./src/app/" directory
//some files in "./scss/" directory
//some files in "./html/" directory
yo webappstarter:update
```
> Warning: When you are asked before an overwrite can occur,please be careful.Default "Y" is overwrite,"n" is skip.
More configurations,please take a look at "project" property of "package.json" file after the generator is done.
Run ```gulp``` to re-build project is required after change the "package.json" file.
Project commands
=================
run this command before you get started.
```shell
npm install -g gulp
```
1.build project,watch change and start browserSync,run
```shell
gulp
```
or run with forever
```shell
forever ./node_modules/.bin/gulp
```
2.deploy to test server,run
```shell
gulp deploytest
```
Please update your ftp auth name and password in ".ftppass".
View the page on test server [http://office.mozat.com:8083/PROJECTNAME/](http://office.mozat.com:8083/PROJECTNAME/).
This command require [openssl](https://www.openssl.org/).
For windows,you might needd to add openssl path to classpath.
3.deploy to offical server,run
```shell
gulp deploy
```
View the page on offical server [http://m.deja.me/PROJECTNAME/](http://m.deja.me/PROJECTNAME/).
This command require [rsync](https://rsync.samba.org/).
For windows,unzip /tools/rsync.zip to a local path and add the path to classpath.
4.run this command to copy source images to project's `resources/images/` path,then generate `scss/_sprites.csss` and `resources/images/sprites.png` for sourceSprites in `package.json`.
```shell
gulp copy
```
5.run this command to start jshint.
```shell
gulp jshint
```
6.run this command to start browserSync,Change browserSync options in `package.json`.
```shell
gulp serve
```
7.run this command to start pagespeed,Change pagespeed options in `package.json`.
```shell
gulp pagespeed
```
Structure
================
The structure is modular design,follow the DOOR-KEY rule you only take minutes to understand it:
* The DOOR for javascript is in ```/src/app/App.js```,and the KEY is ```require```,see [webpack](https://webpack.github.io/docs/commonjs.html) and [commonjs](http://www.commonjs.org/specs/modules/1.0/)
* The DOOR for stylesheets is in ```/scss/styles.scss```,and the KEY is ```@import```,see [SASS](http://sass-lang.com/)
* The DOOR for HTML is in ```/html/debug/index.html```,and the KEY is ```@@include```,see [gulp-file-include](https://www.npmjs.com/package/gulp-file-include)
Git
==========
Random git commit message
```shell
git commit -m"`curl -s http://whatthecommit.com/index.txt`"
```
================================================
FILE: app/index.js
================================================
'use strict';
var yeoman = require('yeoman-generator');
var chalk = require('chalk');
var yosay = require('yosay');
module.exports = yeoman.generators.Base.extend({
constructor: function () {
yeoman.generators.Base.apply(this, arguments);
this.option('skip-install', {
desc: 'Whether dependencies should be installed',
defaults: false
});
},
askFor: function () {
var done = this.async();
// Have Yeoman greet the user.
this.log(yosay('I will give you a Simple Mobile Web App Boilerplate and Structure!'));
var prompts = [{
type: 'input',
name: 'name',
message: 'Give your project a name:(products|account|config|favorite|creation|feed|feedback|dilog|invite|vote|event|share|monitor|follow,are invalid names).',
default: this.appname // Default to current folder name
}];
this.prompt(prompts, function (answers) {
this.projectName = answers.name;
// Save user configuration options to .yo-rc.json file
this.config.set({
projectName: this.projectName
});
this.config.save();
done();
}.bind(this));
},
writing: {
app: function () {
this.template('_package.json', 'package.json');
this.directory('app', './');
},
projectfiles: function () {
this.fs.copy(
this.templatePath('ftppass'),
this.destinationPath('.ftppass')
);
this.fs.copy(
this.templatePath('gitignore'),
this.destinationPath('.gitignore')
);
this.fs.copy(
this.templatePath('editorconfig'),
this.destinationPath('.editorconfig')
);
this.fs.copy(
this.templatePath('jshintrc'),
this.destinationPath('.jshintrc')
);
}
},
install: function () {
this.installDependencies({
bower: false,
npm: true,
skipInstall: this.options['skip-install'],
callback: function () {
console.log('Everything is ready!');
}
});
}
});
================================================
FILE: app/templates/_package.json
================================================
{
"name": "<%=projectName%>",
"version": "0.0.1",
"description": "Hello <%=projectName%>",
"project": {
"invalidNames": "products|account|config|favorite|creation|feed|feedback|dilog|invite|vote|event|share|monitor|follow",
"title": "Title - <%=projectName%>",
"viewport": 750,
"manifest": false,
"sourceImg": "statics/UI/build/img/**/*.{png,jpg}",
"sourceSprites": "statics/UI/build/assets/*.png",
"watchJshint": true,
"watchScsslint": false,
"babel": false,
"browserSync": {
"open": true,
"server": {
"baseDir": "./"
}
},
"pagespeed": {
"url": "http://m.deja.me/<%=projectName%>/",
"options": {
"strategy": "mobile"
}
}
},
"dependencies": {},
"devDependencies": {
"babel": "^5.6.14",
"babel-core": "^6.6.5",
"babel-loader": "^6.2.4",
"babel-preset-es2015": "^6.6.0",
"babel-preset-react": "^6.5.0",
"browser-sync": "^2.11.1",
"chai": "^3.0.0",
"chai-as-promised": "^5.1.0",
"date-utils": "^1.2.16",
"del": "^1.1.1",
"eslint-config-unstandard": "^1.1.0",
"gulp": "^3.9.0",
"gulp-autoprefixer": "^2.1.0",
"gulp-cachebust": "0.0.5",
"gulp-cached": "^1.0.4",
"gulp-csso": "^1.0.0",
"gulp-file-include": "^0.8.0",
"gulp-if": "^1.2.5",
"gulp-imagemin": "^2.2.1",
"gulp-jshint": "^1.9.2",
"gulp-load-plugins": "^0.8.1",
"gulp-minify-html": "^1.0.6",
"gulp-replace": "^0.5.3",
"gulp-sass": "^2.2.0",
"gulp-scsslint": "0.0.5",
"gulp-sftp": "^0.1.4",
"gulp-size": "^1.2.1",
"gulp-sourcemaps": "^1.5.1",
"gulp-uglify": "^1.1.0",
"gulp-util": "^3.0.4",
"gulp-eslint": "^2.0.0",
"imagemin-pngquant": "^4.0.0",
"jshint-stylish": "^1.0.1",
"log4js": "^0.6.26",
"mocha": "^2.2.5",
"pem": "^1.7.1",
"protractor": "^2.5.1",
"psi": "^1.0.6",
"q": "^1.2.0",
"rsync": "^0.4.0",
"run-sequence": "^1.0.2",
"sprity": "^1.0.8",
"strip-ansi": "^3.0.1",
"through2": "^0.6.3",
"vinyl-source-stream": "^1.1.0",
"wd": "^0.3.12",
"wd-bridge": "0.0.2",
"webpack": "^1.12.14",
"webpack-dev-middleware": "^1.5.1",
"webpack-stream": "^3.1.0"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"web app",
"mozat"
],
"author": "unbug",
"license": "MIT"
}
================================================
FILE: app/templates/app/.babelrc
================================================
{ "presets": ["es2015","react"] }
================================================
FILE: app/templates/app/README.md
================================================
Readme
=================
This project require [node](https://nodejs.org/).
Run `npm install` to install dependencies before get started.
This project is generated by [generator-webappstarter](https://github.com/unbug/generator-webappstarter)
Project commands
=================
run this command before you get started.
```shell
npm install -g gulp
```
1.build project,watch change and start browserSync,run
```shell
gulp
```
or run with forever
```shell
forever ./node_modules/.bin/gulp
```
2.deploy to test server,run
```shell
gulp deploytest
```
Please update your ftp auth name and password in ".ftppass".
View the page on test server [http://office.mozat.com:8083/PROJECTNAME/](http://office.mozat.com:8083/PROJECTNAME/).
This command require [openssl](https://www.openssl.org/).
For windows,you might needd to add openssl path to classpath.
3.deploy to offical server,run
```shell
gulp deploy
```
View the page on offical server [http://m.deja.me/PROJECTNAME/](http://m.deja.me/PROJECTNAME/).
This command require [rsync](https://rsync.samba.org/).
For windows,unzip /tools/rsync.zip to a local path and add the path to classpath.
4.run this command to copy source images to project's `resources/images/` path,then generate `scss/_dubug-sprites.csss` and `resources/images/sprites.png` for sourceSprites in `package.json`.
```shell
gulp copy
```
5.run this command to start jshint.
```shell
gulp jshint
```
6.run this command to start browserSync,Change browserSync options in `package.json`.
```shell
gulp serve
```
7.run this command to start pagespeed,Change pagespeed options in `package.json`.
```shell
gulp pagespeed
```
Structure
================
The structure is modular design,follow the DOOR-KEY rule you only take minutes to understand it:
* The DOOR for javascript is in ```/src/app/App.js```,and the KEY is ```require```,see [webpack](https://webpack.github.io/docs/commonjs.html) and [commonjs](http://www.commonjs.org/specs/modules/1.0/)
* The DOOR for stylesheets is in ```/scss/styles.scss```,and the KEY is ```@import```,see [SASS](http://sass-lang.com/)
* The DOOR for HTML is in ```/html/debug/index.html```,and the KEY is ```@@include```,see [gulp-file-include](https://www.npmjs.com/package/gulp-file-include)
Git
==========
Random git commit message
```shell
git commit -m"`curl -s http://whatthecommit.com/index.txt`"
```
================================================
FILE: app/templates/app/data/user.json
================================================
[
{"name": "Peter"},
{"name": "Joy"},
{"name": "Brian"}
]
================================================
FILE: app/templates/app/gulpfile.js
================================================
var spawn = require('child_process').spawn;
var exec = require('child_process').exec;
var fs = require('fs');
var path = require('path');
var gulp = require('gulp');
var source = require('vinyl-source-stream');
var through2 = require('through2');
var $ = require('gulp-load-plugins')();
var del = require('del');
var pem = require('pem');
var rsync = require('rsync');
var sprity = require('sprity');
var pngquant = require('imagemin-pngquant');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync').create();//http://www.browsersync.io/docs/gulp/
var pagespeed = require('psi');
var cachebust = new $.cachebust();
var webpackStream = require("webpack-stream");
require('date-utils');
//values
var conf = require('./package.json');
var projectName = conf.name;
//not available project names
var notAvailableNames = conf.project.invalidNames;
if (notAvailableNames.split('|').indexOf(projectName) != -1) {
console.log('\n"' + notAvailableNames + '" are not available project names!\n');
return;
}
var title = conf.project.title || conf.name;
var viewport = conf.project.viewport || 'device-width';
var globalVersion = conf.version;
var distPath = './dist/';
var distProjectPath = distPath + projectName;
var sourceImg = conf.project.sourceImg;//source images path to copy to this project
var sourceSprites = conf.project.sourceSprites;//source images path to generate sprites
var isBuild = false;
//build version:
//script version
//style version
//manifest version
var startTime = 0;
var buildVersion = 0;
gulp.task('build_version', function (cb) {
var startDate = new Date();
startTime = startDate.getTime();
buildVersion = startDate.toFormat('YYYYMMDDHHMISS');
cb();
});
//clear images
gulp.task('clean:images', function (cb) {
del(['./resources/images/*'], {force: true}, cb);
});
//copy source images to "/resources/images"
gulp.task('copy:source_imgs', function () {
return gulp.src([sourceImg])
.pipe(gulp.dest('./resources/images'))
.pipe($.size({title: 'copy source images'}));
});
//generate sprites.png and _debug-sprites.scss
gulp.task('sprites', function () {
return sprity.src({
src: sourceSprites,
name: 'sprites',
style: '_sprites.scss',
cssPath: '../images/',
processor: 'css'
})
.pipe($.if('*.png', gulp.dest('./resources/images/'), gulp.dest('./scss/')))
.pipe($.size({title: 'sprites'}));
});
//watching script change to start default task
gulp.task('watch', function () {
return gulp.watch([
'src/**/*.js', '!src/app.js',
'scss/**/*.scss',
'html/**/*.html'
], function (event) {
console.log('File ' + event.path + ' was ' + event.type + ', running tasks...');
var tasks = ['prepare'];
if (/.*\.js$/.test(event.path)) {
//jshint
conf.project.watchJshint && gulp.src(event.path)
.pipe($.jshint())
.pipe($.jshint.reporter('jshint-stylish'));
tasks.push('webpackjs');
}
else if (/.*\.scss$/.test(event.path)) {
//scsslint
//https://github.com/noahmiller/gulp-scsslint
conf.project.watchScsslint && gulp.src(event.path)
.pipe($.scsslint())
.pipe($.scsslint.reporter());
tasks.push('sass');
}
else if (/.*\.html$/.test(event.path)) {
tasks.push('html');
}
tasks.push('manifest');
runSequence.apply(null,tasks);
});
});
//compile sass files
var AUTOPREFIXER_BROWSERS = [
'ie >= 10',
'ie_mob >= 10',
'ff >= 30',
'chrome >= 34',
'safari >= 7',
'opera >= 23',
'ios >= 7',
'android >= 4.4',
'bb >= 10'
];
gulp.task('sass', function (cb) {
gulp.src(['./scss/**/*.scss'], {buffer: true})
.pipe($.replace(/_VIEWPORT_WIDTH_/g, viewport == 'device-width'?'100%': viewport+'px'))
.pipe(gulp.dest('./.scss'))//fix replace not working
.on('end', function () {
gulp.src(['./.scss/**/*.scss'], {buffer: true})
.pipe($.sourcemaps.init())
.pipe($.sass({errLogToConsole: true}))
.pipe($.cached('sass-cache', {
optimizeMemory: true
}))
.pipe($.autoprefixer({browsers: AUTOPREFIXER_BROWSERS}))
.pipe($.sourcemaps.write())
.pipe(gulp.dest('./resources/css/'))
.on('end', function () {
del(['./.scss'], {force: true});
cb();
});
});
});
//compile js files
var webpackConfig = require('./webpack.config');
gulp.task('webpackjs', function() {
return gulp.src(['./src/**/*.js'])
.pipe($.cached('webpackjs-cache', {
optimizeMemory: true
}))
.pipe(webpackStream(webpackConfig[isBuild?'build':'dev']))
.pipe(gulp.dest('./src'));
});
//compile html files
gulp.task('html', function () {
return gulp.src(['./html/debug/*.html'])
.pipe($.fileInclude({
basepath: './html/'
}))
.pipe($.cached('html-cache', {
optimizeMemory: true
}))
.pipe($.replace(/_BUILD_VERSION_/g, buildVersion))
.pipe($.replace(/_GLOBAL_VERSION_/g, globalVersion))
.pipe($.replace(/_VIEWPORT_WIDTH_/g, viewport))
.pipe($.replace(/_TITLE_/g, title))
.pipe($.replace(/_MANIFEST_/g, conf.project.manifest?'manifest="cache.manifest"':''))
.pipe(gulp.dest('./'));
});
//generate cache.manifest
gulp.task('manifest', function (cb) {
var resources = ['src/app.js'];
gulp.src(['./resources/**/*.*'])
.pipe(through2.obj(function (file, enc, next) {
this.push(file.path.replace(__dirname+'/',''));
next();
}))
.on('data', function (data) {
resources.push(data)
})
.on('end', function () {
gulp.src(['./html/include/cache.manifest'])
.pipe($.replace(/_BUILD_VERSION_/g, buildVersion))
.pipe($.replace(/_GLOBAL_VERSION_/g, globalVersion))
.pipe($.replace(/_FILES_/g, resources.join('\n')))
.pipe(gulp.dest('./'))
.on('end', function () {
cb();
});
});
});
//clear dist folder
gulp.task('clean:dist', function (cb) {
del([distPath + '*'], {force: true}, cb);
});
//copy resources to "/dist/resources/"
gulp.task('dist:resources', function () {
return gulp.src('./resources/**/*.*')
.pipe(gulp.dest(distProjectPath + '/resources/'))
.pipe($.size({title: 'dist:resources'}));
});
//compress images to dist
gulp.task('dist:images', function () {
return gulp.src(['./resources/**/*.*g'])
.pipe($.imagemin({
use: [pngquant()]
}))
.pipe(cachebust.resources())
.pipe(gulp.dest(distProjectPath + '/resources/'))
.pipe($.size({title: 'dist:images'}));
});
//compress css to dist
gulp.task('dist:css', function () {
return gulp.src('./resources/**/*.css')
.pipe(cachebust.references())
.pipe($.csso())
.pipe(cachebust.resources())
.pipe(gulp.dest(distProjectPath + '/resources'))
.pipe($.size({title: 'dist:css'}));
});
//compress js to dist
gulp.task('dist:js', function () {
return gulp.src(['./src/app.js'])
//remove //_DEBUG_ in files,
//such as: "///_DEBUG_*Todo: debug actions //*/ "
//will become "/*Todo: debug actions //*/",so uglify could remove all comments
.pipe($.replace(/\/\/_DEBUG_/g, ''))
.pipe(cachebust.references())
.pipe($.uglify())
.pipe(cachebust.resources())
.pipe(gulp.dest(distProjectPath + '/src/'))
.pipe($.size({title: 'dist:js'}));
});
//compress html to dist
gulp.task('dist:html', function () {
return gulp.src(['html/official/*.html'])
.pipe($.fileInclude({
basepath: './html/'
}))
.pipe($.replace(/_BUILD_VERSION_/g, buildVersion))
.pipe($.replace(/_GLOBAL_VERSION_/g, globalVersion))
.pipe($.replace(/_VIEWPORT_WIDTH_/g, viewport))
.pipe($.replace(/_TITLE_/g, title))
.pipe($.replace(/_MANIFEST_/g, conf.project.manifest?'manifest="cache.manifest"':''))
.pipe(cachebust.references())
.pipe($.minifyHtml({
empty: true,
cdata: true,
conditionals: true,
spare: true,
quotes: true
}))
.pipe(gulp.dest(distProjectPath))
.pipe($.size({title: 'dist:html'}));
});
//copy cache.manifest to dist
gulp.task('dist:manifest', function () {
return gulp.src('./cache.manifest')
.pipe(cachebust.references())
.pipe(gulp.dest(distProjectPath + '/'))
.pipe($.size({title: 'dist:manifest'}));
});
//deploy to test server
//view http://office.mozat.com:8081/m/PROJECTNAME/
gulp.task('deploy:test', function (cb) {
pem.createCertificate({}, function (err, kyes) {
gulp.src(distPath + '**/*')
.pipe($.sftp({
host: '172.28.2.62',
port: 22,
auth: 'testServer',
key: kyes.clientKey,
keyContents: kyes.keyContents,
remotePath: '/home/gaolu/m/'
}, cb));
});
});
//deploy to offical server
//view http://m.deja.me/PROJECTNAME/
gulp.task('deploy:offical', function (cb) {
//set rsync proxy
process.env.RSYNC_PROXY = 'proxy.lan:8080';
var client = new rsync()
.executable('rsync')
.flags('azv')
.source(distPath)
.destination('rsync://10.160.241.153/m.deja.me/');
client.execute(function (error, code, cmd) {
console.log('\t' + cmd);
//reset rsync proxy
process.env.RSYNC_PROXY = '';
cb();
});
});
//deploy to offical server
//view http://m.deja.me/PROJECTNAME/
// deploy guide
// 1. get rsync's port
// grep rsync /etc/services
// 2. open tunnel
// sudo ssh -N -L 127.0.0.1:873:10.160.241.153:873 tunnel@ssh.mozat.com
gulp.task('_deploy:offical', function (cb) {
//set rsync proxy
var client = new rsync()
.executable('rsync')
.flags('azv')
.source(distPath)
.destination('rsync://localhost/m.deja.me/');
client.execute(function (error, code, cmd) {
console.log('\t' + cmd);
//reset rsync proxy
process.env.RSYNC_PROXY = '';
cb();
});
});
// Lint JavaScript
gulp.task('jshint', function () {
return gulp.src(['./src/**/*.js', '!./src/*.js', '!./src/lib/zepto.js'])
.pipe($.jshint())
.pipe($.jshint.reporter('jshint-stylish'))
.pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});
//browser-sync serve
var browserSyncOptions = conf.project.browserSync || {};
gulp.task('serve', function () {
browserSync.init(browserSyncOptions);
gulp.watch(['./*.html'], browserSync.reload);
gulp.watch(['./src/*.js'], browserSync.reload);
gulp.watch(['./resources/**/*.css'], browserSync.reload);
gulp.watch(['./resources/**/*.*g'], browserSync.reload);
});
// Run PageSpeed Insights
gulp.task('pagespeed', function (cb) {
// By default we use the PageSpeed Insights free (no API key) tier.
// Use a Google Developer API key if you have one: http://goo.gl/RkN0vE
// key: 'YOUR_API_KEY'
pagespeed.output(conf.project.pagespeed.url, conf.project.pagespeed.options, function (err, data) {
cb();
});
});
//print after tasks all done
gulp.task('_endlog', function (cb) {
var endDate = new Date();
var logs = [];
logs.push('\nBuild version is ' + buildVersion);
logs.push(', Completed in ' + ((endDate.getTime() - startTime) / 1000) + 's at ' + endDate + '\n');
console.log(logs.join(''));
cb();
});
//test
function getProtractorBinary(binaryName){
var winExt = /^win/.test(process.platform)? '.cmd' : '';
var pkgPath = require.resolve('protractor');
var protractorDir = path.resolve(path.join(path.dirname(pkgPath), '..', 'bin'));
return path.join(protractorDir, '/'+binaryName+winExt);
}
gulp.task('protractor-install', function(done){
spawn(getProtractorBinary('webdriver-manager'), ['update'], {
stdio: 'inherit'
}).once('close', done);
});
gulp.task('wd', function(done){
spawn(getProtractorBinary('webdriver-manager'), ['start'], {
stdio: 'inherit'
}).once('close', done);
});
gulp.task('scenario', function (done) {
var argv = process.argv.slice(3); // forward args to protractor
argv.push('test/protractor.conf.js');
spawn(getProtractorBinary('protractor'), argv, {
stdio: 'inherit'
}).once('close', done);
});
//end test
function handleError(err) {
console.log(err.toString());
this.emit('end');
}
//task queues
gulp.task('copy', function (cb) {
runSequence('clean:images', 'copy:source_imgs', 'sprites', cb);
});
gulp.task('prepare', function (cb) {
runSequence('build_version', cb);
});
gulp.task('compile', function (cb) {
runSequence('sass', 'webpackjs', 'manifest', 'html', cb);
});
gulp.task('dist', function (cb) {
isBuild = true;
runSequence('clean:dist', 'prepare', 'compile','dist:resources', 'dist:images', 'dist:css', 'dist:js', 'dist:manifest','dist:html', '_endlog', cb);
});
gulp.task('default', function (cb) {
runSequence('prepare', 'compile', 'watch', 'serve', cb);
});
//deploy to test server
gulp.task('deploytest', function (cb) {
runSequence('dist', 'deploy:test', cb);
});
//deploy to offical server
gulp.task('deploy', function (cb) {
runSequence('dist', 'deploy:offical', cb);
});
//deploy to offical server
gulp.task('deployrc', function (cb) {
distProjectPath = distProjectPath +'_rc';
runSequence('deploy', cb);
});
//deploy to offical server from home
gulp.task('_deploy', function (cb) {
runSequence('dist', '_deploy:offical', cb);
});
gulp.task('_deployrc', function (cb) {
distProjectPath = distProjectPath +'_rc';
runSequence('_deploy', cb);
});
gulp.task('test', function (done) {
runSequence('scenario',done);
});
================================================
FILE: app/templates/app/html/debug/analytics.html
================================================
================================================
FILE: app/templates/app/html/debug/index.html
================================================
@@include('include/htmlhead.html')
@@include('include/styles-version.html')
@@include('include/views.html')
@@include('include/components.html')
@@include('include/scripts-version.html')
================================================
FILE: app/templates/app/html/include/cache.manifest
================================================
CACHE MANIFEST
# Version: _GLOBAL_VERSION_
# Build: _BUILD_VERSION_
CACHE:
_FILES_
NETWORK:
*
================================================
FILE: app/templates/app/html/include/components.html
================================================
@@include('include/msgbox.html')
@@include('include/tooltip.html')
================================================
FILE: app/templates/app/html/include/download.html
================================================
================================================
FILE: app/templates/app/html/include/footer.html
================================================
================================================
FILE: app/templates/app/html/include/htmlhead.html
================================================
_TITLE_
================================================
FILE: app/templates/app/html/include/manifest-version.html
================================================
================================================
FILE: app/templates/app/html/include/msgbox-deja.html
================================================
================================================
FILE: app/templates/app/html/include/msgbox.html
================================================
@@include('include/msgbox-deja.html')
================================================
FILE: app/templates/app/html/include/scripts-version.html
================================================
================================================
FILE: app/templates/app/html/include/styles-version.html
================================================
================================================
FILE: app/templates/app/html/include/tooltip.html
================================================
================================================
FILE: app/templates/app/html/include/view-home.html
================================================
================================================
FILE: app/templates/app/html/include/view-user.html
================================================
================================================
FILE: app/templates/app/html/include/views.html
================================================
@@include('include/view-home.html')
@@include('include/view-user.html')
================================================
FILE: app/templates/app/html/official/analytics.html
================================================
================================================
FILE: app/templates/app/html/official/index.html
================================================
@@include('include/htmlhead.html')
@@include('include/styles-version.html')
@@include('include/views.html')
@@include('include/components.html')
@@include('include/scripts-version.html')
================================================
FILE: app/templates/app/scss/_animate.scss
================================================
@charset "UTF-8";
/*
参考自 http://daneden.github.io/animate.css/
*/
/*
@-webkit-keyframes rotateRight{
@for $i from 0 through 45 {
none:2.22%*$i;-webkit-transform: rotate(8deg*$i);
}
}
*/
@-webkit-keyframes tada {
0% {
-webkit-transform: scale(1);
}
10%, 20% {
-webkit-transform: scale(0.6) rotate(-3deg);
}
30%, 50% {
-webkit-transform: scale(0.9) rotate(3deg);
}
70%, 90% {
-webkit-transform: scale(1.0) rotate(3deg);
}
40%, 60%, 80% {
-webkit-transform: scale(1.1) rotate(-3deg);
}
100% {
-webkit-transform: scale(1) rotate(0deg);
}
}
@-webkit-keyframes bounceIn {
0% {
opacity: 0;
-webkit-transform: scale(.3);
}
50% {
opacity: 1;
-webkit-transform: scale(1.05);
}
70% {
-webkit-transform: scale(.9);
}
100% {
opacity: 1;
-webkit-transform: scale(1);
}
}
@-webkit-keyframes MicroBounceOut {
0% {
opacity: 0;
-webkit-transform: scale3d(1, 1.03, 1) translate3d(0, 1, 0);
}
99% {
-webkit-transform: scale3d(1, .999, 1);
}
100% {
opacity: 1;
-webkit-transform: scale3d(1, 1, 1);
}
}
@-webkit-keyframes MicroPopUp {
0% {
opacity: 0;
-webkit-transform: scale3d(1.1, 1.1, 1);
}
90% {
-webkit-transform: scale3d(.99, .99, 1);
}
100% {
opacity: 1;
-webkit-transform: scale3d(1, 1, 1);
}
}
@-webkit-keyframes MicroMask {
0% {
opacity: 0;
}
100% {
opacity: .5;
}
}
@-webkit-keyframes bounceInDown {
0% {
opacity: 0;
-webkit-transform: translateY(-2000px);
}
60% {
opacity: 1;
-webkit-transform: translateY(30px);
}
80% {
-webkit-transform: translateY(-10px);
}
100% {
-webkit-transform: translateY(0);
opacity: 1;
}
}
@-webkit-keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@-webkit-keyframes fadeInOut {
0%, 100% {
opacity: 0;
}
40% {
opacity: 1;
}
}
@-webkit-keyframes fadeOut {
from {
opacity: 1;
}
to {
opacity: 0;
}
}
@-webkit-keyframes flash {
0%, 50%, 100% {
opacity: 1;
}
25%, 75% {
opacity: 0;
}
}
@-webkit-keyframes fadeInUp {
0% {
opacity: 0;
-webkit-transform: translateY(20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
}
}
@-webkit-keyframes fadeInUpMax {
0% {
opacity: 0;
-webkit-transform: translateY(100%);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
}
}
@-webkit-keyframes fadeInDown {
0% {
opacity: 0;
-webkit-transform: translateY(-20px);
}
100% {
opacity: 1;
-webkit-transform: translateY(0);
}
}
@-webkit-keyframes fadeInLeft {
0% {
opacity: 0;
-webkit-transform: translate3d(-100%, 0, 0);
}
90% {
opacity: .9;
-webkit-transform: translate3d(1%, 0, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
}
}
@-webkit-keyframes fadeInRight {
0% {
opacity: 0;
-webkit-transform: translate3d(100%, 0, 0);
}
90% {
opacity: .9;
-webkit-transform: translate3d(-1%, 0, 0);
}
100% {
opacity: 1;
-webkit-transform: none;
}
}
@-webkit-keyframes flipInY {
0% {
-webkit-transform: perspective(400px) rotateY(90deg);
transform: perspective(400px) rotateY(90deg);
opacity: 0;
}
40% {
-webkit-transform: perspective(400px) rotateY(-10deg);
transform: perspective(400px) rotateY(-10deg);
}
70% {
-webkit-transform: perspective(400px) rotateY(10deg);
transform: perspective(400px) rotateY(10deg);
}
100% {
-webkit-transform: perspective(400px) rotateY(0deg);
transform: perspective(400px) rotateY(0deg);
opacity: 1;
}
}
@-webkit-keyframes flipInX {
from {
-webkit-transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
transform: perspective(400px) rotate3d(1, 0, 0, 90deg);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
opacity: 0;
}
40% {
-webkit-transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
transform: perspective(400px) rotate3d(1, 0, 0, -20deg);
-webkit-animation-timing-function: ease-in;
animation-timing-function: ease-in;
}
60% {
-webkit-transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
transform: perspective(400px) rotate3d(1, 0, 0, 10deg);
opacity: 1;
}
80% {
-webkit-transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
transform: perspective(400px) rotate3d(1, 0, 0, -5deg);
}
to {
-webkit-transform: perspective(400px);
transform: perspective(400px);
}
}
@-webkit-keyframes zoomIn {
0% {
opacity: 0;
-webkit-transform: scale3d(.3, .3, .3);
transform: scale3d(.3, .3, .3);
}
50% {
opacity: 1;
}
}
@-webkit-keyframes slideInUp {
0% {
-webkit-transform: translateY(100%);
transform: translateY(100%);
visibility: visible;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
@-webkit-keyframes slideInDown {
0% {
-webkit-transform: translateY(-100%);
transform: translateY(-100%);
visibility: visible;
}
100% {
-webkit-transform: translateY(0);
transform: translateY(0);
}
}
@-webkit-keyframes slideOutDown {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
visibility: hidden;
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
}
@keyframes slideOutDown {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
visibility: hidden;
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
}
}
@-webkit-keyframes slideOutLeft {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
visibility: hidden;
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
}
}
@keyframes slideOutLeft {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
visibility: hidden;
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
}
}
@-webkit-keyframes slideOutRight {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
visibility: hidden;
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
}
@keyframes slideOutRight {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
visibility: hidden;
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0);
}
}
@-webkit-keyframes slideOutUp {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
visibility: hidden;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
}
@keyframes slideOutUp {
0% {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
}
100% {
visibility: hidden;
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0);
}
}
@-webkit-keyframes spinnerRotateRight {
0% {
-webkit-transform: rotate(0deg);
}
8.32% {
-webkit-transform: rotate(0deg);
}
8.33% {
-webkit-transform: rotate(30deg);
}
16.65% {
-webkit-transform: rotate(30deg);
}
16.66% {
-webkit-transform: rotate(60deg);
}
24.99% {
-webkit-transform: rotate(60deg);
}
25% {
-webkit-transform: rotate(90deg);
}
33.32% {
-webkit-transform: rotate(90deg);
}
33.33% {
-webkit-transform: rotate(120deg);
}
41.65% {
-webkit-transform: rotate(120deg);
}
41.66% {
-webkit-transform: rotate(150deg);
}
49.99% {
-webkit-transform: rotate(150deg);
}
50% {
-webkit-transform: rotate(180deg);
}
58.32% {
-webkit-transform: rotate(180deg);
}
58.33% {
-webkit-transform: rotate(210deg);
}
66.65% {
-webkit-transform: rotate(210deg);
}
66.66% {
-webkit-transform: rotate(240deg);
}
74.99% {
-webkit-transform: rotate(240deg);
}
75% {
-webkit-transform: rotate(270deg);
}
83.32% {
-webkit-transform: rotate(270deg);
}
83.33% {
-webkit-transform: rotate(300deg);
}
91.65% {
-webkit-transform: rotate(300deg);
}
91.66% {
-webkit-transform: rotate(330deg);
}
100% {
-webkit-transform: rotate(330deg);
}
}
================================================
FILE: app/templates/app/scss/_box.scss
================================================
@charset "UTF-8";
.box-h{
@include box-h;
}
.box-h-c-c{
@include box-h-c-c;
}
.box-h-c-r{
@include box-h-c-r;
}
.box-h-c-l{
@include box-h-c-l;
}
.box-h-t-c{
@include box-h-t-c;
}
.box-h-t-l{
@include box-h-t-l;
}
.box-h-t-r{
@include box-h-t-r;
}
.box-h-b-c{
@include box-h-b-c;
}
.box-h-b-l{
@include box-h-b-l;
}
.box-h-b-r{
@include box-h-b-r;
}
.box-v{
@include box-v;
}
.box-v-c-c{
@include box-v-c-c;
}
.box-v-c-l{
@include box-v-c-l;
}
.box-v-c-r{
@include box-v-c-r;
}
.box-v-t-c{
@include box-v-t-c;
}
.box-v-t-l{
@include box-v-t-l;
}
.box-v-t-r{
@include box-v-t-r;
}
.box-v-b-c{
@include box-v-b-c;
}
.box-v-b-l{
@include box-v-b-l;
}
.box-v-b-r{
@include box-v-b-r;
}
================================================
FILE: app/templates/app/scss/_button.scss
================================================
@charset "UTF-8";
.btn {
display: inline-block;
text-align: center;
@include active-default;
@include disabled-select;
}
.btn-2i {
i {
&:nth-child(2), &.active {
display: none;
}
}
&:active,&.on {
i {
&:nth-child(1), &.normal {
display: none;
}
&:nth-child(2), &.active {
display: inline-block;
}
}
}
&.dis-active{
&:active{
i {
&:nth-child(1), &.normal {
display: inline-block;
}
&:nth-child(2), &.active {
display: none;
}
}
}
}
}
.bt-primary,.bt-default {
height: 100px;
line-height: 98px;
font-size: 38px;
padding: 0 30px;
text-transform: uppercase;
&.size-s{
padding: 0 20px;
font-size: 28px;
height: 70px;
line-height: 68px;
}
}
.bt-primary{
color: #fff;
background-color: $color-black-fourth;
&:active {
background-color: $color-red;
}
&.disabled{
background-color: $color-gray-third;
&:active{
background-color: $color-gray-third;
}
}
}
.bt-default {
border: 2px solid $color-black-second;
color: $color-black-fourth;
background: #fff;
&:active {
border: 2px solid $color-red;
color: #fff;
background-color: $color-red;
}
&.disabled{
color: #cfcfcf;
&:active{
border: 2px solid $color-gray;
color: #f1f1f1;
background-color: inherit;
}
}
}
================================================
FILE: app/templates/app/scss/_common.scss
================================================
@charset "UTF-8";
/* http://css-tricks.com/poll-results-how-do-you-order-your-css-properties/ */
body, html {
text-align: left;
height: 100%; /*高度必须为100%*/
width: 100%;
font-family: Roboto, HelveticaNeue, Helvetica, Arial, sans-serif;
font-size: 16px;
font-weight: normal;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-align: center;
}
body {
width: $layout-with;
min-height: 100%;
height: auto;
}
* {
margin: 0;
padding: 0;
border: 0;
-webkit-user-select: text;
word-break: normal;
}
*, :after, :before {
-webkit-box-sizing: border-box; /*高宽不算padding,margin*/
-webkit-text-size-adjust: none;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
a, img {
-webkit-touch-callout: none; /*禁用长按弹出系统菜单*/
}
a {
text-decoration: none;
}
section, p, div {
max-height: 999999px;
}
textarea, input[type="password"], input[type="text"] {
resize: none; /*禁用可缩放*/
outline: none; /*禁用发光效果*/
-webkit-appearance: none; /*禁用增加外观效果*/
white-space: pre-wrap;
word-wrap: break-word;
background: #fff;
overflow: scroll;
}
audio {
width: 0;
height: 0;
padding: 0;
margin: 0;
opacity: 0;
visibility: hidden;
display: none;
position: absolute;
top: 0;
left: 0;
}
================================================
FILE: app/templates/app/scss/_components.scss
================================================
@charset "UTF-8";
/*debug-button.scss*/
@import "button.scss";
/*end debug-button.scss*/
/*debug-slide.scss*/
@import "slide.scss";
/*end debug-slide.scss*/
/*debug-msgbox.scss*/
@import "msgbox.scss";
/*end debug-msgbox.scss*/
/*debug-tooltip.scss*/
@import "tooltip.scss";
/*end debug-tooltip.scss*/
/*debug-loading-spinner.scss*/
@import "loading-spinner.scss";
/*end debug-loading-spinner.scss*/
/*debug-section-download.scss*/
@import "section-download.scss";
/*end debug-section-download.scss*/
================================================
FILE: app/templates/app/scss/_fonts.scss
================================================
@charset "UTF-8";
================================================
FILE: app/templates/app/scss/_footer.scss
================================================
@charset "UTF-8";
.footer-section {
margin-top: -1px;
width: 100%;
background-color: #3d1c21;
padding: 45px 0;
color: #fff;
> .stores {
@include box-h-c-c;
a {
display: inline-block;
margin: 0 20px;
}
}
> .about {
font-size: 24px;
margin-top: 18px;
p {
line-height: 30px;
text-align: center;
}
.a, a:active, a:link, a:hover {
color: #fff;
text-decoration: underline;
}
}
}
================================================
FILE: app/templates/app/scss/_header.scss
================================================
@charset "UTF-8";
.tabs-title-default {
@include box-h;
-webkit-border-radius: 10px;
background-color: #eeeeee;
overflow: hidden;
border: 2px solid #acacac;
> div {
@include box-h-c-c;
-webkit-box-flex: 1;
width: 50%;
font-size: 28px;
padding: 12px 0;
color: #acacac;
background-color: #f0f0f0;
@include active-default;
&.first {
-webkit-border-top-left-radius: 8px;
-webkit-border-bottom-left-radius: 8px;
}
&.last {
-webkit-border-top-right-radius: 8px;
-webkit-border-bottom-right-radius: 8px;
}
&.on {
color: #fff;
background-color: #acacac;
}
}
}
.tabs-title-underline {
position: relative;
@include box-h;
height: 70px;
width: 100%;
padding: 0 28px;
background-color: #fff;
color: #747474;
> article {
position: relative;
@include box-h-c-c;
height: 100%;
-webkit-box-flex: 1;
@include active-default;
> .title {
height: 100%;
width: 100%;
text-align: center;
line-height: 70px;
font-size: 32px;
}
> .underline {
position: absolute;
bottom: 0;
left: 0;
@include box-h-c-c;
height: 5px;
width: 100%;;
> div {
opacity: 0;
height: 0;
width: 0;
background-color: #e84142;
-webkit-transition: all .35s ease-in-out;
}
}
> .notify {
display: none;
z-index: 2;
position: absolute;
width: 8px;
height: 8px;
top: 14px;
right: 14px;
background-color: #e84142;
-webkit-border-radius: 100%;
&.on {
@include box-h;
}
}
&.on {
color: #292929;
> .underline {
> div {
opacity: 1;
height: 100%;
width: 80%;
}
}
}
}
}
.section-title-default {
@include box-h-c-l;
height: 100px;
line-height: 100px;
padding: 0 20px;
> div {
display: -webkit-box;
}
> .dot {
width: 10px;
height: 10px;
-webkit-border-radius: 5px;
background-color: $color-black-third;
}
> .title {
padding-left: 12px;
font-size: 36px;
line-height: 38px;
color: $color-black-third;
-webkit-box-flex: 1;
/*
&.animated{
@include animated-1s;
-webkit-animation-name: fadeInLeft;
}
*/
}
> .link {
> a, > a:active, > a:hover, > a:visited {
font-size: 32px;
color: #00a0e9;
}
}
&.bar {
height: 120px;
line-height: 120px;
> .dot {
width: 4px;
height: 30px;
background-color: $color-red;
-webkit-border-radius: 0;
}
> .title {
color: $color-black-second;
padding-left: 20px;
}
}
&.center {
@include box-h-c-c;
.title {
display: inline-block;
}
.dot {
margin-top: -14px;
}
}
}
.section-title-similar{
width: 100%;
height: 48px;
padding: 0 40px;
line-height: 48px;
font-size: 28px;
color: $color-gray-second;
background-color: #f9f9f9;
}
================================================
FILE: app/templates/app/scss/_icon.scss
================================================
@charset "UTF-8";
/*debug-sprites.scss*/
@import "sprites.scss";
/*end debug-sprites.scss*/
.icon {
background-repeat: no-repeat;
display: inline-block;
@include disabled-select;
}
================================================
FILE: app/templates/app/scss/_list.scss
================================================
@charset "UTF-8";
.tabs-content-default {
display: none;
&.show {
display: block;
@include animated-1s;
}
&.animated {
-webkit-animation-name: fadeInUp;
}
}
.user-item-default {
@include box-v;
width: 100%;
font-size: 38px;
> .item {
@include box-h;
width: 100%;
padding: 30px;
text-align: center;
}
}
================================================
FILE: app/templates/app/scss/_loading-spinner.scss
================================================
@charset "UTF-8";
.loading-spinner {
font-size: 120%;
height: 1em;
width: 1em;
position: relative;
-webkit-transform-origin: 0.5em 0.5em;
> span {
display: block;
position: absolute;
width: 0.1em;
height: 0.25em;
top: 0;
-webkit-transform-origin: 0.05em 0.5em;
-webkit-border-radius: 0.05em;
border-radius: 0.05em;
content: " ";
&:before, &:after {
display: block;
position: absolute;
width: 0.1em;
height: 0.25em;
top: 0;
-webkit-transform-origin: 0.05em 0.5em;
-webkit-border-radius: 0.05em;
border-radius: 0.05em;
content: " ";
}
&.loading-top {
background-color: rgba(0, 0, 0, 0.99);
&:after {
background-color: rgba(0, 0, 0, 0.9);
}
}
&.loading-left {
background-color: rgba(0, 0, 0, 0.7);
&:before {
background-color: rgba(0, 0, 0, 0.8);
}
&:after {
background-color: rgba(0, 0, 0, 0.6);
}
}
&.loading-bottom {
background-color: rgba(0, 0, 0, 0.4);
&:before {
background-color: rgba(0, 0, 0, 0.5);
}
&:after {
background-color: rgba(0, 0, 0, 0.35);
}
}
&.loading-right {
background-color: rgba(0, 0, 0, 0.25);
&:before {
background-color: rgba(0, 0, 0, 0.3);
}
&:after {
background-color: rgba(0, 0, 0, 0.2);
}
}
&.loading-top {
&:before {
background-color: rgba(0, 0, 0, 0.15);
}
-webkit-transform: rotate(0deg);
}
left: 50%;
margin-left: -0.05em;
&.loading-right {
-webkit-transform: rotate(90deg);
}
&.loading-bottom {
-webkit-transform: rotate(180deg);
}
&.loading-left {
-webkit-transform: rotate(270deg);
}
&:before {
-webkit-transform: rotate(30deg);
}
&:after {
-webkit-transform: rotate(-30deg);
}
}
-webkit-animation-name: spinnerRotateRight;
-webkit-animation-duration: .8s;
-webkit-animation-iteration-count: infinite;
-webkit-animation-timing-function: linear;
}
================================================
FILE: app/templates/app/scss/_mixin.scss
================================================
@charset "UTF-8";
@mixin animated-1s {
animation-duration: 1s;
animation-iteration-count: 1;
animation-fill-mode: both;
animation-timing-function: ease-in-out;
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: 1;
-webkit-animation-fill-mode: both;
-webkit-animation-timing-function: ease-in-out;
}
@mixin blank-bg {
background-image: url('about:blank');
/*
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAABBJREFUeNpi+P//PwNAgAEACPwC/tuiTRYAAAAASUVORK5CYII=);
background-repeat: repeat;
*/
display: block !important;
}
@mixin box-h {
display: -webkit-box;
-webkit-box-orient: horizontal;
}
@mixin box-h-c-c {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: center;
-webkit-box-align: center;
}
@mixin box-h-c-r {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: end;
-webkit-box-align: center;
}
@mixin box-h-c-l {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-align: center;
-webkit-box-pack: start;
}
@mixin box-h-t-c {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-align: start;
-webkit-box-pack: center;
}
@mixin box-h-t-l {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-align: start;
-webkit-box-pack: start;
}
@mixin box-h-t-r {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-align: start;
-webkit-box-pack: end;
}
@mixin box-h-b-c {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: center;
-webkit-box-align: end;
}
@mixin box-h-b-l {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: start;
-webkit-box-align: end;
}
@mixin box-h-b-r {
display: -webkit-box;
-webkit-box-orient: horizontal;
-webkit-box-pack: end;
-webkit-box-align: end;
}
@mixin box-v {
display: -webkit-box;
-webkit-box-orient: vertical;
}
@mixin box-v-c-c {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-align: center;
-webkit-box-pack: center;
}
@mixin box-v-c-l {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-pack: center;
-webkit-box-align: start;
}
@mixin box-v-c-r {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-pack: center;
-webkit-box-align: end;
}
@mixin box-v-t-c {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-align: center;
}
@mixin box-v-t-l {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-align: start;
-webkit-box-pack: start;
}
@mixin box-v-t-r {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-align: start;
-webkit-box-pack: end;
}
@mixin box-v-b-c {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-pack: end;
-webkit-box-align: center;
}
@mixin box-v-b-l {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-pack: end;
-webkit-box-align: start;
}
@mixin box-v-b-r {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-box-pack: end;
-webkit-box-align: end;
}
@mixin active-default {
&:active {
opacity: $active-opacity;
}
}
@mixin active-ripple {
position: relative;
overflow: hidden;
&:after {
content: "";
position: absolute;
left: 50%;
top: 50%;
display: inherit;
padding-top: $layout-with;
padding-left: $layout-with;
margin-top: -$layout-with/2;
margin-left: -$layout-with/2;
border-radius: 50%;
opacity: 0;
background: rgba(255,255,255,0.5);
transition: all 1s;
}
&:active:after {
padding-top: 0;
padding-left: 0;
margin-top: 0;
margin-left: 0;
opacity: 1;
transition: 0s;
}
}
@mixin gpu-accelerate {
-webkit-transform: translateZ(0);
}
@mixin disabled-select {
-webkit-user-select: none;
}
@mixin disabled-select-all {
* {
@include disabled-select;
}
}
================================================
FILE: app/templates/app/scss/_msgbox-android.scss
================================================
@charset "UTF-8";
$android-border-radius: 8px;
/* android */
.msgbox{
.msgbox-bd.android{
color: #000;
> .box-ct.dialog{
$dialog-w: $layout-with * 0.86;
> .box-bd {
width: $dialog-w;
-webkit-border-radius: $android-border-radius;
.content {
background-color: #fff;
-webkit-border-radius: $android-border-radius $android-border-radius 0 0;
}
.bbts {
@include box-h-c-r;
width: 100%;
background-color: #fff;
padding: 14px 30px;
> div {
display: inline-block;
min-width: 172px;
height: 76px;
padding: 20px 22px;
color: #009688;
text-align: center;
text-transform: uppercase;
background-color: #fff;
-webkit-border-radius: 0 !important;
&:active {
background-color: #b6b6b6;
}
margin-top: 0;
&.no {
margin-right: 0;
}
}
}
}
}
> .box-ct.menu {
$dialog-w: $layout-with * 0.98;
$option-h: 120px;
//position: fixed;
left: ($layout-with - $dialog-w)/2;
bottom: 0px;
> .box-bd {
width: $dialog-w;
padding-bottom: 0;
> div {
@include box-v;
}
.opt {
&:active {
background-color: #b6b6b6;
}
@include box-h-c-c;
-webkit-box-flex: 1;
padding: 20px 0;
height: $option-h;
background-color: #fff;
text-transform: uppercase;
color: #009688;
&.msg{
height: auto;
min-height: $option-h;
padding: 20px;
color: #7b7b7b;
text-transform: none;
&:active{
background-color: #fff;
}
}
&.no {
margin-top: 20px;
background-color: #fff;
-webkit-border-radius: 0 !important;
font-weight: normal;
&:active{
background-color: #b6b6b6;
}
}
&.highlight{
color: #009688;
}
}
.options{
-webkit-border-radius: $android-border-radius;
overflow: hidden;
> div {
margin-top: 0;
&:first-child{
-webkit-border-radius: $android-border-radius $android-border-radius 0 0;
margin-top: 0;
}
}
}
}
}
}
}
/* end android */
================================================
FILE: app/templates/app/scss/_msgbox-deja.scss
================================================
@charset "UTF-8";
/* deja */
.msgbox {
.msgbox-bd.deja{
> .box-ct.dialog {
$dialog-w: $layout-with * 0.82;
color: $color-black-second;
> .box-bd {
width: $dialog-w;
-webkit-border-radius: 0;
.content {
padding-top: 68px;
line-height: 42px;
background-color: #fff;
-webkit-border-radius: 0;
}
.msg {
color: $color-black-second;
padding: 0 30px 68px 30px;
}
.title {
line-height: 60px;
}
.bbts {
background-color: #fff;
> div {
margin-top: 0;
height: 110px;
border-top: 1px solid #e1e1e1;
font-size: 34px;
color: #fff;
&:first-child {
-webkit-border-radius: 0;
}
&:last-child {
-webkit-border-radius: 0;
}
&.no {
margin-right: 0;
color: $color-black-second;
background-color: #f6f6f6;
&:active{
background-color: #fff;
}
}
&.yes {
background-color: $color-red;
font-weight: normal;
&:active{
background-color: rgba(248,31,52,0.5);
}
}
}
}
}
}
> .box-ct.signin {
$dialog-w: 620px;
> .box-bd {
position: relative;
width: $dialog-w;
min-height: 340px;
background-color: #fff;
padding: 30px;
> div {
@include box-v-c-c;
}
.msg {
font-size: 34px;
color: $color-black-second;
min-height: 200px;
text-align: center;
padding-top: 100px;
}
.plf {
.b {
@include active-default;
}
}
.no {
position: absolute;
top: 0;
left: 0;
width: 80px;
height: 80px;
background-color: #e1e1e1;
&:active {
background-color: $color-red;
}
}
}
}
> .box-ct.winfaceanalysis {
$dialog-w: 536px;
> .box-bd {
position: relative;
width: $dialog-w;
> div {
@include box-v-c-c;
position: absolute;
}
.no {
top: 0;
left: 0;
width: 100px;
height: 100px;
}
.share{
left: 0;
bottom: 0;
width: 100%;
height: 200px;
}
}
}
}
}
/* end deja */
================================================
FILE: app/templates/app/scss/_msgbox.scss
================================================
@charset "UTF-8";
$ios-border-radius: 30px;
/* default */
.msgbox {
position: fixed;
z-index: 999;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: none;
background-color: transparent;
word-break: break-all;
overflow: hidden;
&.show {
display: -webkit-box;
> .box-mask {
-webkit-animation-name: MicroMask;
@include animated-1s;
-webkit-animation-timing-function: linear;
-webkit-animation-duration: 250ms;
}
}
> .box-mask, > .msgbox-ct {
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
> .box-mask {
position: absolute;
background-color: #000;
opacity: 0;
}
> .msgbox-ct {
position: relative;
-webkit-transform: translateZ(0);
background-color: transparent;
display: -webkit-box;
-webkit-box-pack: center;
> .msgbox-bd {
position: relative;
@include box-v-c-c;
width: $layout-with;
height: 100%;
> .box-ct {
display: -webkit-box;
font-size: 40px;
color: #f6f6f6;
-webkit-animation-name: MicroPopUp;
@include animated-1s;
-webkit-animation-duration: 250ms;
> .box-bd {
-webkit-transform: translateZ(0);
> div {
width: 100%;
height: 100%;
}
}
&.absolute{
position: absolute;
}
}
> .box-ct.dialog {
$dialog-w: $layout-with * 0.72;
color: #000;
> .box-bd {
width: $dialog-w;
overflow: hidden;
-webkit-border-radius: $ios-border-radius;
> div {
@include box-v;
}
.content {
padding-top: 30px;
background-color: rgba(225, 225, 225, .9);
-webkit-border-radius: $ios-border-radius $ios-border-radius 0 0;
}
.title {
@include box-h-c-c;
font-size: 36px;
}
.msg {
font-size: 28px;
padding: 0 30px 30px 30px;
line-height: 36px;
@include box-v-c-c;
.simley {
margin-left: 40px;
-webkit-animation-name: bounceIn;
@include animated-1s;
-webkit-animation-duration: .6s;
}
> p {
width: 100%;
@include box-h-t-l;
&.c {
text-align: center;
@include box-h-c-c;
}
}
}
.bbts {
@include box-h-c-c;
> div {
&:active {
background-color: #d0d0d0;
}
@include box-h-c-c;
-webkit-box-flex: 1;
font-size: 36px;
color: #007aff;
margin-top: 1px;
padding: 20px 0;
height: 88px;
background-color: rgba(225, 225, 225, .9);
&:first-child {
-webkit-border-radius: 0 0 0 $ios-border-radius;
}
&:last-child {
-webkit-border-radius: 0 0 $ios-border-radius 0;
}
&.no {
margin-right: 1px;
}
&.yes {
font-weight: bold;
}
}
}
}
}
> .box-ct.menu {
$dialog-w: $layout-with * 0.95;
$option-h: 115px;
//position: fixed;
left: ($layout-with - $dialog-w)/2;
bottom: 0px;
color: #000;
-webkit-animation-name: slideInUp;
&.close{
-webkit-animation-name: slideOutDown;
}
> .box-bd {
width: $dialog-w;
padding-bottom: 18px;
> div {
@include box-v;
}
.opt {
&:active {
background-color: #e9e9e9;
}
@include box-h-c-c;
-webkit-box-flex: 1;
font-size: 36px;
color: #007aff;
padding: 20px 0;
height: $option-h;
background-color: #f6f7f8;
&.msg{
height: auto;
min-height: $option-h;
padding: 20px;
color: #8f8f8f;
font-size: 28px;
text-align: center;
&:active{
background-color: #f6f7f8;
}
}
&.no {
margin-top: 18px;
background-color: #fff;
-webkit-border-radius: $ios-border-radius;
font-weight: bold;
&:active{
background-color: #f5f5f5;
}
}
&.highlight{
color: #f1442c;
}
}
.options{
-webkit-border-radius: $ios-border-radius;
overflow: hidden;
> div {
margin-top: 1px;
&:first-child{
-webkit-border-radius: $ios-border-radius $ios-border-radius 0 0;
margin-top: 0;
}
}
}
}
}
> .box-ct.loading {
$dialog-w: 240px;
> .box-bd {
padding-top: 50px;
width: $dialog-w;
height: 240px;
background-color: rgba(225, 225, 225, .9);
-webkit-border-radius: 10px;
> div {
@include box-v-c-c;
}
.msg {
font-size: 34px;
color: #000;
margin-top: 40px;
}
}
}
}
}
}
/* end default */
/*debug-msgbox-android.scss*/
@import "msgbox-android.scss";
/*end debug-msgbox-android.scss*/
/*debug-msgbox-deja.scss*/
@import "msgbox-deja.scss";
/*end debug-msgbox-deja.scss*/
================================================
FILE: app/templates/app/scss/_section-download.scss
================================================
@charset "UTF-8";
.section-download{
@include box-h-c-c;
padding: 20px;
background-color: #fdfdfd;
.sd-logo{
width: 114px;
height: 114px;
img{
width: 100%;
height: 100%;
}
}
.sd-info{
@include box-v;
padding: 23px 20px;
-webkit-box-flex: 1;
.name{
font-size: 36px;
font-weight: bold;
}
.dest{
font-size: 28px;
color: $color-black-third;
}
}
}
================================================
FILE: app/templates/app/scss/_slide.scss
================================================
@charset "UTF-8";
.slide {
position: relative;
}
.slide > * {
position: absolute;
width: 100%;
height: 100%;
top: 0;
right: 0;
bottom: 0;
left: 0;
}
.slide > *:not(.selected):not([animate]) {
display: none !important;
}
.slide-from-right > *, .slide-from-bottom > * {
-webkit-transition: -webkit-transform .6s cubic-bezier(0.4, 0, 0.2, 1);
transition: transform .6s cubic-bezier(0.4, 0, 0.2, 1);
}
.slide-from-right > .selected ~ [animate]:not(.selected) {
-webkit-transform: translateX(100%);
transform: translateX(100%);
}
.slide-from-right > [animate]:not(.selected) {
-webkit-transform: translateX(-100%);
transform: translateX(-100%);
}
.slide-from-bottom > .selected ~ [animate]:not(.selected) {
-webkit-transform: translateY(100%);
transform: translateY(100%);
}
.slide-from-bottom > [animate]:not(.selected) {
-webkit-transform: translateY(-100%);
transform: translateY(-100%);
}
.slide-from-right > .selected, .slide-from-bottom > .selected {
-webkit-transform: none;
transform: none;
}
================================================
FILE: app/templates/app/scss/_sprites.scss
================================================
.icon {
background-image: url('../images/sprites.png');
}
.icon-ic_s_close_pre_2x {
background-position: -4px -4px;
width: 30px;
height: 30px;
}
.icon-ic_s_close_nor_2x {
background-position: -4px -42px;
width: 30px;
height: 30px;
}
.icon-bt_google {
background-position: -4px -80px;
width: 180px;
height: 60px;
}
.icon-bt_Appbutton {
background-position: -4px -148px;
width: 180px;
height: 60px;
}
.icon-btn_fb_login {
background-position: -4px -216px;
width: 460px;
height: 95px;
}
.icon-ic_url_copy_pre_2x {
background-position: -4px -319px;
width: 120px;
height: 120px;
}
.icon-ic_about_ins_pre {
background-position: -4px -447px;
width: 120px;
height: 120px;
}
.icon-ic_about_tw_nor {
background-position: -4px -575px;
width: 120px;
height: 120px;
}
.icon-ic_about_tw_pre {
background-position: -4px -703px;
width: 120px;
height: 120px;
}
.icon-ic_about_fb_nor {
background-position: -4px -831px;
width: 120px;
height: 120px;
}
.icon-ic_about_fb_pre {
background-position: -4px -959px;
width: 120px;
height: 120px;
}
.icon-ic_url_copy_nor_2x {
background-position: -4px -1087px;
width: 120px;
height: 120px;
}
.icon-ic_about_ins_nor {
background-position: -4px -1215px;
width: 120px;
height: 120px;
}
.icon-logo {
background-position: -4px -1343px;
width: 140px;
height: 140px;
}
.icon-ic_vote_skip_nor_2x {
background-position: -4px -1491px;
width: 154px;
height: 154px;
}
.icon-ic_vote_skip_pre_2x {
background-position: -4px -1653px;
width: 154px;
height: 154px;
}
.icon-ic_vote_vote_nor_2x {
background-position: -4px -1815px;
width: 154px;
height: 154px;
}
.icon-ic_vote_vote_pre_2x {
background-position: -4px -1977px;
width: 154px;
height: 154px;
}
.icon-pic_vote_good_2x {
background-position: -4px -2139px;
width: 290px;
height: 240px;
}
.icon-pic_vote_skip_2x {
background-position: -4px -2387px;
width: 290px;
height: 240px;
}
================================================
FILE: app/templates/app/scss/_tooltip.scss
================================================
@charset "UTF-8";
$tooltip-color-default: #fff;
.tooltip {
position: fixed;
z-index: 90;
left: 0;
width: 100%;
display: none;
background-color: transparent;
&.show {
@include box-h-c-c;
}
>.tooltip__ct{
width: $layout-with;
padding-top: 20px;
padding-bottom: 20px;
overflow-x: hidden;
>.tooltip__bd{
position: relative;
display: inline-block;
top: 0;
padding: 20px;
background-color: $tooltip-color-default;
font-size: 30px;
color: $color-black-second;
border: 1px solid rgba(0, 0, 0, 0.3);
-webkit-box-shadow: 0px 0px 3px 0px rgba(0, 0, 0, 0.3);
-webkit-border-radius: 5px;
@include gpu-accelerate;
&.animated{
@include animated-1s;
-webkit-animation-duration: 350ms;
&.fadeIn{
-webkit-animation-name: fadeIn;
}
&.flipInY{
-webkit-animation-name: flipInY;
}
&.flipInX{
-webkit-animation-name: flipInX;
}
&.slideInUp{
-webkit-animation-name: slideInUp;
}
&.slideInDown{
-webkit-animation-name: slideInDown;
}
&.zoomIn{
-webkit-animation-name: zoomIn;
}
&.fadeInLeft{
-webkit-animation-name: fadeInLeft;
}
&.fadeInRight{
-webkit-animation-name: fadeInRight;
}
&.fadeInUp{
-webkit-animation-name: fadeInUp;
}
&.fadeInDown{
-webkit-animation-name: fadeInDown;
}
&.fadeOut{
-webkit-animation-name: fadeOut;
}
}
>.tooltip__content{
text-align: center;
}
>.tooltip__arrow{
position: absolute;
background-color: $tooltip-color-default;
width: 16px;
height: 16px;
overflow: hidden;
@include gpu-accelerate;
>div{
width: 32px;
height: 32px;
border: 1px solid rgba(0, 0, 0, 0.3);
}
&.left{
left: -9px;
-webkit-transform: rotate(-45deg);
}
&.right{
right: -9px;
-webkit-transform: rotate(135deg);
}
&.top{
top: -9px;
-webkit-transform: rotate(45deg);
}
&.bottom{
bottom: -9px;
-webkit-transform: rotate(-135deg);
}
}
}
}
&.clear{
>.tooltip__ct{
>.tooltip__bd{
background-color: transparent;
color: $tooltip-color-default;
border: 0;
-webkit-box-shadow: none;
-webkit-border-radius: 0;
>.tooltip__arrow {
display: none;
}
}
}
}
//theme
&.red{
>.tooltip__ct{
>.tooltip__bd{
background-color: $color-red;
color: $tooltip-color-default;
border: 0;
-webkit-box-shadow: none;
-webkit-border-radius: 0;
>.tooltip__arrow {
background-color: $color-red;
>div {
border: 0;
}
}
}
}
}
&.black{
font-family: "Segoe Print",Roboto, HelveticaNeue, Helvetica, Arial, sans-serif;
>.tooltip__ct{
>.tooltip__bd{
//background-color: rgba(38,39,41,0.9);
background-color: $color-black-fourth;
color: #fff;
border: 0;
-webkit-box-shadow: none;
-webkit-border-radius: 0;
>.tooltip__arrow {
//background-color: rgba(38,39,41,0.9);
background-color: $color-black-fourth;
>div {
border: 0;
}
}
}
}
}
}
================================================
FILE: app/templates/app/scss/_util.scss
================================================
@charset "UTF-8";
.ellipsis {
text-overflow: ellipsis;
display: block;
white-space: nowrap;
overflow: hidden;
}
.clearfix::before,
.clearfix::after {
content: "";
display: table;
}
.clearfix::after {
clear: both;
}
.fix-break-word, p {
@include blank-bg;
background-color: transparent;
word-break: break-all;
// Non standard for webkit
word-break: break-word;
-webkit-hyphens: auto;
hyphens: auto;
}
.hide {
display: none !important;
}
.uppercase {
text-transform: uppercase;
}
img{
&.lazy{
opacity: 0;
//transition: opacity .3s ease-in;
min-height: 1px;
min-width: 1px;
}
&.lazy[src]:not(.animated){
opacity: 1;
}
&.lazy--loaded,&+.lazy__real{
&.animated{
@include animated-1s;
-webkit-animation-name: fadeIn;
-webkit-animation-duration: .3s;
}
}
&.lazy__virtual{
opacity: 0 !important;
&+.lazy__real{
display: none;
}
&.lazy--loaded{
display: none;
&+.lazy__real{
display: inherit;
}
}
}
&.fix-space{
display: block;
}
}
.img-loading-placeholder{
background: url(../images/loading_logo_2x.png) 50% 50% no-repeat;
}
.default-active {
@include active-default;
}
.ripple-active {
@include active-ripple;
}
.disabled-select {
@include disabled-select;
}
.disabled-select-all {
@include disabled-select-all;
}
================================================
FILE: app/templates/app/scss/_value.scss
================================================
@charset "UTF-8";
/*size*/
$layout-with: _VIEWPORT_WIDTH_;/*see viewport:viewport package.json*/
/*end size*/
/*color*/
$color-black: #2f2f2f;
$color-black-second: #414141;
$color-black-third: #818181;
$color-black-fourth: #262729;
$color-gray: #b5b7b6;
$color-gray-second: #cecece;
$color-gray-third: #eaeaea;
$color-gray-fourth: #e1e1e1;
$color-red: #f81f34;
$color-blue: #71b0ea;
/*end color*/
$active-opacity: .8;
================================================
FILE: app/templates/app/scss/_view-home.scss
================================================
@charset "UTF-8";
.view-home {
width: 100%;
min-height: 100%;
background-color: #35161f;
font-size: 38px;
.main {
@include box-h-c-c;
padding: 200px 30px;
}
}
================================================
FILE: app/templates/app/scss/_view-user.scss
================================================
@charset "UTF-8";
.view-user {
width: 100%;
background-color: #fff;
color: #292929;
.users {
@include box-v-c-c;
padding: 50px 30px;
.list {
padding: 50px;
}
}
}
================================================
FILE: app/templates/app/scss/_view.scss
================================================
body > .view {
width: 100%;
visibility: hidden;
display: none;
&.animated {
@include animated-1s;
-webkit-animation-fill-mode: none; /*transform的动效会使当前view里fixed的元素将无效*/
-webkit-animation-name: MicroBounceOut;
-webkit-animation-duration: 350ms;
}
&.show {
visibility: visible !important;
display: block !important;
}
}
/*debug-view-home.scss*/
@import "view-home.scss";
/*end debug-view-home.scss*/
/*debug-view-user.scss*/
@import "view-user.scss";
/*end debug-view-user.scss*/
================================================
FILE: app/templates/app/scss/styles.scss
================================================
@charset "UTF-8";
/*_value.scss*/
@import "value.scss";
/*end _value.scss*/
/*_mixin.scss*/
@import "mixin.scss";
/*end _mixin.scss*/
/*_util.scss*/
@import "util.scss";
/*end _util.scss*/
/*_box.scss*/
@import "box.scss";
/*end _box.scss*/
/*_fonts.scss*/
@import "fonts.scss";
/*end _fonts.scss*/
/*_common.scss*/
@import "common.scss";
/*end _common.scss*/
/*_animate.scss*/
@import "animate.scss";
/*end _animate.scss*/
/*_icon.scss*/
@import "icon.scss";
/*end _icon.scss*/
/*_components.scss*/
@import "components.scss";
/*end _components.scss*/
/*_header.scss*/
@import "header.scss";
/*end _header.scss*/
/*_list.scss*/
@import "list.scss";
/*end _list.scss*/
/*_footer.scss*/
@import "footer.scss";
/*end _footer.scss*/
/*_view.scss*/
@import "view.scss";
/*end _view.scss*/
================================================
FILE: app/templates/app/src/app/App.js
================================================
//require('util/AppCache');
require('lib/zepto');
require('lib/Core');
require('app/resources/i18n');
var BasicController = require('app/controller/Controller');
var HomeController = require('app/controller/HomeController');
var UserController = require('app/controller/UserController');
//__INSERT_POINT__ Don't delete!!
function App() {
var params = Core.localParam(),
standalone = params.search['standalone'];//如果带此参数,初始页非首页的浏览器历史返回将不会回首页
setTimeout(function () {
//Core.Router.init(standalone?'':'/home/');
Core.Router.init();
}, 50);
}
window.App = new App;
================================================
FILE: app/templates/app/src/app/controller/Controller.js
================================================
var Actions = require('app/resources/Actions');
var ThirdVendor = require('util/ThirdVendor');
var BasicModel = require('app/model/Model');
var BasicView = require('app/view/View');
function Controller() {
this.models = {
Basic: BasicModel
};
this.views = {
Basic: BasicView
};
//所有视图初始化前,需要获得客户端的用户登录信息
//Core.Router.onReady(onUserinfo);
Core.Router.onChanged(onViewChanged);
var CTRL = this,
isApp = Core.NativeBridge.isApp(),
params = Core.localParam(),
_userid = params.search['userid'],
_sig = params.search['sig'];
///*Todo: debug user
_userid && CTRL.models.Basic.setUserId(_userid);
_sig && CTRL.models.Basic.setUserSig(_sig);
//*/
//更新数据缓存时间
Core.Event.on('resetModelUpdateTimeout', CTRL.models.Basic.modelUpdate.timer.resetAll);
//通过API名称,调用客户API
Core.Event.on('appAPI', appAPI);
//分享
Core.Event.on('share', appShare);
//去下载
Core.Event.on('appDownload', redirectToDownload);
//直接到Store去下载
Core.Event.on('openAppInStore', downloadDejaInApp);
//去更新
Core.Event.on('appUpdate', appUpdate);
//去deja.me
Core.Event.on('redirectToDejame', redirectToDejame);
//跳转出商城
Core.Event.on('redirect', redirectToPage);
//去登录
Core.Event.on('login', onLogin);
//去WEB登录
Core.Event.on('webLogin', webLogin);
//去App登录
Core.Event.on('appLogin', appLogin);
//从App获取用户信息
Core.Event.on('appUserinfo', onUserinfo);
//去反馈
Core.Event.on('feedback', onFeedback);
//复制文本
Core.Event.on('appCopyText', appCopyText);
//更新标题
Core.Event.on('appModifyTitle', appModifyTitle);
//修改右上角功能菜单
Core.Event.on('appActionbutton', appActionButton);
//修改右上角功能菜单成默认
Core.Event.on('appActionDefaultButton', appActionDefaultButton);
//修改右上角功能菜单成分享
Core.Event.on('appActionShareButton', appActionShareButton);
//修改右上角功能菜单成Filter
Core.Event.on('appActionFilterButton', appActionFilterButton);
//修改右上角功能菜单成add
Core.Event.on('appActionAddButton', appActionAddButton);
//注册webview关闭事件
Core.Event.on('appOnUnload', appOnUnload);
//关闭webview
Core.Event.on('appCloseWebView', appCloseWebView);
//Update Profile
Core.Event.on('appUpdateProfile', appUpdateProfile);
//tab 切换
Core.Event.on('switchTab', switchTab);
//触发暂时性动画
Core.Event.on('trigerAnimate', trigerAnimate);
//text 收展
Core.Event.on('toggleTextSectionExpand', toggleTextSectionExpand);
//统计
Core.Event.on('analytics', analytics);
//滚动到顶部
Core.Event.on('scrollTop', scrollTop);
//back
Core.Event.on('back', function (action) {
Core.Router.back(action || -1);
});
function analytics(params, title) {
setTimeout(function () {
var url = Actions.analytics + '?devevent=1' + (params ? ('&' + params) : '');
//android和iOS
if ($.os.ios && !$.os.android) {
url += '&ios';
} else if ($.os.android) {
url += '&android';
}
if (ThirdVendor) {
url += '&plf=' + ThirdVendor.code;
}
if (CTRL.models.Basic.isLogined()) {
url += '&logined';
}
url += '&t=' + (new Date().getTime());
Core.Navigator.protocol(url, true);
}, 0);
}
function scrollTop() {
var top = Math.min(Math.min(window.pageYOffset, document.documentElement.scrollTop || document.body.scrollTop), window.scrollY),
start = top,
to = 0,
timer = 0,
change = to - start,
currentTime = 0,
increment = 20,
duration = 500;
(function animloop() {
// increment the time
currentTime += increment;
if (start < 2 || CTRL.views.Basic.GlobalTouch.touched || currentTime > duration) {
if (start < 2) {
window.scrollTo(0, 1);
}
cancelRequestAnimFrame(timer);
return;
}
window.scrollTo(0, Math.easeInOutQuad(currentTime, start, change, duration));
timer = requestAnimFrame(animloop);
})();
}
function onViewChanged() {
appModifyTitle();
CTRL.views.Basic.msgbox.hideLoading();
}
function onUserinfo() {
if (isApp) {
Core.NativeBridge.userInfo(null, function (rs) {
CTRL.models.Basic.setAppUserMeta(rs);
if (rs && !!rs.userid) {
if (CTRL.models.Basic.verifyLoginCookie() && CTRL.models.Basic.verifyLoginCookieTimeout(30)) {
Core.Router.run();
} else {
rs.sig ? appLogin() : appAnonymousLogin();
}
} else {
Core.Router.run();
}
});
//Core.NativeBridge.device(function(rs){
// if(rs){
// CTRL.models.Basic.setNativeBridgeDeviceMeta(rs);
// }
//});
} else {
Core.Router.run();
}
}
function onLogin(arg, msg, callback) {
if (isApp) {
appLogin(callback);
} else {
CTRL.views.Basic.msgbox.showSignin({
msg: msg,
yesCallback: function (plf) {
webLogin(Actions.main + (arg || ''), null, plf);
}
});
//CTRL.views.Basic.msgbox.showDownload({
// yesCallback: function () {
// redirectToDownload(Actions.main + (arg || ''));
// }
//});
}
}
function onFeedback(email) {
Core.Navigator.protocol('mailto:' + (email || 'mozat@mozat.com?subject=Suggestion'), true);
}
function switchTab(el, tabs, tabContents) {
if (!tabs || !tabContents) {
return;
}
var isClicked = !!el;
el = el || tabs[0];
for (var i = 0; i < tabs.length; i++) {
if (tabs[i] == el) {
tabs[i].classList.add('on');
trigerAnimate(tabContents.eq(i));
tabContents[i] && tabContents[i].classList.add('show');
isClicked && Core.Event.trigger('analyticsCurView', 'tab=' + i);
} else {
tabs[i].classList.remove('on');
tabContents[i] && tabContents[i].classList.remove('show');
}
}
Core.Event.trigger('analyticsCurView');
}
function trigerAnimate(el, classname, timeout) {
if (!el) {
return;
}
classname = classname || 'animated';
timeout = timeout || 1200;
el.animTimer && clearTimeout(el.animTimer);
el.addClass(classname);
el.animTimer = setTimeout(function () {
el.removeClass(classname);
}, timeout);
}
function toggleTextSectionExpand(el) {
el && el.classList.toggle('expand');
}
function webLogin(surl, furl, pf) {
var murl = window.location.href;
surl = surl || murl;
furl = furl || murl;
pf = pf || 'fb';
redirectToPage(Actions.login
.replace('{SURL}', encodeURIComponent(surl))
.replace('{FURL}', encodeURIComponent(furl))
.replace('{PF}', pf));
}
function appLogin(callback, subProtocol) {
Core.NativeBridge.login(null, function (rs) {
if (rs) {
CTRL.models.Basic.saveLoginCookieTimeout();
CTRL.models.Basic.setAppUserMeta(rs);
if (callback) {
callback();
} else {
Core.Router.run();
}
}
}, subProtocol);
}
function appAnonymousLogin(callback) {
appLogin(callback, '?anonymous=1')
}
function appUpdate(msg, force) {
redirectToApp(function () {
CTRL.views.Basic.msgbox.showDialog({
msg: msg || 'Please up to date your App',
noText: force ? null : 'Close',
yesText: 'Update',
yesCallback: function () {
downloadDejaInApp();
}
});
});
}
function appShare(callback, plf) {
redirectToApp(function () {
var fn = Core.NativeBridge['share' + (plf ? ('_' + plf) : '')];
fn && fn(null, callback);
});
}
function appCopyText(text) {
if (isApp) {
Core.NativeBridge.copy(text);
}
}
function appModifyTitle(title) {
title = title || document.title;
document.title = title;
if (isApp) {
Core.NativeBridge.modifytitle(title);
}
}
function appActionButton(name, callback) {
if (isApp) {
Core.NativeBridge.updateBarButton(name, !Core.isDebug && callback);
}
}
function appActionShareButton() {
appActionButton('share');
}
function appActionFilterButton(callback) {
appActionButton('filter', callback);
}
function appActionAddButton(callback) {
appActionButton('add', callback);
}
function appActionDefaultButton() {
appActionButton('', function () {
});
}
function appOnUnload(callback) {
if (isApp) {
Core.NativeBridge.set_before_for_unload(callback);
}
}
function appCloseWebView() {
if (isApp) {
Core.NativeBridge.closeweb();
}
}
/**
* @param subProtocol String creationLike,creationDelete,productLike,follow
*/
function appUpdateProfile(subProtocol) {
appAPI('updateProfile', null, null, subProtocol);
}
/**
* dejafashion://name/subProtocol
* window.__dejafashion_data_name = data;
* window.__dejafashion_after_name = callback;
*/
function appAPI(name, data, callback, subProtocol, redirect) {
if (isApp) {
Core.NativeBridge.trigger.apply(null, arguments);
} else if (redirect) {
var proto = [name];
subProtocol && proto.push(subProtocol);
redirectToDownload(null, true, Actions.dejafashionSchema + proto.join('/'));
}
}
function downloadDejaInApp() {
var url = Actions.dejaAppAndroid;
if ($.os.ios && !$.os.android) {
url = Actions.dejaAppIos;
}
window.location = url;
}
function redirectToDejame() {
redirectToPage(Actions.dejame);
}
//打开客户端原生视图
function redirectToApp(callback, link) {
if (isApp) {
callback && callback();
} else {
CTRL.views.Basic.msgbox.showDownload({
yesCallback: function () {
redirectToDownload(link || window.location.href);
}
});
}
}
/**
* open a web site in app,or just op
* @param link
* @param autoopen
* @param schema
*/
function redirectToDownload(link, autoopen, schema) {
link = !!link && link != '0' ? ('#url=dejafashion://web/' + link) : '';
link = !!schema ? ('#url=' + schema) : link;
redirectToPage(Actions.dejaDwonloadBridge + (autoopen ? '?autoopen=1' : '') + link);
}
function redirectToPage(link) {
if (link) {
!(/__NativeBridge_target/g.test(link)) && appActionDefaultButton();
window.location = link;
}
}
}//end Controller
module.exports = new Controller;
================================================
FILE: app/templates/app/src/app/controller/HomeController.js
================================================
var Actions = require('../resources/Actions');
var BasicModel = require('app/model/Model');
var BasicView = require('app/view/View');
var HomeView = require('app/view/HomeView');
function HomeController() {
this.models = {
Basic: BasicModel
}
this.views = {
Basic: BasicView,
Home: HomeView
};
var CTRL = this,
viewNames,
curViewId = '',
viewHomeQuery = {};
viewNames = {
'home': 'Home'
}
Core.Router
.onUnsubscribed(onViewUnnamed,unViewUnnamed)
.subscribe('/home/', onViewHome, unViewHome);
//统计视图
Core.Event.on('analyticsCurView', analyticsCurView);
//forwardHome
Core.Event.on('forwardHome', forwardHome);
function unViewUnnamed() {
unViewHome();
}
function unViewHome() {
CTRL.views.Home.hide();
}
function onViewUnnamed(req) {
onViewHome(req);
Core.Event.trigger('analytics');
}
function onViewHome(req) {
curViewId = 'home';
viewHomeQuery = req.query;
CTRL.views.Home.show();
//追加统计
analyticsCurView();
}
function forwardHome(arg) {
Core.Router.forward('/home/' + (arg || ''));
}
function analyticsCurView(params, title) {
if (!Core.Router.currentMatch(['/home/', Core.Router.getUnsubscribedAction()])) {
return;
}
params = params ? ('&' + params) : '';
title = title || viewNames[curViewId] || document.title;
Core.Event.trigger('analytics', 'viewid=' + curViewId + params, title);
}
}
module.exports = new HomeController;
================================================
FILE: app/templates/app/src/app/controller/UserController.js
================================================
var Actions = require('../resources/Actions');
var BasicModel = require('app/model/Model');
var UserModel = require('app/model/UserModel');
var BasicView = require('app/view/View');
var UserView = require('app/view/UserView');
function UserController() {
this.models = {
Basic: BasicModel,
User: UserModel
}
this.views = {
Basic: BasicView,
User: UserView
};
var CTRL = this,
viewNames,
curViewId = '',
viewUserQuery = {};
viewNames = {
'user': 'User'
}
Core.Router.subscribe('/user/', onViewUser, unViewUser);
//统计视图
Core.Event.on('analyticsCurView', analyticsCurView);
//forwardUser
Core.Event.on('forwardUser', forwardUser);
function unViewUser() {
CTRL.views.User.hide();
}
function onViewUser(req) {
curViewId = 'user';
viewUserQuery = req.query;
CTRL.views.User.show();
CTRL.views.Basic.msgbox.hideLoading();
CTRL.models.User.user.request({id: viewUserQuery.userid},afterRequestUser);
//追加统计
analyticsCurView();
}
function afterRequestUser(success) {
CTRL.views.Basic.msgbox.hideLoading();
if (success) {
CTRL.models.User.user.timer.update();
} else {
CTRL.views.Basic.msgbox.showFailed();
}
}
function forwardUser(arg) {
Core.Router.forward('/user/' + (arg || ''));
}
function analyticsCurView(params, title) {
if (!Core.Router.currentMatch(['/user/'])) {
return;
}
params = params ? ('&' + params) : '';
title = title || viewNames[curViewId] || document.title;
Core.Event.trigger('analytics', 'viewid=' + curViewId + params, title);
}
}
module.exports = new UserController;
================================================
FILE: app/templates/app/src/app/model/Model.js
================================================
var RequestHelper = require('app/model/RequestHelper');
var Actions = require('app/resources/Actions');
var Mdl = Core.Class.Model,
lcStorage = Core.localStorage;
function Model() {
var MODEL = this,
userId, udid, appUserMeta,sig,
loginCookieTimerPrefix = 'loginCookieTimer_';
this.getCookie = function (sKey) {
return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
}
this.setCookie = function (name, value, days) {
if (days) {
var date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
var expires = "; expires=" + date.toGMTString();
}
else var expires = "";
document.cookie = name + "=" + value + expires + "; path=/";
}
//校验登录 cookies
this.verifyLoginCookie = function () {
var uid = this.getCookie('uid');
this.setUserId(uid);
return uid;
}
this.saveLoginCookieTimeout = function () {
var key = loginCookieTimerPrefix + this.getUserId();
lcStorage.set(key, new Date().getTime());
}
//校验 cookies 有效期,这里用 1 天
this.verifyLoginCookieTimeout = function (minutes) {
var key = loginCookieTimerPrefix + this.getUserId(),
last = lcStorage.get(key) || 0;
minutes = minutes || 1 * 60 * 24 * 1;
return ( (new Date().getTime()) - last ) < minutes * 60 * 1000;
}
this.setUdId = function (id) {
udid = id;
}
this.getUdId = function () {
return udid;
}
this.setUserId = function (id) {
userId = id || userId;
}
this.getUserId = function () {
return userId || (appUserMeta && appUserMeta.userid) || this.getCookie('uid');
}
this.getAppUserMeta = function () {
return appUserMeta;
}
this.setAppUserMeta = function (data) {
appUserMeta = data;
}
this.getUserSig = function () {
return sig || (appUserMeta && appUserMeta.sig) || this.getCookie('sig');
}
this.setUserSig = function (val) {
sig = val;
}
this.isLogined = function () {
return !!this.getUserId() && !!this.getUserSig();
}
//数据缓存更新
this.modelUpdate = new Mdl();
}
module.exports = new Model;
================================================
FILE: app/templates/app/src/app/model/RequestHelper.js
================================================
var getJSON = Core.RequestHandler.getJSON,
postJSON = Core.RequestHandler.postJSON,
JSONP = Core.RequestHandler.JSONP;
function request(action, data, callback, scope, options) {
options = options || {};
var __STORE_ID, conf;
data = data || {};
data._t = new Date().getTime();
__STORE_ID = data.__STORE_ID;
delete data.__STORE_ID;
conf = {
action: action,
data: data,
complete: function (data) {
if (data.success) {
scope && scope.set && scope.set(data.data, __STORE_ID);
}
callback && callback(data.success);
}
};
for (var name in options) conf[name] = options[name];
conf.action = action;
conf.data = data;
getJSON(conf);
}
function post(action, data, callback, scope, options) {
options = options || {};
var conf = {
action: action,
data: data,
contentType: options.contentType || "application/json;charset=utf-8",
complete: function (data) {
if (data.success) {
scope && scope.set && scope.set(data.data);
}
callback && callback(data.success);
}
};
for (var name in options) conf[name] = options[name];
conf.action = action;
conf.data = data;
postJSON(conf);
}
module.exports = {
getJSON: getJSON,
postJSON: postJSON,
JSONP: JSONP,
request: request,
post: post
};
================================================
FILE: app/templates/app/src/app/model/StoreHelper.js
================================================
var RequestHelper = require('app/model/RequestHelper');
function pagingStore(action, options) {
var option = {
isFromStore: false,
page: 0,
page_size: 20,
setWithStoreData: function (data) {
this.resetPage(data.__page);
this.set(data, data.__STORE_ID);
},
resetPage: function (storePage) {
if (storePage == undefined || storePage == null) {
this.isFromStore = false;
this.page = 0;
}
//reset page by history
else {
this.isFromStore = true;
this.page = storePage + 1;
}
},
request: function (data, callback) {
this.isFromStore = false;
data = data || {};
var _this = this;
var __STORE_ID;
__STORE_ID = data.__STORE_ID;
delete data.__STORE_ID;
data.page = this.page;
data.page_size = this.page_size;
RequestHelper.getJSON({
data: data,
action: action,
complete: function (data) {
if (data.success) {
_this.set(function (storeData) {
//set __STORE_ID's cache data
if (storeData) {
storeData.__page = _this.page;
storeData.end = data.data.end;
storeData.data = storeData.data.concat(data.data.data);
}
//set model's data
else {
storeData = data.data;
storeData.__page = _this.page;
}
return storeData;
}, __STORE_ID);
_this.page++;
}
callback && callback(data.success);
}
});
}
};
Core.Class.apply(option, options);
return new Core.Class.Model(option);
}
function requestStore(action, options) {
var option = {
request: function (data,callback) {
RequestHelper.request(action,data,callback,this);
}
};
Core.Class.apply(option, options);
return new Core.Class.Model(option);
}
function postStore(action, options) {
var option = {
post: function (data,callback) {
RequestHelper.post(action,data,callback,this);
}
};
Core.Class.apply(option, options);
return new Core.Class.Model(option);
}
function JSONPStore(action, options) {
var option = {
request: function(data, callback){
data = data || {};
var _this = this,
__STORE_ID = data.__STORE_ID,
callbackName;
callbackName = '__JSCB_'+Core.GUID(8);
data.callback = callbackName;
delete data.__STORE_ID;
window[callbackName] = function(data){
_this.set(data, __STORE_ID);
callback && callback(data);
}
RequestHelper.JSONP({
url: action,
data: data
});
}
};
Core.Class.apply(option, options);
return new Core.Class.Model(option);
}
function pagingJSONPStore(action, options) {
var option = {
isFromStore: false,
page: 0,
page_size: 20,
setWithStoreData: function (data) {
this.resetPage(data.__page);
this.set(data, data.__STORE_ID);
},
resetPage: function (storePage) {
if (storePage == undefined || storePage == null) {
this.isFromStore = false;
this.page = 0;
}
//reset page by history
else {
this.isFromStore = true;
this.page = storePage + 1;
}
},
request: function (data, callback) {
this.isFromStore = false;
data = data || {};
var _this = this,
__STORE_ID = data.__STORE_ID,
callbackName;
callbackName = '__JSCB_'+Core.GUID(8);
data.callback = callbackName;
delete data.__STORE_ID;
window[callbackName] = function(data){
_this.set(function (storeData) {
//set __STORE_ID's cache data
if (storeData) {
storeData.__page = _this.page;
storeData.data = storeData.data.concat(data.data.data);
}
//set model's data
else {
storeData = data;
storeData.__page = _this.page;
}
return storeData;
}, __STORE_ID);
_this.page++;
callback && callback(data);
}
data.page = this.page;
data.page_size = this.page_size;
RequestHelper.JSONP({
url: action,
data: data
});
}
};
Core.Class.apply(option, options);
return new Core.Class.Model(option);
}
module.exports = {
pagingStore: pagingStore,
requestStore: requestStore,
postStore: postStore,
JSONPStore: JSONPStore,
pagingJSONPStore: pagingJSONPStore
};
================================================
FILE: app/templates/app/src/app/model/UserModel.js
================================================
var StoreHelper = require('app/model/StoreHelper');
var Actions = require('app/resources/Actions');
var Basic = require('app/model/Model');
var Mdl = Core.Class.Model,
lcStorage = Core.localStorage;
function User() {
}
User.prototype.user = StoreHelper.requestStore(Actions.user);
User.prototype.userInfo = StoreHelper.JSONPStore(Actions.user);
module.exports = new User();
================================================
FILE: app/templates/app/src/app/resources/Actions.js
================================================
var thisPage = window.location.href
//注意,保留search 是为了避免微信自动追加的应用检测状态值
//.replace(window.location.search,'')
.replace(window.location.hash, '');
var thisPath = thisPage.substring(0, thisPage.lastIndexOf('/') + 1);
///*official
var Actions = {
user: Core.localHost + '/user/list.php',
login: Core.localHost + '/account/login_third?success={SURL}&fail={FURL}&pf={PF}',
main: thisPath + 'index.html',
analytics: thisPath + 'analytics.html',
dejame: 'http://deja.me/u/XPKab9',
dejaAppAndroid: 'http://deja.me/u/XPKab9',
dejaAppIos: 'http://deja.me/u/fzb1KO',
dejaDwonloadBridge: 'http://m.deja.me/bridge/',
dejaShareLogo: thisPath + 'resources/images/deja_icon_ios_228.png'
}
//*/
///_DEBUG_*Todo: debug actions
var Actions = {
user: 'data/user.json',
login: Core.localHost + '/account/login_third?success={SURL}&fail={FURL}&pf={PF}',
main: thisPath + 'index.html',
analytics: thisPath + 'analytics.html',
dejame: 'http://deja.me/u/XPKab9',
dejaAppAndroid: 'http://deja.me/u/XPKab9',
dejaAppIos: 'http://deja.me/u/fzb1KO',
dejaDwonloadBridge: 'http://m.deja.me/bridge/',
dejaShareLogo: thisPath + 'resources/images/deja_icon_ios_228.png'
}
//*/
module.exports = Actions;
================================================
FILE: app/templates/app/src/app/resources/Audios.js
================================================
var Audios = {}
module.exports = Audios;
================================================
FILE: app/templates/app/src/app/resources/i18n/en_US/Msgbox.js
================================================
module.exports = {
ok: 'OK',
no: 'Cancel',
sure: 'Sure',
loading: 'Loading...',
submitting: 'Submitting...'
}
================================================
FILE: app/templates/app/src/app/resources/i18n/en_US/index.js
================================================
var Msgbox = require('./Msgbox');
var en_US = {
Msgbox: Msgbox
}
module.exports = en_US;
================================================
FILE: app/templates/app/src/app/resources/i18n/index.js
================================================
var en_US = require('./en_US');
var zh_CN = require('./zh_CN');
var langs = {
zh_CN: zh_CN
}
//https://github.com/jquery-i18n-properties/jquery-i18n-properties/blob/master/jquery.i18n.properties.js
function getLangName(lang) {
if (!lang || lang.length < 2) {
lang = (navigator.languages) ? navigator.languages[0]
: (navigator.language || navigator.userLanguage /* IE */ || 'en');
}
lang = lang.toLowerCase();
lang = lang.replace(/-/,"_"); // some browsers report language as en-US instead of en_US
if (lang.length > 3) {
lang = lang.substring(0, 3) + lang.substring(3).toUpperCase();
}
return lang;
}
function i18n() {
this.getLangName = getLangName;
}
i18n.prototype = en_US;
function createI18n(lang) {
var lang = langs[lang];
if(lang) {
Core.extend(i18n, lang);
}else{
lang = i18n;
}
return new lang;
}
var oI18n = createI18n(getLangName());
window.i18n = oI18n;
module.exports = oI18n;
================================================
FILE: app/templates/app/src/app/resources/i18n/zh_CN/Msgbox.js
================================================
module.exports = {
ok: '好的',
no: '取消',
sure: '确定',
loading: '正在加载...',
submitting: '正在提交...'
}
================================================
FILE: app/templates/app/src/app/resources/i18n/zh_CN/index.js
================================================
var Msgbox = require('./Msgbox');
function zh_CN() {
//extend from super
zh_CN.superclass.constructor.call(this);
this.Msgbox = Msgbox;
}
module.exports = zh_CN;
================================================
FILE: app/templates/app/src/app/view/HomeView.js
================================================
var BasicView = require('app/view/View');
var BasicModel = require('app/model/Model');
function HomeView() {
this.models = {
Basic: BasicModel
}
this.viewCls = 'view-home';
this._BasicView = BasicView;
var VIEW = this,
isApp = Core.NativeBridge.isApp(),
Tpl, els,
tap = VIEW._BasicView.tapEvent;
//model listeners
function initEls() {
if(els){return;}
els = VIEW._BasicView.getElements(VIEW.viewCls);
bindEvent();
}//end initEls
function initTpls(){
if(Tpl){return;}
Tpl = Tpl || VIEW._BasicView.getTemplates(VIEW.viewCls);
}
function initResources() {
initEls();
initTpls();
}
this.getEls = function () {
initEls();
return els;
}
this.getTpls = function(){
initTpls();
return Tpl;
}
function bindEvent() {
}//end bindEvent
this.show = function () {
initResources();
if (!els.main.hasClass('show')) {
Core.Event.trigger('trigerAnimate', els.main);
VIEW._BasicView.show(VIEW.viewCls);
}
}
this.hide = function () {
if (!els) {
return;
}
}
function render(data) {
initResources();
}//end render
}//end View
module.exports = new HomeView();
================================================
FILE: app/templates/app/src/app/view/UserView.js
================================================
var BasicView = require('app/view/View');
var BasicModel = require('app/model/Model');
var UserModel = require('app/model/UserModel');
function UserView() {
this.models = {
Basic: BasicModel,
User: UserModel
}
this.viewCls = 'view-user';
this._BasicView = BasicView;
var VIEW = this,
isApp = Core.NativeBridge.isApp(),
Tpl, els,
tap = VIEW._BasicView.tapEvent;
//model listeners
VIEW.models.User.user.updated(render);
function initEls() {
if(els){return;}
els = VIEW._BasicView.getElements(VIEW.viewCls,function(){
return {
back: this.main.find('.back')
}
});
bindEvent();
}//end initEls
function initTpls(){
if(Tpl){return;}
Tpl = Tpl || VIEW._BasicView.getTemplates(VIEW.viewCls);
}
function initResources() {
initEls();
initTpls();
}
this.getEls = function () {
initEls();
return els;
}
this.getTpls = function(){
initTpls();
return Tpl;
}
function bindEvent() {
els.back.on(tap, Core.Router.back);
}//end bindEvent
this.show = function () {
initResources();
Core.Event.trigger('trigerAnimate',els.main);
VIEW._BasicView.show(VIEW.viewCls);
}
this.hide = function () {
if (!els) {
return;
}
}
function render(data) {
initResources();
data = data || VIEW.models.User.user.get();
if (!data || !data.length) {
return;
}
var list = [];
data.forEach(function (key, index) {
list.push(Tpl.listItem(key));
});
els.list.html(list.join(''));
list = null;
}//end render
}//end View
module.exports = new UserView();
================================================
FILE: app/templates/app/src/app/view/View.js
================================================
var Actions = require('app/resources/Actions');
var Msgbox = require('widget/Msgbox');
var WechatShare = require('util/WechatShare');
var YiXinShare = require('util/YiXinShare');
var BasicModel = require('app/model/Model');
function View() {
this.models = {
Basic: BasicModel
}
var VIEW = this,
els, shareStore = {},
params = Core.localParam(),
isApp = Core.NativeBridge.isApp();
//click事件
this.tapEvent = $.os.ios || $.os.android ? 'tap' : 'click';
function init() {
Core.MetaHandler.fixViewportWidth();
initEls();
bindEvent();
VIEW.hide();
els.body.css({'visibility': 'visible'});
};//end init
function initEls() {
var body = $('body');
els = {
window: $(window),
body: body,
views: body.children('.view')
}
VIEW.GlobalTouch = {
preventMove: false,
touched: false
}
window.GlobalTouch = VIEW.GlobalTouch;
VIEW.msgbox = new Msgbox({
// themeCls: 'deja',
GlobalTouch: VIEW.GlobalTouch
});
}
this.getEls = function () {
return els;
}
this.getView = function (viewCls) {
return els.views.filter('.' + viewCls);
}
//parse elements have "data-template" into templates
this.getTemplates = function (viewCls) {
var el = this.getView(viewCls);
if (el.$Templates) {
return el.$Templates;
}
var Templates = {};
el.find('*[data-template]').each(function () {
var key = $(this),
name = key.attr('data-template');
if (name) {
Templates[name] = Core.microTmpl(key.text());
}
});
el.$Templates = Templates;
return Templates;
}
/**
* find all elements have "data-element"
* @param viewCls
* @param extra object
* e.g.:
* //1. pass an object
* {
* defaultUserid: 'user_id_xxxx'
* }
*
* //2. pass a function that return an obeject,"this" is the elements' own object
* function(){
* return {
* userList: this.main.find('.list')
* }
* }
*
* @returns {*}
*/
this.getElements = function (viewCls,extra) {
var el = this.getView(viewCls);
if (el.$Elements) {
return el.$Elements;
}
var Elements = {
window: els.window,
body: els.body,
main: el,
apply: function(conf){
Core.Class.apply(Elements,conf);
}
};
el.find('*[data-element]').each(function () {
var name = $(this).attr('data-element');
if (name) {
Elements[name] = Elements[name] || el.find('*[data-element="'+name+'"]');
}
});
Elements.apply(extra);
el.$Elements = Elements;
return Elements;
}
this.resizeCalculateWindow = function () {
els.window = $(window);
els.body = $('body');
setTimeout(function () {
els.bodyHeight = els.body.height();
els.windowMaxScroll = els.bodyHeight - els.window.height() * 2;
}, 300);
}
this.isMaxWindowScroll = function (budget) {
var top = Math.min(Math.min(window.pageYOffset, document.documentElement.scrollTop || document.body.scrollTop), window.scrollY),
maxScroll = budget ? (els.bodyHeight - budget) : els.windowMaxScroll;
return top > maxScroll;
}
function bindEvent() {
document.addEventListener('touchmove', function (e) {
VIEW.GlobalTouch.preventMove && e.preventDefault();
}, false);
document.addEventListener('touchstart', function (e) {
VIEW.GlobalTouch.touched = true;
}, false);
document.addEventListener('touchend', function (e) {
VIEW.GlobalTouch.touched = false;
}, false);
//data-prevent-move="start" prevent document to move ontouchstart and cancel ontouchend,
//data-prevent-move="all" will always prevent the whole document to move
els.body.on('touchstart', '* [data-prevent-move]', function () {
VIEW._BasicView.GlobalTouch.preventMove = true;
});
els.body.on('touchend', '* [data-prevent-move="start"]', function () {
VIEW._BasicView.GlobalTouch.preventMove = false;
});
if (VIEW.tapEvent == 'tap') {
els.body.on('click', 'a', function (e) {
e.preventDefault();
return false;
});
els.body.on('tap', 'a', function () {
Core.Event.trigger('redirect', this.href);
});
}
//fix chrome for android active effect remain issue
$.os.android && /Chrome/i.test(window.navigator.userAgent) && els.body.on('touchstart', '* [data-fix-active]', function (e) {
e.preventDefault();
});
els.body.on(VIEW.tapEvent, '* [data-fake-link]', function () {
Core.Event.trigger('redirect', this.getAttribute('data-fake-link'));
});
els.body.on(VIEW.tapEvent == 'tap' ? 'touchstart' : VIEW.tapEvent, '* [data-analytics]', function () {
Core.Event.trigger(this.getAttribute('data-analytics-global') ? 'analytics' : 'analyticsCurView', this.getAttribute('data-analytics'));
});
els.body.on(VIEW.tapEvent, '* [data-eventname]', function () {
var ename = this.getAttribute('data-eventname'),
eparam = this.getAttribute('data-eventparam') || '',
eparams = this.getAttribute('data-eventparams') || '';
if (ename) {
var params = [];
params.push(ename);
if (eparams) {
Array.prototype.push.apply(params, eparams.split(','));
} else if (eparam) {
params.push(eparam);
}
Core.Event.trigger.apply(null, params);
}
});
}
this.show = function (viewCls, autoRevert) {
this.hide(viewCls);
var view = this.getView(viewCls);
!view.hasClass('show') && view.addClass('show');
//auto scroll to history position,and restore title
if (autoRevert === undefined || autoRevert) {
Core.Event.trigger('appModifyTitle', Core.Router.getHistoryTitle());
setTimeout(Core.Router.scrollToHistoryPosition, 100);
restoreShare();
}
return this;
}
this.hide = function (notCls) {
(notCls ? els.views.not('.' + notCls) : els.views).removeClass('show');
return this;
}
/**
*
* option = {
title String 'share title',
text String 'share text',
summary String 'share summary',
imageurl String 'share image url',
thumburl String 'share image thumb url',
link String 'share link'
}
*/
this.renderShare = function (option) {
option = option || {};
option.link = option.link || window.location.href;
option.title = option.title || document.title;
option.summary = option.summary || option.title;
option.text = option.text || option.summary;
option.thumburl = option.thumburl || Actions.dejaShareLogo;
option.imageurl = option.imageurl || option.thumburl;
Core.NativeBridge.set_data_for_share(option);
shareStore[Core.Router.getCurrentHashStr()] = option;
updateWechatShareMeta(option.title, option.summary, option.thumburl || option.imageurl);
updateYiXinShareMeta(option.summary, option.thumburl || option.imageurl);
return this;
}
function updateWechatShareMeta(title, content, link, img) {
WechatShare({
"appid": "",
"img_url": img || Actions.dejaShareLogo,
"img_width": "200",
"img_height": "200",
"link": link || window.location.href,
"url": link || window.location.href,
"desc": content || document.title,
"content": content || document.title,
"title": title || document.title
});
}
function updateYiXinShareMeta(content, img) {
YiXinShare({
content: content || document.title,
img: img || Actions.dejaShareLogo
});
}
function restoreShare() {
if(shareStore[Core.Router.getCurrentHashStr()]){
VIEW.renderShare(shareStore[Core.Router.getCurrentHashStr()]);
}
}
this.lazyLoadImg = function (el) {
el && setTimeout(function () {
el.find("img").unveil(200, function () {
this.onload = function () {
if (/lazy/.test(this.className)) {
this.classList.add('lazy--loaded');
//this.style.opacity = 1;
}
}
});
}, 0);
}
this.renderI18n = function(el){
el.find('*[data-i18n]').each(function () {
var currEl = $(this),
name = currEl.attr('data-i18n');
if (name) {
var attrs = name.split('.'),
text = window[attrs.shift()];
attrs.forEach(function (key) {
if(key.length && text!==undefined){
text = text[key];
}
});
if(text!==undefined){
currEl.html(text);
}
}
});
}
init();
}//end View
module.exports = new View;
================================================
FILE: app/templates/app/src/core/Class.js
================================================
var Subject = require('./Subject');
var Class;
/**
* @param obj
* @param config
* @param promise
*/
function apply(obj, config, promise) {
var conf = typeof config=='function'?config.call(obj):config;
if (conf) {
var attr;
for (attr in conf) {
obj[attr] = promise ? promise(conf[attr]) : conf[attr];
}
}
}
/**
*
* @param obj
* @param config
* @param promise
*/
function applyIf(obj, config, promise) {
var conf = typeof config=='function'?config.call(obj):config;
if (conf) {
var attr;
for (attr in conf) {
if (!obj[attr]) {
obj[attr] = promise ? promise(conf[attr]) : conf[attr];
}
}
}
}
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
if (!Object.keys) {
Object.keys = (function () {
'use strict';
var hasOwnProperty = Object.prototype.hasOwnProperty,
hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'),
dontEnums = [
'toString',
'toLocaleString',
'valueOf',
'hasOwnProperty',
'isPrototypeOf',
'propertyIsEnumerable',
'constructor'
],
dontEnumsLength = dontEnums.length;
return function (obj) {
if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
throw new TypeError('Object.keys called on non-object');
}
var result = [], prop, i;
for (prop in obj) {
if (hasOwnProperty.call(obj, prop)) {
result.push(prop);
}
}
if (hasDontEnumBug) {
for (i = 0; i < dontEnumsLength; i++) {
if (hasOwnProperty.call(obj, dontEnums[i])) {
result.push(dontEnums[i]);
}
}
}
return result;
};
}());
}
//http://stackoverflow.com/a/16788517/479039
function objectEquals(x, y) {
if (x === null || x === undefined || y === null || y === undefined) {
return x === y;
}
// after this just checking type of one would be enough
if (x.constructor !== y.constructor) {
return false;
}
// if they are functions, they should exactly refer to same one (because of closures)
if (x instanceof Function) {
return x === y;
}
// if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
if (x instanceof RegExp) {
return x === y;
}
if (x === y || x.valueOf() === y.valueOf()) {
return true;
}
if (Array.isArray(x) && x.length !== y.length) {
return false;
}
// if they are dates, they must had equal valueOf
if (x instanceof Date) {
return false;
}
// if they are strictly equal, they both need to be object at least
if (!(x instanceof Object)) {
return false;
}
if (!(y instanceof Object)) {
return false;
}
// recursive object equality check
var p = Object.keys(x);
return Object.keys(y).every(function (i) {
return p.indexOf(i) !== -1;
}) &&
p.every(function (i) {
return objectEquals(x[i], y[i]);
});
}
/**
*
* @param superClass
* @param subClass
*/
var extend = (function () {
var F = function () {
};
return function (superClass, subClass) {
F.prototype = superClass.prototype;
subClass.prototype = new F();//空函数避免创建超类的新实例(超类可能较庞大或有大量计算)
subClass.prototype.constructor = subClass;
subClass.superclass = superClass.prototype;//superclass减少子类与超类之间的偶合
//http://stackoverflow.com/questions/12691020/why-javascripts-extend-function-has-to-set-objects-prototypes-constructor-pro
if (superClass.prototype.constructor == Object.prototype.constructor) {
superClass.prototype.constructor = superClass;
}
return subClass;
}
})();
function updateFactory() {
var lastUpdate = 0,
_timeout = 1000 * 60 * 5,
names = {};
/**
*
* @param timeout 时间倍数,默认是1
* @param name
* @returns {boolean}
*/
this.isTimeout = function (timeout, name) {
timeout = _timeout * (timeout || 1);
name = name !== undefined ? names[name] : lastUpdate;
return !name || ( (new Date().getTime()) - name > timeout );
}
this.update = function (name) {
var now = new Date().getTime();
if (name !== undefined) {
if (names[name] === undefined) {
this.reset(name);
} else {
names[name] = now;
}
} else {
lastUpdate = now;
}
}
this.reset = function (name) {
if (name !== undefined) {
names[name] = 0;
} else {
lastUpdate = 0;
}
}
this.resetAll = function () {
names = {};
lastUpdate = 0;
}
}
/**
*
* Model
*
*
* Nested Model:
* e.g.
* var testModel = new Model({
* store: new Model({
* request: function(){
* console.log('store.request',this);
* }
* }),
* request: function(){
* console.log('request',this);
* }
* });
*
* testModel.updated(function(){
* console.log(testModel.get());
* testModel.request();
* });
* testModel.set('1');
*
* testModel.store.updated(function(){
* console.log(testModel.store.get());
* testModel.store.request();
* });
* testModel.store.set('2');
*/
function Model(option) {
Model.superclass.constructor.call(this);
this.updated = this.register;
this.refresh = this.notify;
this.data;
apply(this, option);
//数据缓存更新
this.updateFactory = updateFactory;
this.timer = new updateFactory;
}
extend(Subject, Model);
Model.prototype.store = function (storeid, data) {
this._cacheStore = this._cacheStore || {};
if (data && storeid) {
if (toString.call(data) == '[object Array]') {
this._cacheStore[storeid] = {
__STORE_ID: storeid,
data: data
};
} else {
this._cacheStore[storeid] = /function/i.test(typeof data)
? data.call(this, this._cacheStore[storeid])
: data;
if (typeof this._cacheStore[storeid] == 'object') {
this._cacheStore[storeid].__STORE_ID = storeid;
}
}
}
}
/**
*
* @param data
* @param storeid ,will cache in store,use getFromStoreById(storeid) to get access it
* @param diff ,if true,and nothing changed between the new data in the old data,data observers will not got notify
*/
Model.prototype.set = function (data, storeid, diff) {
var _data = /function/i.test(typeof data) ? data.call(this) : data;
diff = diff && objectEquals(this.data, _data);
this.data = _data;
if (storeid) {
this.store(storeid, data);
}
!diff && this.refresh();
this.timer.update();
}
/**
*
* @param clone will return a copy of the data
* @returns {*}
*/
Model.prototype.get = function (clone) {
return (clone && typeof this.data == 'object') ? JSON.parse(JSON.stringify(this.data)) : this.data;
}
/**
*
* @param clone will return a copy of the data
* @returns {*}
*/
Model.prototype.getStore = function (clone) {
return (clone && typeof this._cacheStore == 'object') ? JSON.parse(JSON.stringify(this._cacheStore)) : this._cacheStore;
}
/**
*
* @param storeid
* @param clone ,will return a copy of the data
* @returns {*|{}}
*/
Model.prototype.getFromStoreById = function (storeid, clone) {
return storeid
&& this._cacheStore
&& ( (clone && typeof this._cacheStore[storeid] == 'object')
? JSON.parse(JSON.stringify(this._cacheStore[storeid]))
: this._cacheStore[storeid]);
}
/**
*
* @param storeid
* @returns {*|{}}
*/
Model.prototype.deleteFromStoreById = function (storeid) {
storeid
&& this._cacheStore
&& delete this._cacheStore[storeid];
}
//end Model
Class = {
objectEquals: objectEquals,
apply: apply,
applyIf: applyIf,
extend: extend,
Model: Model
}
//end Class
module.exports = Class;
================================================
FILE: app/templates/app/src/core/Event.js
================================================
var Class = require('./Class');
var Pubsub = require('./Pubsub');
var Subject = require('./Subject');
function Event(Subject) {
Event.superclass.constructor.call(this, Subject);
this.on = this.subscribe;
this.off = this.unsubscribe;
this.trigger = this.publish;
}
Class.extend(Pubsub, Event);
module.exports = new Event(Subject);
================================================
FILE: app/templates/app/src/core/HashHandler.js
================================================
var HashHandler = (function () {
var lc = window.location;
function getByURL(url) {
var hash;
url && decodeURIComponent(url).replace(new RegExp('#(.*)', 'g'), function ($1, $2) {
hash = $2;
});
return hash;
}
function get() {
return getByURL(lc.hash);
}
function set(hash) {
lc.hash = hash;
}
return {
get: get,
set: set,
getByURL: getByURL
}
})();
module.exports = HashHandler;
================================================
FILE: app/templates/app/src/core/MicroTmpl.js
================================================
/**
* 模板解析
* 1. 可接收 DOM Element,不支持 DOM Element 模板里嵌套模板
* 2. 如果 DOM Element 中的元素的属性可加前缀micro-(或ntes-),如img的src改为micro-src;及内样式style改为micro-style。以避免模板没有使用前生效。最终模板将会替换掉micro-(或ntes-)前缀。
*
* e.g.
*
*
* 如果 mustache 参数为true,默认为false,可使用{{}},不支持JS表达式
* e.g
*
*/
var microTmpl = function (mustache) {
var intro = mustache ? '{{' : '<%',
outro = mustache ? '}}' : '%>',
tmplAttrs = ['micro-template', 'ntes-template'],
childTmplAttrs = ['micro-template-child', 'ntes-template-child'];
//http://stackoverflow.com/questions/384286/javascript-isdom-how-do-you-check-if-a-javascript-object-is-a-dom-object?answertab=votes#tab-top
function isElement(o) {
return (
typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"
);
}
function hasChildTmplAttr(el) {
var i = 0;
for (; i < childTmplAttrs.length; i++) {
if (el.hasAttribute(childTmplAttrs[i])) {
return true;
}
}
return false;
}
function removeChildTmplAttrs(el) {
var i = 0;
for (; i < childTmplAttrs.length; i++) {
el.removeAttribute(childTmplAttrs[i]);
}
}
function getTmpl(str) {
//如果是DOM节点则取outerHTML或者innerHTML
if (isElement(str)) {
if (hasChildTmplAttr(str) || str.tagName.toLowerCase() == 'script') {
var text = str.innerHTML;
str.innerHTML = '';
removeChildTmplAttrs(str);
str = text;
} else {
str = str.outerHTML;
}
}
//将模板中所有 micro-(或者micro-template,ntes-,ntes-template)替换为空
return str && str.toString().replace(/(micro|ntes)-(template)?/g, '');
}
//http://ejohn.org/blog/javascript-micro-templating/
var cache = {};
function tmpl(str, data) {
str = getTmpl(str);
var reg1 = new RegExp('((^|' + outro + ")[^\t]*)'", 'g');
var reg2 = new RegExp('\t' + (mustache ? '' : '=') + '(.*?)' + outro, 'g');
var fn = !/\W/.test(str) ? //W大写,可以匹配任何一个字母或者数字或者下划线以外的字符
cache[str] = cache[str] :
new Function("obj",
"var p=[],print=function(){p.push.apply(p,arguments);};"
+ "with(obj){p.push('"
+ str
.replace(/[\r\t\n]/g, " ") //将"\r\t\n"先替换成" "
//.split("<%").join("\t") //将模板开始符号"<%"全部替换成"\t"
.split(intro).join("\t")//--> split("<%").join("\t")
//.replace(/((^|%>)[^\t]*)'/g, "$1\r") //将将模板结束符号%>全部替换成\r
.replace(reg1, "$1\r")//--> replace(/((^|%>)[^\t]*)'/g, "$1\r")
//.replace(/\t=(.*?)%>/g, "',$1,'") //将=与%>之间的变量替换成",变量,"
.replace(reg2, "',$1,'")//--> replace(/\t=(.*?)%>/g, "',$1,'")
.split("\t").join("');") //将之前替换的"\t"再替换成");"
//.split("%>").join("p.push('")
.split(outro).join("p.push('")//-->split("%>").join("p.push('")
.split("\r").join("\\'")
+ "');}return p.join('');");
return data ? fn(data) : fn;
}
return tmpl;
};
module.exports = microTmpl;
================================================
FILE: app/templates/app/src/core/NativeBridge.js
================================================
var Navigator = require('./Navigator');
/**
* 与客户端交互
*
* 前端与客户端交互的流程主要为前端调用,客户端读取,客户端回调三个步骤:
* 前端调用客户端规则为:dejafashion://NAME(android 前端调用window.__dejafashion_NAME())
* 客户端读取前端的数据规则为 window.__dejafashion_data_NAME,前端返回JSON {default:'',...}
* 【如果有】客户端处理完逻辑回调前端的方法规则为: window.__dejafashion_after_NAME([json])
* 拿分享为例:
* 1 -------> 前端调用dejafashion://share(android 前端调用window.__dejafashion_share())
* 2 --------> 客户端接收到请求,向前端读取必要参数window.__dejafashion_data_share
* 3 ------- >【如果有】 客户端处理完逻辑回调通知前端处理完毕调用window.__dejafashion_after_share([json])
* 4 ------>前端接收客户端传回来的参数该干嘛干嘛
* 注意,客户端在调用前端方法之前一定要先做判断,如 window.__dejafashion_after_share && window.__dejafashion_after_share([json])
*
*/
function NativeBridge(protocolHandler) {
var _NB = this,
global = window,
emptyFn = function () {
},
appUA = (/Deja/ig).test(navigator.userAgent),
debug = false,
afterCallbacks = {},
Protocols = {},
baseName = 'dejafashion',
baseProtocol = baseName + '://',
baseObjName = '__' + baseName,
baseDataName = baseObjName + '_data_',
baseBeforeName = baseObjName + '_before_',
baseAfterName = baseObjName + '_after_',
baseUpdateDataName = 'set_data_for_',
baseUpdateBeforeName = 'set_before_for_';
afterCallbacks = {};
Protocols = {};
function enableDebug() {
debug = true;
}
function isApp() {
return appUA || debug;
}
function protocol(action, callback) {
protocolHandler(action, true);
//开启调试
if (debug && callback) {
var _data = action.match(/[\w]:\/\/(.*)/);
if (typeof callback == 'function') {
callback(_data && _data[1]);
}
}
}
function afterCallback(rs, callback) {
callback = callback || emptyFn;
if (typeof callback == 'function') {
callback(rs);
}
callback = emptyFn;
}
function updateData(name, data) {
if (data != null && data != undefined) {
if (!/object/i.test(typeof data)) {
data = {default: data};
}
global[baseDataName + name] = data;
}
}
function updateBefore(name, fn) {
if (/function/i.test(typeof fn)) {
global[baseBeforeName + name] = fn;
} else {
delete global[baseBeforeName + name];
}
}
/**
* set_data_for_NAME = function(data)
* @param name
* @param fn
*/
function registerUpdateDataFn(name, fn) {
var updateName = baseUpdateDataName + name;
_NB[updateName] = fn || function (data) {
updateData(name, data);
}
}
/**
* set_before_for_NAME = function(callback)
* @param name
*/
function registerBeforeFn(name) {
var beforeName = baseUpdateBeforeName + name;
_NB[beforeName] = function (fn) {
updateBefore(name, fn);
}
}
/**
* register a native API
*
* @param name
* @param fn
* @returns {*}
*/
function registerFn(name, fn) {
Protocols[name] = baseProtocol + name;
afterCallbacks[name] = emptyFn;
global[baseAfterName + name] = function (rs) {
afterCallback(rs, afterCallbacks[name]);
}
registerUpdateDataFn(name);
_NB[name] = fn
|| function (data, callback, subProtocol) {
updateData(name, data);
afterCallbacks[name] = callback;
if (isApp()) {
if (global[baseObjName] && global[baseObjName][name]) {
global[baseObjName][name]();
} else {
protocol(Protocols[name] + (subProtocol ? ('/' + subProtocol) : ''), callback);
}
}
};
return _NB[name];
}
/**
*
* execute a native API by it's name
* if it's not exist then register it and execute it
*
* @param name
*/
function trigger(name) {
var fn = _NB[name],
args = [].slice.call(arguments, 1);
if (!fn) {
fn = registerFn(name);
}
fn.apply(_NB, args);
}
this.isApp = isApp;
this.enableDebug = enableDebug;
this.trigger = trigger;
['userInfo', 'login', 'share', 'modifytitle', 'updateBarButton', 'setBgColor', 'copy', 'closeweb'].forEach(function (key, index) {
registerFn(key);
});
['facebook', 'twitter', 'instagram'].forEach(function (key, index) {
_NB['share_' + key] = function (data, callback) {
_NB['share'](data, callback, key);
}
_NB[baseUpdateDataName + 'share_' + key] = _NB[baseUpdateDataName + 'share'];
});
['unload'].forEach(function (key, index) {
registerBeforeFn(key);
});
}
module.exports = new NativeBridge(Navigator.protocol);
================================================
FILE: app/templates/app/src/core/Navigator.js
================================================
var Navigator = (function () {
var frame,
androidReg = /Android/gi,
isAndroid = androidReg.test(navigator.platform) || androidReg.test(navigator.userAgent);
/**
* iframe 元素
*
* @property {Element} frame
*/
frame = null;
/**
* append iframe
*
* @param frame
*/
function appendFrame(frame) {
frame && document.body.appendChild(frame);
}
/**
* 删除iframe
*
* @method removeFrame
* @param {Element} frame 执行的方法
*/
function removeFrame(frame) {
frame && frame.parentNode.removeChild(frame);
}
/**
* 创建iframe,帮助解决iOS的UIWebView没有JS API
*
* @method getFrame
* @return {Element} iframe
*/
function getFrame(src, name) {
var _frame = document.createElement("iframe");
_frame.setAttribute("style", "display:none;width:0;height:0;position: absolute;top:0;left:0;border:0;");
_frame.setAttribute("height", "0px");
_frame.setAttribute("width", "0px");
_frame.setAttribute("frameborder", "0");
name && _frame.setAttribute("name", name);
if (src) {
_frame.setAttribute("src", src);
} else {
appendFrame(_frame);
}
return _frame;
}
/**
* 执行与客户端交互方法的命令
*
* @method excute
* @param {String} ns 与客户端交互的协议server/类Class
* @param {String} fn 执行的方法
* @param {Object} option 参数
* @param {boolean} single 是否是使用独立的iframe,默认false
* @param {boolean} noframe 是否不通过iframe,默认false
*/
function excute(ns, fn, option, single, noframe) {
var data, command;
data = option ? JSON.stringify(option) : '';//将JSON转换成字符串
if (ns && (typeof ns == 'object') && ns[fn]) {//android
ns[fn](data);
} else {//iOS
command = ns;
if (typeof fn == 'string' && fn.length > 0) {
command += fn + '/' + data;
}
protocol(command, single, noframe);
}
}
/**
* 执行与客户端交互的协议
*
* @method protocol
* @param {String} command 执行的协议及命令
* @param {boolean} single 是否是使用独立的iframe,默认false
* @param {boolean} noframe 是否不通过iframe,默认false
*/
function protocol(command, single, noframe) {
var _frame, timer;
//不通过iframe
if (noframe) {
window.location.href = command;
return;
}
//通过iframe
if (single) {
if (isAndroid) {
_frame = getFrame();
_frame.setAttribute("src", command);
} else {
_frame = getFrame(command);
appendFrame(_frame);
}
timer = setTimeout(function () {
_frame && removeFrame(_frame);
}, 30000);
_frame.onload = _frame.onreadystatechange = function () {
timer && clearTimeout(timer);
setTimeout(function () {
_frame && removeFrame(_frame);
}, 3500);
}
} else {
frame = frame || getFrame();
frame.setAttribute("src", command);
}
}
return {
protocol: protocol,
excute: excute,
getFrame: getFrame,
appendFrame: appendFrame,
removeFrame: removeFrame
}
})();//end Object Navigator
module.exports = Navigator;
================================================
FILE: app/templates/app/src/core/Pubsub.js
================================================
function Pubsub(Subject) {
var topics = {};
function subscribe(topic, observer) {
var subject;
for (var key in topics) {
if (key === topic) {
subject = topics[key];
break;
}
}
if (!subject) {
subject = new Subject();
addTopic(topic, subject);
}
subject.register(observer);
return this;
}
function unsubscribe(topic) {
removeTopic(topic);
return this;
}
function publish(topic) {
var args = [].slice.call(arguments);
topics[topic] && topics[topic].notify.apply(topics[topic], args.slice(1));
return this;
}
function addTopic(topic, subject) {
topics[topic] = subject;
}
function removeTopic(topic) {
delete topics[topic];
}
function getTopics() {
var _topics = [];
for (var key in topics) {
(typeof key === 'string') && _topics.push(key);
}
return _topics;
}
this.getTopics = getTopics;
this.subscribe = subscribe;
this.unsubscribe = unsubscribe;
this.publish = publish;
}
module.exports = Pubsub;
================================================
FILE: app/templates/app/src/core/Router.js
================================================
var Pubsub = require('./Pubsub');
var Subject = require('./Subject');
var HashHandler = require('./HashHandler');
/**
*
* Router 初始化流程:
* init(withAction) --> onReady 是否注册有 Callback --> (是)执行所有 Callback (在关键Callback里,手动执行 run 来强制刷新是推荐的)
* --> (否)执行 run
*
*
* Hash 变化事件执行流程:
* Hash变化(或者显示调用 run,forward,back方法) --> 执行 onChanged 注册的所有事件 --> 执行 subscribe 注册的与 Hash 关联的所有事件
*
* subscribe 注册的事件回调方法传递参数:
* actionValue - 当前 action 之后的字符串,如/user/id=1&name=test 对应 action 是 /user/ ,actionValue 是 id=1&name=test
* request - { action: 当前action字符串,
valeu: actionValue,
hash: {
curHash: String,
newHash: String,
oldHash: String
},
query: actionValue的键值对,如 {id:1,name:test}
}
*
* 本 Router 暂不支持正则表达式
*/
function Router(Pubsub, HashHandler) {
var _Router = this,
subscribe = Pubsub.subscribe,
android = /Android/gi.test(navigator.userAgent),
iOS = /(iPad|iPhone|iPod)/gi.test(navigator.userAgent) && !android,
UN_SUB_NAME = '__UN_SUBSCRIBED_ACTION',
INIT_HASH_STR = formatHash(HashHandler.get()),
currentHash,
currentHashStr = INIT_HASH_STR || UN_SUB_NAME,
currentQureyStr = '',
lastActionKey,
leavePrefix = '__',
_isFroward = true,
isReady = false,
initCallback,
readyCallbacks = [],
changedCallbacks = [],
actionsHistory = [INIT_HASH_STR],
queryHistory = {},
historyPositions = {},
historyTitles = {},
anchorEl;
//iOS使用pushstate,解决iOS7没有历史的问题
if (iOS) {
window.addEventListener('popstate', locationHashChanged, false);
} else {
window.addEventListener('hashchange', locationHashChanged, false);
}
function getQuery(search) {
search = search || currentQureyStr || '';
var fn = function (str, reg) {
if (str) {
var data = {};
str.replace(reg, function ($0, $1, $2, $3) {
data[$1] = $3;
});
return data;
}
}
return fn(search, new RegExp("([^?=&]+)(=([^&]*))?", "g")) || {};
}
function saveQuery(key, query) {
queryHistory[key] = queryHistory[key] || [];
queryHistory[key].push(query);
}
function getLastQuery(key) {
return queryHistory[key]?(queryHistory[key][queryHistory[key].length-1]||{}):{};
}
function formatHash(hash) {
if (hash) {
//hash后不能带search值
hash = hash.replace(/\?.*/g, '');
}
return hash;
}
function locationHashChanged(e) {
e && e.preventDefault();
var args = arguments[0] || {},
hash;
hash = {
curHash: formatHash(HashHandler.get()),
newHash: formatHash(HashHandler.getByURL(args.newURL)),
oldHash: formatHash(HashHandler.getByURL(args.oldURL))
}
setHistoryPosition();
setHistoryTitle();
currentHash = hash;
currentHashStr = hash.curHash || UN_SUB_NAME;
setLastAction(hash.curHash);
initCallback && initCallback(hash.curHash, hash);
if (isReady) {
doChanged(hash.curHash, hash);
dispatch(hash);
}
hash.curHash && addAnchor(hash.curHash);
return false;
}
function dispatch(hash) {
var topics = Pubsub.getTopics(),
published = false;
if (hash.curHash !== undefined) {
for (var i = 0; i < topics.length; i++) {
var key = topics[i];
if (key !== UN_SUB_NAME) {
hash.curHash.replace(new RegExp('^' + key + '(.*)', 'g'), function ($1, $2) {
if ($1) {
currentQureyStr = $2;
published = true;
lastActionKey = key;
restoreHistoryTitle();
var query = getQuery($2);
Pubsub.publish(key, {
action: key,
param: $2,
hash: hash,
preQuery: getLastQuery(key),
query: query
});
saveQuery(key, query);
}
});
}
}
}
if (!published) {
lastActionKey = UN_SUB_NAME;
currentQureyStr = hash.curHash;
restoreHistoryTitle();
var query = getQuery(hash.curHash);
Pubsub.publish(UN_SUB_NAME, {
action: hash.curHash,
param: hash.curHash,
hash: hash,
preQuery: getLastQuery(UN_SUB_NAME),
query: query
});
saveQuery(UN_SUB_NAME, query);
}
}
/**
* 手动初始化
* 如在onReady里注册了很多的callback,可以通过此方法手动初始化它们
* 如果带上 withAction 可实现:
* 在所有主题被订阅之前,在页面首次加载之初,初始化一个与源hash不同且暂未被订阅的主题,最终仍然跳转到源hash
*
* 此方法并不是必要的
* @param {String} withAction 初始化的action
*/
function init(withAction) {
if ((withAction === null) || (withAction === undefined) || (withAction === '' )) {
ready();
} else {
//注意原初始化的action不应该包含在将初始化的hash里
var reg = new RegExp('^' + withAction + '(.*)', 'i');
if (INIT_HASH_STR && !reg.test(INIT_HASH_STR)) {
initCallback = function (curHash) {
if (curHash === INIT_HASH_STR) {
initCallback = null;
setTimeout(function () {
ready();
}, 0);
} else if (curHash === withAction) {
forward(INIT_HASH_STR);
}
};
forward(withAction);
} else {
ready();
}
}
return Pubsub;
}
/**
* 强制刷新
* 在无需引起 hash 变化情况下,强制执行与当前 hash 关联的所有主题一次
* 流程:
* 执行 onChanged 注册的所有事件 --> 执行 subscribe 注册的与 Hash 关联的所有事件
* 如果:action 不为空,则仅执行 subscribe 注册的与 action 关联的所有事件
* @param action {Array}|{String}
* @returns {Pubsub}
*/
function run(action) {
action ?
Pubsub.publish(action, {
action: action,
param: currentQureyStr,
hash: currentHash,
preQuery: getLastQuery(action),
query: getQuery()
})
: locationHashChanged();
return Pubsub;
}
/**
* 订阅所有没有被注册的主题
* @param {Object} observer
*/
function onUnsubscribed(enterObserver, leaveObserver) {
onSubscribe(UN_SUB_NAME, enterObserver, leaveObserver);
return Pubsub;
}
/**
* 订阅所有没有被注册的主题
* @param action {Array}|{String}
* @param {Object} observer
* @param {Object} observer
*/
function onSubscribe(action, enterObserver, leaveObserver) {
subscribe.call(Pubsub, action, enterObserver);
leaveObserver && subscribe.call(Pubsub, leavePrefix + action, leaveObserver);
return Pubsub;
}
/**
* 当hash发生变化时触发,会先于所有订阅的主题触发
*/
function onChanged(callback) {
if (typeof callback === 'function') {
changedCallbacks.push(callback);
}
return Pubsub;
}
/**
* 手动执行init方法会执行通过此方法注册的所有callback方法
* @param callback
*/
function onReady(callback) {
if (typeof callback === 'function') {
readyCallbacks.push(callback);
}
return Pubsub;
}
function ready() {
isReady = true;
//如果 onReady 方法中有回调则执行回调
//注意,onReady 的回调里需要有且仅有一个要显示调用 run 来强制执行所有主题的调回
if (readyCallbacks.length) {
while (readyCallbacks.length) {
readyCallbacks.shift().call(_Router, Pubsub)
}
}
//否则自动强制执行所有主题
else {
run();
}
}
function doChanged() {
var i = 0,
l = changedCallbacks.length;
for (; i < l; i++) {
changedCallbacks[i].apply(undefined, arguments);
}
lastActionKey && Pubsub.publish(leavePrefix + lastActionKey);
}
/**
* 跳转到一个指定的主题
* @param {String}|{Number} action
*/
function forward(action) {
_isFroward = true;
if (action === null) {
window.history.forward();
} else if (typeof action === 'number') {
if (action == -1) {
_isFroward = false;
}
window.history.go(action);
} else if (typeof action === 'string') {
if (iOS) {
window.history.pushState(null, null, '#' + action);
run();
} else {
HashHandler.set(action);
}
}
return Pubsub;
}
/**
* 返回上一主题
* action仅在解决当不确实是否有浏览历史,并且又需要跳转到一个指定的hash值时
* 优先级是: 浏览器历史 > actionsHistory > action
* @param {String}|{Number} action
*/
function back(action) {
var ac = popLastAction() || action || -1;
//如果浏览器有历史则走历史
if (window.history.length > 1) {
ac = -1;
}
forward(ac);
return Pubsub;
}
function setLastAction(action) {
var ac = [].concat.call(actionsHistory).pop();
if (ac != action) {
actionsHistory.push(action);
}
}
function getLastAction() {
var last = [].concat.call(actionsHistory);
last.pop();
return last.pop();
}
function popLastAction() {
//pop两次是因为最后一次是当前的
actionsHistory.pop();
return actionsHistory.pop();
}
function setFirstAction(action) {
var ac = [].concat.call(actionsHistory).shift();
if (ac != action) {
actionsHistory.unshift(action);
}
}
function getFirstAction() {
return actionsHistory.shift();
}
function isFroward() {
return _isFroward;
}
/**
* 解决历史滚动位置记录的问题,保证视图切换总在最顶部
* @param id
*/
function addAnchor(id) {
return;//暂停使用
if (!anchorEl) {
var st = document.createElement('style');
anchorEl = document.createElement('div');
st.innerText = '.Router-anchor{position: fixed; top: 0; left: 0;}';
anchorEl.className = 'Router-anchor';
document.body.appendChild(st);
document.body.appendChild(anchorEl);
}
var cd = document.createElement('div'),
od = document.getElementById(id);
cd.id = id;
anchorEl.appendChild(cd);
if (od) {
anchorEl.removeChild(od);
}
}
/**
* 校验是否存在与当前的 action 匹配的 action
* @param action {Array}|{String}
* @returns {boolean}
*/
function actionMatch(expected, actual) {
var ac = [], i = 0, l;
if (typeof expected === 'string') {
ac.push(expected)
} else if (toString.call(expected) == '[object Array]') {
ac = ac.concat(expected)
}
l = ac.length;
for (; i < l; i++) {
if ((new RegExp('^' + ac[i] + '(.*)', 'i')).test(actual)) {
return true;
}
}
return false;
}
/**
* 校验是否存在与当前的 action 匹配的 action
* @param action {Array}|{String}
* @returns {boolean}
*/
function currentMatch(action) {
return actionMatch(action, currentHashStr || UN_SUB_NAME);
}
/**
* 校验是否存在与上一个 action 匹配的 action
* @param action {Array}|{String}
* @returns {boolean}
*/
function lastMatch(action) {
return actionMatch(action, getLastAction() || UN_SUB_NAME);
}
function setHistoryPosition(id, position) {
id = id || currentHashStr;
if (id) {
historyPositions[id] = position || window.pageYOffset || window.scrollY || document.body.scrollTop;
}
}
function getHistoryPosition(id) {
id = id || currentHashStr;
return id && historyPositions[id];
}
function scrollToHistoryPosition(id) {
window.scrollTo(0, getHistoryPosition(id) || 1);
setHistoryPosition(id, 0);
}
function setHistoryTitle(id, title) {
id = id || currentHashStr;
if (id) {
historyTitles[id] = title || document.title;
}
}
function getHistoryTitle(id) {
id = id || currentHashStr;
return id && historyTitles[id];
}
function restoreHistoryTitle(id) {
var title = getHistoryTitle(id);
if (title) {
document.title = title;
}
}
function getCurrentHashStr() {
return currentHashStr;
}
Pubsub.initHash = INIT_HASH_STR;
Pubsub.init = init;
Pubsub.run = run;
Pubsub.forward = forward;
Pubsub.back = back;
Pubsub.isFroward = isFroward;
Pubsub.currentMatch = currentMatch;
Pubsub.lastMatch = lastMatch;
Pubsub.onReady = onReady;
Pubsub.onChanged = onChanged;
Pubsub.subscribe = onSubscribe;
Pubsub.onUnsubscribed = onUnsubscribed;
Pubsub.getQuery = getQuery;
Pubsub.getHistoryPosition = getHistoryPosition;
Pubsub.scrollToHistoryPosition = scrollToHistoryPosition;
Pubsub.getHistoryTitle = getHistoryTitle;
Pubsub.getCurrentHashStr = getCurrentHashStr;
Pubsub.getUnsubscribedAction = function () {
return UN_SUB_NAME;
};
return Pubsub;
}
module.exports = Router(new Pubsub(Subject), HashHandler);
================================================
FILE: app/templates/app/src/core/Subject.js
================================================
function Subject(subject) {
this._subject = subject;
this.observers = [];
}
Subject.prototype = {
/**
* @param {Function}|{Boject} observer
*/
register: function (observer) {
if (!observer) {
throw new Error('An observer can not be undefined!');
} else if (typeof observer === 'object' && typeof observer.update !== 'function') {
throw {
name: 'Error',
method: 'Subject.register',
message: 'An observer object can not register without an update method!'
}
}
this.unregister(observer);//防止重复注册
this.observers.push(observer);
return this;
},
/**
* @param {Function}|{Boject} observer
*/
unregister: function (observer) {
this.observers = this.observers.filter(function (obsv) {
if (obsv !== observer) {
return obsv;
}
});
return this;
},
notify: function () {
var args = [].slice.call(arguments);
this.observers.forEach(function (obsv) {
if (typeof obsv === 'function') {
obsv.apply(obsv, args);
} else {
obsv.update.apply(obsv, args);
}
});
return this;
}
}
module.exports = Subject;
================================================
FILE: app/templates/app/src/lib/Core.js
================================================
require('util/RequestAnimationFrame');
require('util/Easing');
require('util/Unveil');
require('util/VirtualDOMLite');
var Navigator = require('core/Navigator');
var Subject = require('core/Subject');
var MicroTmpl = require('core/MicroTmpl');
var Class = require('core/Class');
var NativeBridge = require('core/NativeBridge');
var Router = require('core/Router');
var HashHandler = require('core/HashHandler');
var Event = require('core/Event');
var localStorage = require('util/LocalStorage');
var LocalHost = require('util/LocalHost');
var localParam = require('util/LocalParam');
var MetaHandler = require('util/MetaHandler');
var RequestHandler = require('util/RequestHandler');
var versionCompare = require('util/versionCompare');
var FormHandler = require('util/FormHandler');
var randomList = require('util/RandomList');
var Num = require('util/Number');
var GUID = require('util/GUID');
var DateHandler = require('util/DateHandler');
var Core = {
localStorage: localStorage,
localHost: LocalHost,
localParam: localParam,
Navigator: Navigator,
MetaHandler: MetaHandler,
Subject: Subject,
microTmpl: MicroTmpl(),
Class: Class,
extend: Class.extend,
HashHandler: HashHandler,
RequestHandler: RequestHandler,
NativeBridge: NativeBridge,
versionCompare: versionCompare,
FormHandler: FormHandler,
Event: Event,
Router: Router,
Num: Num,
GUID: GUID,
randomList: randomList,
DateHandler: DateHandler,
isDebug: localParam().search['debug'] == 1
};
//enable debug model
Core.isDebug && Core.NativeBridge.enableDebug();
window.Core = Core;
module.exports = Core;
================================================
FILE: app/templates/app/src/lib/Core.standalone.js
================================================
require('util/RequestAnimationFrame');
var localStorage = require('core/LocalStorage');
var Navigator = require('core/Navigator');
var Subject = require('core/Subject');
var Class = require('core/Class');
var Event = require('core/Event');
var LocalHost = require('util/LocalHost');
var localParam = require('util/LocalParam');
var RequestHandler = require('util/RequestHandler');
var randomList = require('util/RandomList');
var Num = require('util/Number');
var DateHandler = require('util/DateHandler');
var Core = {
localStorage: localStorage,
localHost: LocalHost,
localParam: localParam,
Navigator: Navigator,
Class: Class,
extend: Class.extend,
RequestHandler: RequestHandler,
Event: Event,
Num: Num,
randomList: randomList,
DateHandler: DateHandler
};
window.Core = Core;
module.exports = Core;
================================================
FILE: app/templates/app/src/lib/diffDOM.js
================================================
"use strict";
var diffcount;
var Diff = function (options) {
var diff = this;
Object.keys(options).forEach(function (option) {
diff[option] = options[option];
});
};
Diff.prototype = {
toString: function () {
return JSON.stringify(this);
}
// TODO: compress diff output by replacing these keys with numbers or alike:
/* 'addAttribute' = 0,
'modifyAttribute' = 1,
'removeAttribute' = 2,
'modifyTextElement' = 3,
'relocateGroup' = 4,
'removeElement' = 5,
'addElement' = 6,
'removeTextElement' = 7,
'addTextElement' = 8,
'replaceElement' = 9,
'modifyValue' = 10,
'modifyChecked' = 11,
'modifySelected' = 12,
'modifyComment' = 13,
'action' = 14,
'route' = 15,
'oldValue' = 16,
'newValue' = 17,
'element' = 18,
'group' = 19,
'from' = 20,
'to' = 21,
'name' = 22,
'value' = 23,
'data' = 24,
'attributes' = 25,
'nodeName' = 26,
'childNodes' = 27,
'checked' = 28,
'selected' = 29;*/
};
var SubsetMapping = function SubsetMapping(a, b) {
this.old = a;
this.new = b;
};
SubsetMapping.prototype = {
contains: function contains(subset) {
if (subset.length < this.length) {
return subset.new >= this.new && subset.new < this.new + this.length;
}
return false;
},
toString: function toString() {
return this.length + " element subset, first mapping: old " + this.old + " → new " + this.new;
}
};
var elementDescriptors = function (el) {
var output = [];
if (el.nodeName != '#text' && el.nodeName != '#comment') {
output.push(el.nodeName);
if (el.attributes) {
if (el.attributes.class) {
output.push(el.nodeName + '.' + el.attributes.class.replace(/ /g, '.'));
}
if (el.attributes.id) {
output.push(el.nodeName + '#' + el.attributes.id);
}
}
}
return output;
};
var findUniqueDescriptors = function (li) {
var uniqueDescriptors = {},
duplicateDescriptors = {};
li.forEach(function (node) {
elementDescriptors(node).forEach(function (descriptor) {
var inUnique = descriptor in uniqueDescriptors,
inDupes = descriptor in duplicateDescriptors;
if (!inUnique && !inDupes) {
uniqueDescriptors[descriptor] = true;
} else if (inUnique) {
delete uniqueDescriptors[descriptor];
duplicateDescriptors[descriptor] = true;
}
});
});
return uniqueDescriptors;
};
var uniqueInBoth = function (l1, l2) {
var l1Unique = findUniqueDescriptors(l1),
l2Unique = findUniqueDescriptors(l2),
inBoth = {};
Object.keys(l1Unique).forEach(function (key) {
if (l2Unique[key]) {
inBoth[key] = true;
}
});
return inBoth;
};
var removeDone = function (tree) {
delete tree.outerDone;
delete tree.innerDone;
delete tree.valueDone;
if (tree.childNodes) {
return tree.childNodes.every(removeDone);
} else {
return true;
}
};
var isEqual = function (e1, e2) {
var e1Attributes, e2Attributes;
if (!['nodeName', 'value', 'checked', 'selected', 'data'].every(function (element) {
if (e1[element] !== e2[element]) {
return false;
}
return true;
})) {
return false;
}
if (Boolean(e1.attributes) !== Boolean(e2.attributes)) {
return false;
}
if (Boolean(e1.childNodes) !== Boolean(e2.childNodes)) {
return false;
}
if (e1.attributes) {
e1Attributes = Object.keys(e1.attributes);
e2Attributes = Object.keys(e2.attributes);
if (e1Attributes.length != e2Attributes.length) {
return false;
}
if (!e1Attributes.every(function (attribute) {
if (e1.attributes[attribute] !== e2.attributes[attribute]) {
return false;
}
})) {
return false;
}
}
if (e1.childNodes) {
if (e1.childNodes.length !== e2.childNodes.length) {
return false;
}
if (!e1.childNodes.every(function (childNode, index) {
return isEqual(childNode, e2.childNodes[index]);
})) {
return false;
}
}
return true;
};
var roughlyEqual = function (e1, e2, uniqueDescriptors, sameSiblings, preventRecursion) {
var childUniqueDescriptors, nodeList1, nodeList2;
if (!e1 || !e2) {
return false;
}
if (e1.nodeName !== e2.nodeName) {
return false;
}
if (e1.nodeName === '#text') {
// Note that we initially don't care what the text content of a node is,
// the mere fact that it's the same tag and "has text" means it's roughly
// equal, and then we can find out the true text difference later.
return preventRecursion ? true : e1.data === e2.data;
}
if (e1.nodeName in uniqueDescriptors) {
return true;
}
if (e1.attributes && e2.attributes) {
if (e1.attributes.id && e1.attributes.id === e2.attributes.id) {
var idDescriptor = e1.nodeName + '#' + e1.attributes.id;
if (idDescriptor in uniqueDescriptors) {
return true;
}
}
if (e1.attributes.class && e1.attributes.class === e2.attributes.class) {
var classDescriptor = e1.nodeName + '.' + e1.attributes.class.replace(/ /g, '.');
if (classDescriptor in uniqueDescriptors) {
return true;
}
}
}
if (sameSiblings) {
return true;
}
nodeList1 = e1.childNodes ? e1.childNodes.slice().reverse() : [];
nodeList2 = e2.childNodes ? e2.childNodes.slice().reverse() : [];
if (nodeList1.length !== nodeList2.length) {
return false;
}
if (preventRecursion) {
return nodeList1.every(function (element, index) {
return element.nodeName === nodeList2[index].nodeName;
});
} else {
// note: we only allow one level of recursion at any depth. If 'preventRecursion'
// was not set, we must explicitly force it to true for child iterations.
childUniqueDescriptors = uniqueInBoth(nodeList1, nodeList2);
return nodeList1.every(function (element, index) {
return roughlyEqual(element, nodeList2[index], childUniqueDescriptors, true, true);
});
}
};
var cloneObj = function (obj) {
// TODO: Do we really need to clone here? Is it not enough to just return the original object?
return JSON.parse(JSON.stringify(obj));
//return obj;
};
/**
* based on https://en.wikibooks.org/wiki/Algorithm_implementation/Strings/Longest_common_substring#JavaScript
*/
var findCommonSubsets = function (c1, c2, marked1, marked2) {
var lcsSize = 0,
index = [],
matches = Array.apply(null, new Array(c1.length + 1)).map(function () {
return [];
}), // set up the matching table
uniqueDescriptors = uniqueInBoth(c1, c2),
// If all of the elements are the same tag, id and class, then we can
// consider them roughly the same even if they have a different number of
// children. This will reduce removing and re-adding similar elements.
subsetsSame = c1.length === c2.length,
origin, ret;
if (subsetsSame) {
c1.some(function (element, i) {
var c1Desc = elementDescriptors(element),
c2Desc = elementDescriptors(c2[i]);
if (c1Desc.length !== c2Desc.length) {
subsetsSame = false;
return true;
}
c1Desc.some(function (description, i) {
if (description !== c2Desc[i]) {
subsetsSame = false;
return true;
}
});
if (!subsetsSame) {
return true;
}
});
}
// fill the matches with distance values
c1.forEach(function (c1Element, c1Index) {
c2.forEach(function (c2Element, c2Index) {
if (!marked1[c1Index] && !marked2[c2Index] && roughlyEqual(c1Element, c2Element, uniqueDescriptors, subsetsSame)) {
matches[c1Index + 1][c2Index + 1] = (matches[c1Index][c2Index] ? matches[c1Index][c2Index] + 1 : 1);
if (matches[c1Index + 1][c2Index + 1] >= lcsSize) {
lcsSize = matches[c1Index + 1][c2Index + 1];
index = [c1Index + 1, c2Index + 1];
}
} else {
matches[c1Index + 1][c2Index + 1] = 0;
}
});
});
if (lcsSize === 0) {
return false;
}
origin = [index[0] - lcsSize, index[1] - lcsSize];
ret = new SubsetMapping(origin[0], origin[1]);
ret.length = lcsSize;
return ret;
};
/**
* This should really be a predefined function in Array...
*/
var makeArray = function (n, v) {
return Array.apply(null, new Array(n)).map(function () {
return v;
});
};
/**
* Generate arrays that indicate which node belongs to which subset,
* or whether it's actually an orphan node, existing in only one
* of the two trees, rather than somewhere in both.
*
* So if t1 = ![]()