├── .gitignore ├── Gruntfile.js ├── README.md ├── dist ├── BlendHybridUI-0.0.1.js ├── BlendHybridUI-0.0.1.min.js ├── BlendHybridUI-0.0.2.js ├── BlendHybridUI-0.0.2.min.js ├── BlendHybridUI-0.0.3.js ├── BlendHybridUI-0.0.3.min.js ├── BlendHybridUI-0.0.4.js ├── BlendHybridUI-0.0.4.min.js ├── BlendHybridUI-0.0.5.js ├── BlendHybridUI-0.0.5.min.js ├── BlendHybridUI-0.0.6.js ├── BlendHybridUI-0.0.6.min.js ├── BlendHybridUI.min.js ├── BlendUIBoost.js ├── BlendUIBoost.min.js ├── BlendWebUI.js └── BlendWebUI.min.js ├── npm-tools └── blendui ├── package.json ├── res ├── bdloading.png └── crema.css ├── src ├── almond.js ├── boost │ ├── layerTrigger.js │ ├── main.js │ ├── meta.js │ └── sizzle.js ├── common │ ├── lib.js │ ├── lib │ │ ├── lang.js │ │ └── string.js │ └── loader.js ├── hybrid │ ├── CascadingMenu.js │ ├── Control.js │ ├── Footbar.js │ ├── Layer.js │ ├── LayerGroup.js │ ├── Slider.js │ ├── Sms.js │ ├── api │ │ ├── component.js │ │ ├── component │ │ │ ├── cascadingMenu.js │ │ │ ├── footbar.js │ │ │ └── slider.js │ │ ├── config.js │ │ ├── core.js │ │ ├── dialog.js │ │ ├── event.js │ │ ├── layer.js │ │ ├── layerGroup.js │ │ ├── uix.js │ │ └── util.js │ ├── blend.js │ ├── delegateLayer.js │ ├── main.js │ └── runtime.js └── web │ ├── Actions.js │ ├── Control.js │ ├── Dialog.js │ ├── Layer.js │ ├── LayerGroup.js │ ├── Navbar.js │ ├── Popover.js │ ├── Popup.js │ ├── Slider.js │ ├── WebControl.js │ ├── api.js │ ├── blend.js │ ├── configs.js │ ├── dialog │ ├── Confirm.js │ ├── Preloader.js │ ├── Prompt.js │ └── alert.js │ ├── events.js │ ├── layer │ ├── layerGroupApi.js │ └── layerapi.js │ └── main.js ├── test ├── autotest.html ├── autotest.js ├── autotest │ ├── Layer.js │ ├── native-api.js │ └── uatest.js ├── case-test.html └── case │ ├── CascadingMenu.html │ ├── dialog.html │ ├── footbar.html │ ├── index_demo.html │ ├── index_demo1.html │ ├── keyboard.html │ ├── layer.html │ ├── layerGroup.html │ ├── layerload.html │ ├── menuLayer-1.html │ ├── menuLayer.html │ ├── popSelects.html │ ├── silder.html │ ├── sms.html │ └── subLayer.html ├── third_party ├── almond.js ├── chai.js ├── expect.js ├── hammer.js ├── mocha.css ├── mocha.js ├── require.js ├── underscore.js └── zepto.js └── usecase ├── css ├── blendui.css ├── crema.css └── main.css ├── img └── list.png ├── index.html ├── item.html └── js ├── BlendHybridUI.js ├── BlendWebUI.js ├── all.js ├── blend.js └── lib ├── jquery-2.1.1.js └── zepto.js /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | .DS_Store -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function (grunt) { 2 | 3 | 4 | grunt.initConfig({ 5 | pkg: grunt.file.readJSON('package.json'), 6 | requirejs: { 7 | hybrid: { 8 | options: { 9 | baseUrl: "./", 10 | name: 'src/almond', 11 | include: [ 12 | 'src/hybrid/main' 13 | ], 14 | out: 'dist/BlendHybridUI-<%= pkg.version %>.js', 15 | optimize: 'none', 16 | wrap: true 17 | } 18 | }, 19 | web: { 20 | options: { 21 | baseUrl: "./", 22 | name: 'src/almond', 23 | include: [ 24 | 'src/web/main' 25 | ], 26 | out: 'dist/BlendWebUI.js', 27 | optimize: 'none', 28 | wrap: true 29 | } 30 | }, 31 | boost: { 32 | options: { 33 | baseUrl: "./", 34 | name: 'src/almond', 35 | include: ["src/boost/main"], 36 | out: "dist/BlendUIBoost.js", 37 | optimize: "none", 38 | wrap: true 39 | } 40 | } 41 | }, 42 | jshint: { 43 | files: ['Gruntfile.js', 'src/web/**/*.js', 'src/hybrid/**/*.js', 'src/boost/**/*.js'], 44 | options: { 45 | '-W083': true, //for循环中function函数 46 | '-W054': true, //new Function 47 | '-W061': true, 48 | '-W030': true, 49 | sub: true, 50 | globals: { 51 | '$': true, 52 | console: true, 53 | module: true, 54 | document: true 55 | } 56 | } 57 | }, 58 | uglify : { 59 | options: { 60 | banner: '/*! <%= pkg.name %> v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> - http://clouda.com */\n'//添加banner 61 | }, 62 | hybrid: { 63 | files: [{ 64 | src: 'dist/BlendHybridUI-<%= pkg.version %>.js', 65 | dest: 'dist/BlendHybridUI-<%= pkg.version %>.min.js' 66 | }, { 67 | src: 'dist/BlendHybridUI-<%= pkg.version %>.js', 68 | dest: 'dist/BlendHybridUI.min.js' 69 | }] 70 | }, 71 | web: { 72 | src: ['dist/BlendWebUI.js'], 73 | dest: 'dist/BlendWebUI.min.js' 74 | }, 75 | boost: { 76 | src: ['dist/BlendUIBoost.js'], 77 | dest: 'dist/BlendUIBoost.min.js' 78 | } 79 | }, 80 | watch: { 81 | scripts: { 82 | files: ['Gruntfile.js', 'src/web/**/*.js', 'src/hybrid/**/*.js', 'src/boost/**/*.js'], 83 | tasks: ['jshint'], 84 | options: { 85 | spawn: false, 86 | } 87 | }, 88 | }, 89 | mocha:{ 90 | test:{ 91 | src: ['test/autotest.html'], 92 | } 93 | } 94 | autoTest:{ 95 | src: ['test/autotest/*.js'] 96 | } 97 | }); 98 | 99 | grunt.loadNpmTasks('grunt-contrib-requirejs'); 100 | grunt.loadNpmTasks('grunt-contrib-uglify'); 101 | grunt.loadNpmTasks('grunt-contrib-jshint'); 102 | grunt.loadNpmTasks('grunt-contrib-watch'); 103 | grunt.loadNpmTasks('grunt-mocha'); 104 | 105 | grunt.registerMultiTask('autoTest','自动化测试脚本合并',function(){ 106 | var options = this.options(); 107 | var file = []; 108 | this.files.forEach(function(filePair) { 109 | //console.log(filePair); 110 | filePair.src.forEach(function(src) { 111 | file.push('\''+src.replace('test\/','')+'\''); 112 | }); 113 | }); 114 | console.log(this); 115 | var sc = 'require(['+file.join(',')+'],function(){mocha.run();});' 116 | grunt.file.write('test/test.js', sc , { 117 | encoding:"utf8" 118 | }); 119 | }); 120 | 121 | grunt.registerTask('hybrid', [ 122 | 'jshint', 123 | 'requirejs:hybrid', 124 | 'uglify:hybrid' 125 | ]); 126 | grunt.registerTask('web', [ 127 | //'jshint', 128 | 'requirejs:web', 129 | 'uglify:web' 130 | ]); 131 | grunt.registerTask('boost', [ 132 | 'jshint', 133 | 'requirejs:boost', 134 | 'uglify:boost' 135 | ]); 136 | grunt.registerTask('test', [ 137 | 'jshint', 138 | 'mocha' 139 | ]); 140 | }; 141 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BlendUI 2 | ===== 3 | 4 | [![NPM version](https://badge.fury.io/js/blendui.svg)](http://badge.fury.io/js/blendui) 5 | [![Dependency Status](https://david-dm.org/clouda-team/blendui.svg)](https://david-dm.org/clouda-team/blendui) 6 | 7 | ### Installation 8 | ```bash 9 | $ sudo npm install -g blendui 10 | ``` 11 | ### Create the app 12 | ```bash 13 | $ blendui init ./yourpath 14 | ``` 15 | 16 | BlendUI是Clouda+中的重要组成部分,他能让webapp的用户界面体验和交互能和Native媲美。操作性能是webapp中体验最薄弱的一环,具体而言,这包括:转场动画不流畅、DOM结构过于复杂导致卡顿,用Javascript实现固定头尾布局性能较差等。 17 | 18 | 因而,我们用Native技术来扩展Javascript,同时我们选择了最易于理解的方式:让Javascript能像操作DOM那样操作多个webview,以及在webview中嵌入Native组件。 19 | 20 | * 多Webview控制能力。让一个Webapp拆到多个webview中运行,并能用Javascript来调度,解决了页面过大导致卡顿的问题,同时,webview的转场动画由Native代码实现,也解决了转场动画不流畅的问题。 21 | * Native组件嵌入能力。能将Native控件嵌入Webview中,这样就能让页面中那些性能较差的部分用Native来实现,以最大化地提高体验和交互。 22 | 23 | BlendUI只在最基础的部分使用Native,BlendUI的核心消息机制类似传统的web事件,而所有BlendUI组件都可以采用完完全全的web来编写。总之,我们保持了所有web的风格和灵活性。 24 | 25 | 文档 26 | ==== 27 | 28 | 请前往[Clouda+官网](http://cloudaplus.duapp.com/blendui/introduction/introduction)阅读 29 | -------------------------------------------------------------------------------- /npm-tools/blendui: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | var fs = require('fs') 7 | , os = require('os') 8 | , path = require('path') 9 | , util = require('util') 10 | //, mkdirp = fs.mkdir//require('mkdirp') 11 | , exec = require('child_process').exec 12 | , spawn = require('child_process').spawn 13 | , version = require('../package.json').version 14 | ; 15 | 16 | 17 | /** 18 | * Common Variables 19 | */ 20 | var DEV = 'development'; 21 | var PRD = 'production'; 22 | var DEM = '--daemon'; 23 | var DAEMON = false; 24 | var TIME_INIT = 0.1 * 1000; 25 | var TIME_KILL_WAIT = 5 * 1000; 26 | 27 | var CUR_DIR = process.cwd(); 28 | // var IF_SERVERSPACE = fs.existsSync('./run.js'); 29 | // var IF_WORKSPACE = IF_SERVERSPACE || fs.existsSync('sumeru/server/run.js'); 30 | // var HOME = IF_SERVERSPACE ? CUR_DIR : path.join(CUR_DIR, 'sumeru/server'); 31 | // var LOGS_DIR = IF_SERVERSPACE ? path.join(CUR_DIR, 'logs') : path.join(CUR_DIR, 'sumeru/server/logs'); 32 | // var TMP_FILE = path.resolve(LOGS_DIR, 'tmp'); 33 | var KILL_CMD_LUX = 'kill -9 `ps -ef|grep node|awk \'{print $2}\'`'; 34 | var KILL_CMD_WIN = 'taskkill /im node.exe /f'; 35 | var MASTER_JSON; 36 | 37 | var NOWORKSPACE_ERROR = 'Please go to your sumeru/server directory to start the application.'; 38 | var COMMAND_ERROR = 'The command is error format.'; 39 | var FILEREAD_ERROR = 'Fail to read the file, please check if the application is started legally.'; 40 | var RUNDAEMON_INFO = 'Application run in daemon.\nStop the application use the command:sumeru stop.'; 41 | var CLOSEAPP_INFO = 'Closing the application......\nPlease wait......'; 42 | 43 | /** 44 | * Usage documentation. 45 | */ 46 | var usage = '' + '\n' + ' Usage: blendui [action] [option]\n' + '\n' + ' Options:\n' + ' init [path] create new application. \n'; 47 | 48 | /** 49 | * Parse command arguments. 50 | */ 51 | var args = process.argv.slice(2); 52 | 53 | (function() { 54 | var arg = args.shift(); 55 | switch(arg) { 56 | case undefined: 57 | case '--help': 58 | legalArgNum(0); 59 | abort(usage); 60 | break; 61 | case '--version': 62 | legalArgNum(0); 63 | abort(version); 64 | break; 65 | case 'init': 66 | legalArgNum(1); 67 | init(args[0]); 68 | break; 69 | default: 70 | abort(COMMAND_ERROR); 71 | break; 72 | } 73 | })(); 74 | 75 | /** 76 | * Init application at the given directory `path`. 77 | * 78 | * @param {String} path 79 | */ 80 | function init(path) { 81 | emptyDirectory(path, function(empty) { 82 | if(empty) { 83 | createApplicationAt(path); 84 | } else { 85 | confirm('destination is not empty, continue? (y/n)', function(ok) { 86 | if(ok) { 87 | process.stdin.destroy(); 88 | createApplicationAt(path); 89 | } else { 90 | abort('aborting'); 91 | } 92 | }); 93 | } 94 | }); 95 | }; 96 | 97 | /** 98 | * Create directory and files at the given directory `path`. 99 | * 100 | * @param {String} ph 101 | */ 102 | function createApplicationAt(ph, options) { 103 | 104 | options = options || {}; 105 | 106 | var name = path.basename(path.resolve(CUR_DIR, ph)); 107 | copyDir(path.join(__dirname, '../usecase'), ph); 108 | 109 | var repalcefile = []; 110 | for(var i = 0; i < repalcefile.length; i++) { 111 | var str = fs.readFileSync(repalcefile[i]).toString(); 112 | fs.writeFileSync(repalcefile[i], str.replace('$', name)); 113 | } 114 | 115 | if(!options['noFarewell']){ 116 | console.log(' \x1b[36mCongratulations! Your application has been succesfully initiated\x1b[0m'); 117 | } 118 | 119 | }; 120 | 121 | /** 122 | * Update application's sumeru fw at the given directory `path`. 123 | * 124 | * @param {String} path 125 | */ 126 | function update(path_) { 127 | emptyDirectory(path_, function(empty) { 128 | var resolvedPath = path.resolve(path_); 129 | if(empty) { 130 | abort(resolvedPath + ' is not a valid sumeru project space'); 131 | } else { 132 | confirm('update ' + resolvedPath + ' to use the latest sumeru after auto-backup, continue? (y/n)', function(ok) { 133 | if(ok) { 134 | process.stdin.destroy(); 135 | updateApplicationAt(path_); 136 | } else { 137 | abort('aborting'); 138 | } 139 | }); 140 | } 141 | }); 142 | }; 143 | 144 | 145 | 146 | 147 | /** 148 | * Terminal application. 149 | * 150 | * @param {String} signal stop/kill 151 | * 152 | */ 153 | function terminal(signal) { 154 | if(IF_WORKSPACE) { 155 | console.info(CLOSEAPP_INFO); 156 | //if(args[0] === '--force') { 157 | os.platform() === 'win32' ? exec(KILL_CMD_WIN):exec(KILL_CMD_LUX); 158 | process.exit(1); 159 | //} 160 | } else abort(NOWORKSPACE_ERROR); 161 | }; 162 | 163 | /** 164 | * Check if the given directory `path` is empty. 165 | * 166 | * @param {String} path 167 | * @param {Function} fn 168 | */ 169 | function emptyDirectory(path, fn) { 170 | fs.readdir(path, function(err, files) { 171 | if(err && 'ENOENT' != err.code) abort(FILEREAD_ERROR); 172 | fn(!files || !files.length); 173 | }); 174 | }; 175 | 176 | /** 177 | * Prompt confirmation with the given `msg`. 178 | * 179 | * @param {String} msg 180 | * @param {Function} fn 181 | */ 182 | function confirm(msg, fn) { 183 | prompt(msg, function(val) { 184 | fn(/^ *y(es)?/i.test(val)); 185 | }); 186 | }; 187 | 188 | /** 189 | * Prompt input with the given `msg` and callback `fn`. 190 | * 191 | * @param {String} msg 192 | * @param {Function} fn 193 | */ 194 | function prompt(msg, fn) { 195 | if(' ' == msg[msg.length - 1]) process.stdout.write(msg); 196 | else console.log(msg); 197 | process.stdin.setEncoding('ascii'); 198 | process.stdin.once('data', function(data) { 199 | fn(data); 200 | }).resume(); 201 | }; 202 | 203 | /** 204 | * Exit with the given `str`. 205 | * 206 | * @param {String} str 207 | */ 208 | function abort(str) { 209 | console.error(str); 210 | process.exit(1); 211 | }; 212 | 213 | /** 214 | * Check whether the number of arguments is legal. 215 | * 216 | * @param {Number} argNum 217 | */ 218 | function legalArgNum(argNum) { 219 | if(args.length != argNum) abort(COMMAND_ERROR); 220 | }; 221 | 222 | /** 223 | * Copy one file to project. 224 | * 225 | * @param {String} origin 226 | * @param {String} target 227 | */ 228 | function copyFile(origin, target) { 229 | if(!fs.existsSync(origin)) abort(origin + ' is not exist.'); 230 | if(!fs.existsSync(path.dirname(target))) { 231 | mkdir(path.dirname(target)); 232 | console.log(' \x1b[36mcreate\x1b[0m : ' + path.dirname(target)); 233 | } 234 | 235 | 236 | if(fs.statSync(origin).isFile()) { 237 | fs.writeFileSync(target, fs.readFileSync(origin, ''), ''); 238 | console.log(' \x1b[36mcreate\x1b[0m : ' + target); 239 | } 240 | 241 | }; 242 | 243 | /** 244 | * Copy template files to project. 245 | * 246 | * @param {String} origin 247 | * @param {String} target 248 | */ 249 | function copyDir(origin, target) { 250 | if (path.basename(origin) == 'backup') {return}; 251 | if(!fs.existsSync(origin)) abort(origin + ' is not exist.'); 252 | if(!fs.existsSync(target) && fs.statSync(origin).isDirectory()) { 253 | mkdir(target); 254 | console.log(' \x1b[36mcreate\x1b[0m : ' + target); 255 | } 256 | 257 | var datalist = fs.readdirSync(origin); 258 | 259 | for(var i = 0; i < datalist.length; i++) { 260 | var oCurrent = path.resolve(origin, datalist[i]); 261 | var tCurrent = path.resolve(target, datalist[i]); 262 | if(fs.statSync(oCurrent).isFile()) { 263 | fs.writeFileSync(tCurrent, fs.readFileSync(oCurrent, ''), ''); 264 | console.log(' \x1b[36mcreate\x1b[0m : ' + tCurrent); 265 | } else if(fs.statSync(oCurrent).isDirectory()) { 266 | copyDir(oCurrent, tCurrent); 267 | } 268 | } 269 | 270 | /* 271 | fs.readdir(origin, function(err, datalist) { 272 | if(err) abort(FILEREAD_ERROR); 273 | for(var i = 0; i < datalist.length; i++) { 274 | var oCurrent = path.resolve(origin, datalist[i]); 275 | var tCurrent = path.resolve(target, datalist[i]); 276 | if(fs.statSync(oCurrent).isFile()) { 277 | fs.writeFileSync(tCurrent, fs.readFileSync(oCurrent, ''), ''); 278 | console.log(' \x1b[36mcreate\x1b[0m : ' + tCurrent); 279 | } else if(fs.statSync(oCurrent).isDirectory()) copyDir(oCurrent, tCurrent); 280 | } 281 | });*/ 282 | }; 283 | 284 | /** 285 | * Mkdir -p. 286 | * 287 | * @param {String} path 288 | * @param {Function} fn 289 | */ 290 | function mkdir(path, fn) { 291 | if (!fs.existsSync(path)) { 292 | fs.mkdirSync(path); 293 | console.log(' \033[36mmkdir\033[0m : ' + path); 294 | } 295 | fn && fn(); 296 | } 297 | 298 | /** 299 | * get current time 300 | * 301 | **/ 302 | 303 | function time(){ 304 | var time = new Date(); 305 | 306 | return time.getDate() + '-' + (time.getMonth() - 0 + 1) + '-' + time.getFullYear() + '-' + time.valueOf(); 307 | } 308 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blendui", 3 | "description": "A Hybrid Javascript Framework For Mobile WebApp", 4 | "version": "0.0.6", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/Clouda-team/BlendUI.git" 8 | }, 9 | "bin": { 10 | "blendui": "./npm-tools/blendui" 11 | }, 12 | "author": "Clouda+ team", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "grunt": "~0.4.1", 16 | "grunt-contrib-jshint": "^0.10.0", 17 | "grunt-contrib-qunit": "~v0.5.2", 18 | "grunt-contrib-requirejs": "~0.4.0", 19 | "grunt-contrib-uglify": "~0.2.2", 20 | "grunt-contrib-watch": "^0.6.1", 21 | "gruntify-eslint": "^0.1.0" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /res/bdloading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clouda-team/BlendUI/dd7775c5b626c551c13e927e130a63de463ab05e/res/bdloading.png -------------------------------------------------------------------------------- /src/boost/layerTrigger.js: -------------------------------------------------------------------------------- 1 | define(["src/boost/sizzle", "src/boost/meta"], function (Sizzle, meta) { 2 | "use strict"; 3 | var LAYER_TRIGGER = "blend-layer-trigger"; 4 | var LAYER_BACK = "blend-layer-back"; 5 | var LAYER_FX = "blend-layer-fx"; 6 | 7 | function findParentByTagName(element, tagName) { 8 | tagName = tagName.toUpperCase(); 9 | while (element && element.nodeName != tagName) { 10 | element = element.parentNode; 11 | } 12 | return element; 13 | } 14 | 15 | function isDefaultPrevented(src) { 16 | return src.defaultPrevented ? src.defaultPrevented() : src.returnValue === false; 17 | } 18 | 19 | var layer; 20 | 21 | function openInLayer(url) { 22 | if (layer && layer.distory) { 23 | layer.distory(); 24 | } 25 | 26 | layer = new Blend.ui.Layer({ 27 | url: url, 28 | active: true, 29 | fx: meta.get(LAYER_FX, "slide") 30 | }); 31 | } 32 | 33 | var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g; 34 | 35 | function trim(text) { 36 | return text == null ? 37 | "" : 38 | (text + "").replace(rtrim, ""); 39 | } 40 | 41 | function isBackTrigger(element) { 42 | var selector = meta.get(LAYER_BACK); 43 | if (selector && Sizzle.matchesSelector(element, selector)) { 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | function isLayerTrigger(element) { 50 | var selector = meta.get(LAYER_TRIGGER); 51 | if (selector && Sizzle.matchesSelector(element, selector)) { 52 | return true; 53 | } 54 | return false; 55 | } 56 | 57 | function preventDefault(event) { 58 | event.preventDefault(); 59 | event.returnValue = false; 60 | } 61 | 62 | function layerTriggerHandler(event) { 63 | var target; 64 | var href; 65 | 66 | if (isDefaultPrevented(event)) { 67 | return; 68 | } 69 | 70 | target = findParentByTagName(event.target, "A"); 71 | 72 | if (!target || !target.hasAttribute("href")) { 73 | return; 74 | } 75 | 76 | href = trim(target.getAttribute("href")); 77 | 78 | //判断是否是回退的触发器 79 | if (isBackTrigger(target)) { 80 | preventDefault(event); 81 | Blend.ui.layerBack(); 82 | } 83 | //是否是Layer触发按键 84 | else if (isLayerTrigger(target)) { 85 | preventDefault(event); 86 | openInLayer(href); 87 | } 88 | } 89 | 90 | var inited = false; 91 | 92 | function init() { 93 | if (inited) { 94 | return; 95 | } 96 | inited = true; 97 | document.addEventListener("click", layerTriggerHandler, false); 98 | } 99 | 100 | return { 101 | init: init 102 | }; 103 | }); 104 | -------------------------------------------------------------------------------- /src/boost/main.js: -------------------------------------------------------------------------------- 1 | require(["src/boost/layerTrigger"], function (layerTrigger) { 2 | layerTrigger.init(); 3 | }, null, true); 4 | -------------------------------------------------------------------------------- /src/boost/meta.js: -------------------------------------------------------------------------------- 1 | define(function () { 2 | 3 | function feed(target) { 4 | var metaElements = document.getElementsByTagName("META"); 5 | var metaCount = metaElements.length; 6 | 7 | var collectAll; 8 | var index; 9 | var element; 10 | var name; 11 | var content; 12 | 13 | collectAll = false; 14 | if (!target) { 15 | target = {}; 16 | collectAll = true; 17 | } 18 | 19 | for (index = 0; index < metaCount; index++) { 20 | element = metaElements[index]; 21 | name = element.name; 22 | content = element.content; 23 | setCache(name, content); 24 | if (collectAll || target.hasOwnProperty(name)) { 25 | target[name] = content; 26 | } 27 | } 28 | 29 | cacheInited = true; 30 | return target; 31 | } 32 | 33 | var metaCache; 34 | var cacheInited; 35 | 36 | function setCache(name, content) { 37 | metaCache[name] = content; 38 | } 39 | 40 | function hasCache(name) { 41 | return metaCache.hasOwnProperty(name); 42 | } 43 | 44 | function getCache(name) { 45 | return metaCache[name]; 46 | } 47 | 48 | function clearCache() { 49 | metaCache = {}; 50 | cacheInited = false; 51 | } 52 | 53 | clearCache(); 54 | 55 | function get(name, defaultValue) { 56 | if (hasCache(name)) { 57 | return getCache(name); 58 | } 59 | 60 | if (cacheInited) { 61 | return defaultValue; 62 | } 63 | 64 | var obj = {}; 65 | obj[name] = defaultValue; 66 | feed(obj); 67 | 68 | return obj[name]; 69 | } 70 | 71 | return { 72 | feed: feed, 73 | setCache: setCache, 74 | hasCache: hasCache, 75 | getCache: getCache, 76 | clearCache: clearCache, 77 | get: get 78 | }; 79 | }); 80 | -------------------------------------------------------------------------------- /src/common/lib.js: -------------------------------------------------------------------------------- 1 | define( 2 | function(require) { 3 | 4 | 5 | /** 6 | * @class blend.lib 7 | * @singleton 8 | * @private 9 | */ 10 | var lib = {}; 11 | 12 | var lang = require('./lib/lang'); 13 | var string = require('./lib/string'); 14 | 15 | var count = 0x861005; 16 | 17 | /** 18 | * 获得全局唯一的ID 19 | * 20 | * @param {string} prefix 前缀 21 | */ 22 | lib.getUniqueID = function(prefix) { 23 | prefix = prefix || 'BlendUI'; 24 | return prefix + count++; 25 | }; 26 | 27 | lib.noop = function() {}; 28 | 29 | /** 30 | * 变同步为异步,0 delay 31 | * 32 | * @param {Object} fn function 33 | */ 34 | lib.offloadFn = function(fn) { 35 | setTimeout(fn || lib.noop, 0); 36 | }; 37 | 38 | Array.prototype.contains = function (search){ 39 | for(var i in this){ 40 | if(this[i]==search){ 41 | return true; 42 | } 43 | } 44 | return false; 45 | } 46 | 47 | /** 48 | * string相关的lib方法 49 | * 50 | * @class {Object} string 51 | */ 52 | /** 53 | * lang相关的lib方法 54 | * 55 | * @class {Object} lang 56 | */ 57 | lang.extend(lib, lang); 58 | lang.extend(lib, string); 59 | 60 | return lib; 61 | 62 | } 63 | ); 64 | -------------------------------------------------------------------------------- /src/common/lib/lang.js: -------------------------------------------------------------------------------- 1 | define( 2 | function(require) { 3 | 4 | var lang = {}; 5 | lang.inherits = function(subClass, superClass) { 6 | 7 | var Empty = function() {}; 8 | Empty.prototype = superClass.prototype; 9 | var selfPrototype = subClass.prototype; 10 | var proto = subClass.prototype = new Empty(); 11 | 12 | for (var key in selfPrototype) { 13 | proto[key] = selfPrototype[key];//可能出现引用传递的问题 14 | } 15 | subClass.prototype.constructor = subClass; 16 | subClass.superClass = superClass.prototype; 17 | 18 | return subClass; 19 | }; 20 | 21 | lang.clone = function(source) { 22 | if (!source || typeof source !== 'object') { 23 | return source; 24 | } 25 | 26 | var result = source; 27 | if (u.isArray(source)) { 28 | result = u.clone(source); 29 | } 30 | else if (({}).toString.call(source) === '[object Object]' && ('isPrototypeOf' in source)) { 31 | result = {}; 32 | for (var key in source) { 33 | if (source.hasOwnProperty(key)) { 34 | result[key] = lib.deepClone(source[key]); 35 | } 36 | } 37 | } 38 | 39 | return result; 40 | }; 41 | 42 | lang.extend = function(target, source) { 43 | for (var pro in source) { 44 | if (source.hasOwnProperty(pro) && typeof source[pro] != 'undefined') { 45 | target[pro] = source[pro]; 46 | } 47 | } 48 | return target; 49 | }; 50 | return lang; 51 | } 52 | ); 53 | -------------------------------------------------------------------------------- /src/common/lib/string.js: -------------------------------------------------------------------------------- 1 | define( 2 | function(require) { 3 | 4 | 5 | /** 6 | * @class blend.lib.string 7 | * @singleton 8 | * @private 9 | */ 10 | 11 | var string = {}; 12 | 13 | /** 14 | * 以「-」作为分隔符,将单词转换成首字母大写形式 15 | * 16 | * @param {string} str 17 | * @return {string} 18 | */ 19 | string.toPascal = function(str) { 20 | if (!str) { 21 | return ''; 22 | } 23 | return str.charAt(0).toUpperCase() + string.toCamel(str.slice(1)); 24 | }; 25 | 26 | /** 27 | * 以「-」作为分隔符,将单词转换成驼峰表示 28 | * 29 | * @param {string} str 30 | * @return {string} 31 | */ 32 | string.toCamel = function(str) { 33 | if (!str) { 34 | return ''; 35 | } 36 | 37 | return str.replace( 38 | /-([a-z])/g, 39 | function(s) { 40 | return s.toUpperCase(); 41 | } 42 | ); 43 | }; 44 | return string; 45 | } 46 | ); 47 | -------------------------------------------------------------------------------- /src/common/loader.js: -------------------------------------------------------------------------------- 1 | define( 2 | function(require) { 3 | 4 | /** 5 | * @class blend.loader 6 | * @singleton 7 | * @private 8 | */ 9 | 10 | //baidu-async-module, 11 | //这里实现两个方法 12 | //A. 加载页面源js 13 | //B. 页面内嵌js 14 | 15 | var loader = {}; 16 | 17 | var getScript = function(url,cb){ 18 | var script = document.createElement('script'); 19 | script.setAttribute('src', url); 20 | document.head.appendChild(script); 21 | script.onload = function(){ 22 | if(cb){cb(script);} 23 | }; 24 | }; 25 | 26 | 27 | loader.getScript = function(layerid,jsarr,callback){ 28 | var getscript = 0; 29 | for(var i = 0,len=jsarr.length;i="2.5"?true:false); 117 | 118 | } 119 | 120 | return SMSAuth; 121 | 122 | }); -------------------------------------------------------------------------------- /src/hybrid/api/component.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file component.js 3 | * @path hybrid/api/component.js 4 | * @desc 组件相关代码入口文件; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | var comp = {}; 11 | 12 | // 幻灯片组件 13 | comp.slider = require('./component/slider'); 14 | // footbar组件 15 | comp.footbar = require('./component/footbar'); 16 | // cascadingMenu 17 | comp.cascadingMenu = require('./component/cascadingMenu'); 18 | 19 | return comp; 20 | } 21 | ); 22 | -------------------------------------------------------------------------------- /src/hybrid/api/component/cascadingMenu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file cascadingMenu.js 3 | * @path hybrid/api/component/cascadingMenu.js 4 | * @desc native 级联菜单组件相关api; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class rutime.component.cascadingMenu 12 | * @singleton 13 | * @private 14 | */ 15 | var config = require('../config'); 16 | var event = require('../event'); 17 | var util = require('../util'); 18 | var cascadingMenu = {}; 19 | var devPR = config.DEVICE_PR; 20 | 21 | var filterOption = util.filterPositionOption; 22 | var apiFn = util.apiFn; 23 | 24 | // 增加footbar组件 25 | cascadingMenu.add = function(id, options) { 26 | var _options = { 27 | 'left': 0, 28 | 'top': 0, 29 | 'width': window.innerWidth * devPR, 30 | 'height':window.innerHeight/2 * devPR, 31 | 'fixed': true 32 | }; 33 | 34 | _options = filterOption(options, false, _options); 35 | 36 | apiFn('addComponent', [ 37 | id, 38 | 'UIBase', 39 | 'com.baidu.lappgui.blend.component.cascadingMenu.CascadingMenu', 40 | JSON.stringify(_options) 41 | ]); 42 | 43 | return cascadingMenu; 44 | }; 45 | 46 | // 设置菜单数据 47 | cascadingMenu.setMenu = function(id, data) { 48 | apiFn('componentExecuteNative', [ 49 | id, 50 | 'setMenu', 51 | JSON.stringify(data) 52 | ]); 53 | return cascadingMenu; 54 | }; 55 | 56 | // 配置信息 57 | cascadingMenu.setConfig = function(id,data){ 58 | apiFn('componentExecuteNative',[ 59 | id, 60 | 'setConfig', 61 | JSON.stringify(data) 62 | ]); 63 | return cascadingMenu; 64 | }; 65 | 66 | // 显示 67 | cascadingMenu.show = function(id, options) { 68 | options = options || {}; 69 | apiFn('componentExecuteNative', [ 70 | id, 71 | 'show', 72 | JSON.stringify(options) 73 | ]); 74 | return cascadingMenu; 75 | }; 76 | 77 | // 选择菜单项 78 | cascadingMenu.setItemSelected = function(id, options) { 79 | options = options || {}; 80 | apiFn('componentExecuteNative', [ 81 | id, 82 | 'setItemSelected', 83 | JSON.stringify(options) 84 | ]); 85 | return cascadingMenu; 86 | }; 87 | 88 | // 隐藏 89 | cascadingMenu.hide = function(id, options) { 90 | options = options || {}; 91 | apiFn('componentExecuteNative', [ 92 | id, 93 | 'hide', 94 | JSON.stringify(options) 95 | ]); 96 | return cascadingMenu; 97 | }; 98 | 99 | // 移除组件 100 | cascadingMenu.remove = function(id) { 101 | apiFn('removeComponent', [ 102 | id, 103 | 'UIBase' 104 | ]); 105 | }; 106 | 107 | // 事件扩展到footbar组件中 108 | cascadingMenu.on = event.on; 109 | 110 | cascadingMenu.off = event.off; 111 | 112 | return cascadingMenu; 113 | } 114 | ); 115 | -------------------------------------------------------------------------------- /src/hybrid/api/component/footbar.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file footbar.js 3 | * @path hybrid/api/component/footbar.js 4 | * @desc native footbar组件相关api; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class rutime.component.footbar 12 | * @singleton 13 | * @private 14 | */ 15 | var config = require('../config'); 16 | var event = require('../event'); 17 | var util = require('../util'); 18 | var footbar = {}; 19 | var devPR = config.DEVICE_PR; 20 | 21 | var filterOption = util.filterPositionOption; 22 | var apiFn = util.apiFn; 23 | 24 | // 增加footbar组件 25 | footbar.add = function(id, options) { 26 | var _options = { 27 | 'left': 0, 28 | 'top': (window.innerHeight - 45) * devPR, 29 | 'width': window.innerWidth * devPR, 30 | 'height': 45 * devPR, 31 | 'fixed': true 32 | 33 | }; 34 | 35 | _options = filterOption(options, false, _options); 36 | 37 | apiFn('addComponent', [ 38 | id, 39 | 'UIBase', 40 | 'com.baidu.lightui.component.toolbar.Toolbar', 41 | JSON.stringify(_options) 42 | ]); 43 | 44 | return footbar; 45 | }; 46 | 47 | // 设置组件菜单数据 48 | footbar.setMenu = function(id, data) { 49 | apiFn('componentExecuteNative', [ 50 | id, 51 | 'setMenu', 52 | JSON.stringify(data) 53 | ]); 54 | return footbar; 55 | }; 56 | 57 | // 设置组件菜单数据 58 | footbar.updateMenu = function(id, data) { 59 | apiFn('componentExecuteNative', [ 60 | id, 61 | 'updateMenu', 62 | JSON.stringify(data) 63 | ]); 64 | return footbar; 65 | }; 66 | 67 | // 显示 68 | footbar.show = function(id, options) { 69 | options = options || {}; 70 | apiFn('componentExecuteNative', [ 71 | id, 72 | 'show', 73 | JSON.stringify(options) 74 | ]); 75 | return footbar; 76 | }; 77 | 78 | // 隐藏 79 | footbar.hide = function(id, options) { 80 | options = options || {}; 81 | apiFn('componentExecuteNative', [ 82 | id, 83 | 'hide', 84 | JSON.stringify(options) 85 | ]); 86 | return footbar; 87 | }; 88 | 89 | // 移除组件 90 | footbar.remove = function(id) { 91 | apiFn('removeComponent', [ 92 | id, 93 | 'UIBase' 94 | ]); 95 | }; 96 | 97 | // 事件扩展到footbar组件中 98 | footbar.on = event.on; 99 | 100 | footbar.off = event.off; 101 | 102 | return footbar; 103 | } 104 | ); 105 | -------------------------------------------------------------------------------- /src/hybrid/api/component/slider.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file slider.js 3 | * @path hybrid/api/component/slider.js 4 | * @desc native slider组件相关api; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class rutime.component.slider 12 | * @singleton 13 | * @private 14 | */ 15 | var config = require('../config'); 16 | var event = require('../event'); 17 | var util = require('../util'); 18 | 19 | var slider = {}; 20 | var devPR = config.DEVICE_PR; 21 | 22 | var filterOption = util.filterPositionOption; 23 | var apiFn = util.apiFn; 24 | 25 | // 增加幻灯片组件到app中 26 | slider.add = function(id, options) { 27 | var _options = { 28 | 'left': 0, 29 | 'top': 0, 30 | 'width': window.innerWidth * devPR, 31 | 'height': window.innerHeight * devPR, 32 | 'fixed': false 33 | 34 | }; 35 | 36 | _options = filterOption(options, false, _options); 37 | 38 | _options.top += window.pageYOffset * devPR; 39 | apiFn('addComponent', [ 40 | id, 41 | 'UIBase', 42 | 'com.baidu.lightui.component.slider.Slider', 43 | JSON.stringify(_options) 44 | ]); 45 | 46 | return slider; 47 | }; 48 | 49 | // 增加幻灯片图片数据 50 | slider.addItems = function(id, images) { 51 | apiFn('componentExecuteNative', [ 52 | id, 53 | 'addItems', 54 | JSON.stringify(images) 55 | ]); 56 | return slider; 57 | }; 58 | // 设置背景 59 | slider.setConfig = function(id, options) { 60 | apiFn('componentExecuteNative', [ 61 | id, 62 | 'setSliderConfig', 63 | JSON.stringify(options) 64 | ]); 65 | return slider; 66 | }; 67 | 68 | // 设置指示器 69 | slider.setupIndicator = function(id, options) { 70 | // alert(JSON.stringify(options)); 71 | options.layoutRules = [ 72 | config.CENTER_HORIZONTAL, 73 | config.ALIGN_PARENT_BOTTOM 74 | ]; 75 | options.verticalMargin = Math.round((options.verticalMargin || 5) * devPR); 76 | options.unitSize = Math.round((options.unitSize || 10) * devPR); 77 | options.unitSpace = Math.round((options.unitSpace || 5) * devPR); 78 | apiFn('componentExecuteNative', [ 79 | id, 80 | 'setupIndicator', 81 | JSON.stringify(options) 82 | ]); 83 | return slider; 84 | }; 85 | 86 | // 滑动到后一个索引 87 | slider.next = function(id) { 88 | apiFn('componentExecuteNative', [ 89 | id, 90 | 'next', 91 | '' 92 | ]); 93 | return slider; 94 | }; 95 | 96 | // 滑动到前一个索引 97 | slider.prev = function(id) { 98 | apiFn('componentExecuteNative', [ 99 | id, 100 | 'prev', 101 | '' 102 | ]); 103 | return slider; 104 | }; 105 | 106 | // 滑动到指定索引 107 | slider.slideTo = function(id, index, hasAnim) { 108 | apiFn('componentExecuteNative', [ 109 | id, 110 | 'slideTo', 111 | JSON.stringify({ 112 | index: index, 113 | isAnim: !!hasAnim 114 | 115 | }) 116 | ]); 117 | return slider; 118 | }; 119 | 120 | // 移除组件 121 | slider.remove = function(id) { 122 | apiFn('removeComponent', [ 123 | id, 124 | 'UIBase' 125 | ]); 126 | }; 127 | 128 | // 事件扩展到footbar组件中 129 | slider.on = event.on; 130 | 131 | slider.off = event.off; 132 | 133 | return slider; 134 | } 135 | ); 136 | -------------------------------------------------------------------------------- /src/hybrid/api/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file config.js 3 | * @path hybrid/api/config.js 4 | * @desc naitve组件相关设置文件 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class LayoutRule 12 | * 用于控制组件内元素的相对定位, 13 | * 对应的值不应被修改 14 | */ 15 | var config = { 16 | 17 | /** 18 | * Rule that aligns the child's left edge with its RelativeLayout 19 | * parent's left edge. 20 | */ 21 | ALIGN_PARENT_LEFT: 9, 22 | 23 | /** 24 | * Rule that aligns the child's top edge with its RelativeLayout 25 | * parent's top edge. 26 | */ 27 | ALIGN_PARENT_TOP: 10, 28 | 29 | /** 30 | * Rule that aligns the child's right edge with its RelativeLayout 31 | * parent's right edge. 32 | */ 33 | ALIGN_PARENT_RIGHT: 11, 34 | 35 | /** 36 | * Rule that aligns the child's bottom edge with its RelativeLayout 37 | * parent's bottom edge. 38 | */ 39 | ALIGN_PARENT_BOTTOM: 12, 40 | 41 | /** 42 | * Rule that centers the child with respect to the bounds of its 43 | * RelativeLayout parent. 44 | */ 45 | CENTER_IN_PARENT: 13, 46 | 47 | /** 48 | * Rule that centers the child horizontally with respect to the 49 | * bounds of its RelativeLayout parent. 50 | */ 51 | CENTER_HORIZONTAL: 14, 52 | 53 | /** 54 | * Rule that centers the child vertically with respect to the 55 | * bounds of its RelativeLayout parent. 56 | */ 57 | CENTER_VERTICAL: 15, 58 | 59 | IOS: /iP(ad|hone|od)/i.test(navigator.userAgent), 60 | 61 | /** 62 | * devicePixelRatio 63 | */ 64 | DEVICE_PR: (/iP(ad|hone|od)/.test(navigator.userAgent)) ? 1 : (window.devicePixelRatio || 2) 65 | 66 | }; 67 | 68 | return config; 69 | } 70 | ); 71 | -------------------------------------------------------------------------------- /src/hybrid/api/core.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file core.js 3 | * @path hybrid/api/core.js 4 | * @desc native核心接口api; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class blend.api.core 12 | * @blendui native核心接口层 13 | * @private 14 | */ 15 | var util = require('./util'); 16 | // dialog作为核心接口引入blend 17 | var dialog = require('./dialog'); 18 | 19 | var apiFn = util.apiFn; 20 | var core = {}; 21 | 22 | // 标志是否已经创建了键盘组件; 23 | var keyboard; 24 | 25 | /** 26 | * 移除启动画面 27 | */ 28 | core.removeSplashScreen = function() { 29 | apiFn('removeSplashScreen', arguments); 30 | }; 31 | 32 | /** 33 | * 退出app应用 34 | */ 35 | core.exitApp = function() { 36 | apiFn('exitApp', arguments); 37 | }; 38 | 39 | /** 40 | * 启动app应用 41 | */ 42 | core.launchApp = function() { 43 | apiFn('launchLightApp', arguments); 44 | }; 45 | 46 | /** 47 | * 显示/ 隐藏键盘 48 | * @param {boolean} boolShow 显示 or 隐藏 49 | */ 50 | core.keyboard = function(boolShow) { 51 | if (!keyboard) { 52 | apiFn('addComponent', [ 53 | 'KEYBOARD', 54 | 'UIBase', 55 | 'com.baidu.lightui.component.keyboard.KeyboardHelper', 56 | '{"left":0,"top":0,"width":1,"height":1,"fixed":false}' 57 | ]); 58 | keyboard = true; 59 | } 60 | var isShow = boolShow ? 'show' : 'hide'; 61 | apiFn('componentExecuteNative', [ 62 | 'KEYBOARD', 63 | isShow, 64 | '{}' 65 | ]); 66 | }; 67 | 68 | /** 69 | * dialog对话框组件直接引入core 70 | */ 71 | core.dialog = dialog; 72 | 73 | return core; 74 | } 75 | ); 76 | -------------------------------------------------------------------------------- /src/hybrid/api/dialog.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file dialog.js 3 | * @path hybrid/api/dialog.js 4 | * @desc native dialog相关api接口; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class blend.api.core 12 | * @blendui native核心接口层 13 | * @private 14 | */ 15 | var util = require('./util'); 16 | var layer = require('./layer'); 17 | var event = require('./event'); 18 | 19 | var apiFn = util.apiFn; 20 | 21 | var dialog = { 22 | alert: function(options, callback) { 23 | var title = options.title || ''; 24 | var message = options.msg || ''; 25 | var button = options.button || '确定'; 26 | var layerId = options.layerId || layer.getCurrentId(); 27 | var alertId = options.alertId || (1 * new Date() + ''); 28 | if (callback) { 29 | event.once('showAlert', callback, layerId); 30 | } 31 | apiFn('showAlert', [ 32 | layerId, 33 | alertId, 34 | message, 35 | title, 36 | button 37 | ]); 38 | }, 39 | prompt: function(options, callback) { 40 | var title = options.title || ''; 41 | var message = options.msg || ''; 42 | var buttons = JSON.stringify(options.buttons || [ 43 | '确定', 44 | '取消' 45 | ]); 46 | var layerId = options.layerId || layer.getCurrentId(); 47 | var promptId = options.promptId || (1 * new Date() + ''); 48 | var defaultText = options.defaultText || ''; 49 | if (callback) { 50 | event.once('showPrompt', callback, layerId); 51 | } 52 | apiFn('showPrompt', [ 53 | layerId, 54 | promptId, 55 | message, 56 | title, 57 | buttons, 58 | defaultText 59 | ]); 60 | }, 61 | confirm: function(options, callback) { 62 | var title = options.title || ''; 63 | var message = options.msg || ''; 64 | var buttons = JSON.stringify(options.buttons || [ 65 | '确定', 66 | '取消' 67 | ]); 68 | var layerId = options.layerId || layer.getCurrentId(); 69 | var promptId = options.promptId || (1 * new Date() + ''); 70 | if (callback) { 71 | event.once('showConfirm', callback, layerId); 72 | } 73 | apiFn('showConfirmDialog', [ 74 | layerId, 75 | promptId, 76 | message, 77 | title, 78 | buttons 79 | ]); 80 | }, 81 | toast: function(options) { 82 | var message = options.msg || ''; 83 | var layerId = options.layerId || layer.getCurrentId(); 84 | var promptId = options.promptId || (1 * new Date() + ''); 85 | var duration = options.duration || 0; 86 | apiFn('showToast', [ 87 | layerId, 88 | promptId, 89 | message, 90 | duration 91 | ]); 92 | } 93 | 94 | }; 95 | 96 | return dialog; 97 | } 98 | ); 99 | -------------------------------------------------------------------------------- /src/hybrid/api/event.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file event.js 3 | * @path hybrid/api/event.js 4 | * @desc native所有组件传递事件通过此封装成on off fire; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | /** 10 | * @class event 11 | * @singleton 12 | * @private 13 | */ 14 | var event = {}; 15 | 16 | // 原生事件; 17 | var _type = [ 18 | // layer创建成功 19 | 'layerCreateSuccess', 20 | // layer 页面载入成功 21 | 'layerLoadFinish', 22 | // 下拉刷新loading 23 | 'layerPullDown', 24 | // layer返回事件 25 | 'layerPoped', 26 | // slider点击 sliderTap 27 | 'sliderTap', 28 | // slider 滑动切换 sliderSlide 29 | 'sliderSlide', 30 | // 菜单建事件 31 | 'menuPressed', 32 | // layer中返回键goBack回调 33 | 'layerGoBack', 34 | // 返回键退出事件 35 | 'backPressedBeforeExit', 36 | // footbar点中toolbarMenuSelected 37 | 'toolbarMenuSelected', 38 | 'softKeyboardShow', 39 | 'softKeyboardHide', 40 | 'showAlert', 41 | 'showPrompt', 42 | 'showConfirm', 43 | 'cascadingMenuSelected' 44 | ]; 45 | 46 | var handlers = {}; 47 | var jsonParseFliter = function(key, val) { 48 | if (val && val.indexOf && val.indexOf('function') >= 0) { 49 | return new Function('return ' + val)(); 50 | } 51 | return val; 52 | }; 53 | 54 | event.on = function(type, handler, id, context, isonce) { 55 | var me = this; 56 | id = id || (this.getCurrentId && this.getCurrentId()) || 'empty'; 57 | context = context || this; 58 | if (handlers[type]) { 59 | var i = 0; 60 | var listeners = handlers[type]['listener']; 61 | var len = listeners.length; 62 | for (; i < len; i++) { 63 | if (listeners[i].id === id 64 | && listeners[i].callback === handler 65 | && listeners[i].context === context) { 66 | break; 67 | } 68 | } 69 | if (i === len) { 70 | handlers[type]['listener'].push({ 71 | id: id, 72 | context: context, 73 | callback: handler 74 | 75 | }); 76 | } 77 | if (!handlers[type]['listened']) { 78 | document.addEventListener(type, handlers[type].callback, false); 79 | handlers[type]['listened'] = true; 80 | } 81 | } 82 | else { 83 | // console.log('不支持此事件'); 84 | handlers[type] = {}; 85 | handlers[type]['listener'] = []; 86 | if (_type.indexOf(type) < 0) { 87 | handlers[type]['callback'] = function(event) { 88 | var parseData = JSON.parse(decodeURIComponent(event.data), jsonParseFliter); 89 | var callback; 90 | var listeners = handlers[type]['listener']; 91 | event.origin = event['sender'] || parseData.origin; 92 | event.data = parseData.data; 93 | event.detail = event.origin; 94 | event.reciever = event.target = parseData.target; 95 | callback = function(data) { 96 | me.fire(parseData.callEvent, event.origin, data); 97 | }; 98 | for (var i = 0, len = listeners.length; i < len; i++) { 99 | if (parseData.callEvent) { 100 | event.callback = callback; 101 | } 102 | listeners[i].callback.call(listeners[i].context, event, listeners[i].id); 103 | } 104 | isonce && me.off(type); 105 | }; 106 | } 107 | else { 108 | handlers[type]['callback'] = function(event) { 109 | var listeners = handlers[type]['listener']; 110 | for (var i = 0, len = listeners.length; i < len; i++) { 111 | if (listeners[i].id === event['origin']) { 112 | event.detail = event.origin; 113 | listeners[i].callback.call(listeners[i].context, event, listeners[i].id); 114 | } 115 | } 116 | isonce && me.off(type); 117 | }; 118 | } 119 | this.on(type, handler, id, context); 120 | } 121 | }; 122 | event.off = function(type, handler, id, context) { 123 | id = id || (this.getCurrentId && this.getCurrentId()) || 'empty'; 124 | context = context || this; 125 | if (handlers[type]) { 126 | if (!handler) { 127 | document.removeEventListener(type, handlers[type].callback); 128 | handlers[type]['listened'] = false; 129 | handlers[type]['listener'] = []; 130 | } 131 | else { 132 | var i = 0; 133 | var listeners = handlers[type]['listener']; 134 | var isAll = handler === 'all'; 135 | var len = listeners.length; 136 | 137 | for (; i < len; i++) { 138 | if (listeners[i].id === id 139 | && listeners[i].context === context 140 | && (isAll || listeners[i].callback === handler)) { 141 | listeners.splice && listeners.splice(i, 1); 142 | break; 143 | } 144 | } 145 | if (listeners.length === 0 && handlers[type]['listened']) { 146 | document.removeEventListener(type, handlers[type].callback); 147 | handlers[type]['listened'] = false; 148 | } 149 | } 150 | } 151 | else { 152 | window.console && window.console.log('无此事件绑定'); 153 | } 154 | }; 155 | 156 | event.once = function(type, handler, id, context) { 157 | this.on(type, handler, id, context, 'isonce'); 158 | }; 159 | 160 | return event; 161 | } 162 | ); 163 | -------------------------------------------------------------------------------- /src/hybrid/api/layer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file layer.js 3 | * @path hybrid/api/layer.js 4 | * @desc native layer相关api; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class blend.layerApi 12 | * @singleton 13 | * @private 14 | */ 15 | var event = require('./event'); 16 | var util = require('./util'); 17 | var layer = {}; 18 | var initLayerId = '0'; 19 | var getBasePath = util.getBasePath; 20 | 21 | var stringifyFilter = util.stringifyFilter; 22 | 23 | var filterOption = util.filterPositionOption; 24 | 25 | var apiFn = util.apiFn; 26 | 27 | /** 28 | * 创建独立layer 29 | * @param {string} layerId 创建的layer的id,app全局唯一 30 | * @param {Object} options Layer的参数 31 | * @param {string} options.url 页面url 32 | * @param {boolean} options.pullToRefresh 是否可以上拉刷新 33 | * @return {string} layerId 34 | * @private 35 | */ 36 | layer.prepare = function(layerId, options) { 37 | // subLayer 38 | var layerOptions = filterOption(options); 39 | if (layerOptions.url) { 40 | layerOptions.url = getBasePath(layerOptions.url); 41 | } 42 | if(layerOptions.sliderLayer){ 43 | layerOptions.width = (layerOptions.width||(window.innerWidth*1.5))+""; 44 | } 45 | 46 | apiFn('prepareLayer', [ 47 | layerId, 48 | JSON.stringify(layerOptions) 49 | ]); 50 | return layerId; 51 | }; 52 | 53 | /** 54 | * 激活创建的layer 55 | * @param {string} layerId 页面layerId 56 | * @param {Object} options 页面出现参数 57 | * slow 500,normal 300, quick 100 58 | * @private 59 | */ 60 | layer.resume = function(layerId, options) { 61 | if (layer.isActive(layerId)) { 62 | return; 63 | } 64 | var _options = { 65 | 'fx': 'slide', 66 | 'reverse': false, 67 | 'duration': 300, 68 | 'cover': false 69 | }; 70 | var replaceString = { 71 | 'slow': 500, 72 | 'normal': 300, 73 | 'quick': 100 74 | }; 75 | _options = filterOption(options, false, _options); 76 | if (replaceString[_options['duration']]) { 77 | _options['duration'] = replaceString[_options['duration']]; 78 | } 79 | apiFn('resumeLayer', [ 80 | layerId, 81 | JSON.stringify(_options) 82 | ]); 83 | setTimeout(function() { 84 | layer.canGoBack(layerId) && layer.clearHistory(layerId); 85 | }, 500); 86 | layer.fire('in', false, layerId); 87 | }; 88 | 89 | /** 90 | * 退出激活的layer 91 | * @param {string} layerId 退出后返回到的layerId 92 | */ 93 | layer.back = function(layerId) { 94 | layerId = layerId || ''; 95 | apiFn('backToPreviousLayer', [ 96 | layerId 97 | ]); 98 | }; 99 | 100 | /** 101 | * 隐藏sublayer 102 | * @param {string} layerId 要隐藏的subLayer Id 103 | */ 104 | layer.hideSubLayer = function(layerId) { 105 | layerId = layerId || ''; 106 | apiFn('hideSubLayer', [ 107 | layerId 108 | ]); 109 | }; 110 | 111 | /** 112 | * 刷新独立打开的layer 113 | * @param {string} [layerId] 要刷新的layerId 114 | * @param {string} [url] 要刷新的页面url 115 | * @return {string} layerId 116 | * @private 117 | */ 118 | layer.reload = function(layerId, url) { 119 | if (arguments.length === 1 || arguments.length === 0) { 120 | url = layerId; 121 | layerId = layer.getCurrentId(); 122 | } 123 | if (!url) { 124 | url = layer.getCurrentUrl(); 125 | layer.replaceUrl(layerId, url); 126 | } 127 | else { 128 | url = getBasePath(url); 129 | apiFn('layerLoadUrl', [ 130 | layerId, 131 | url 132 | ]); 133 | } 134 | return layerId; 135 | }; 136 | 137 | layer.replaceUrl = function(layerId, url) { 138 | layer.fire('replace', layerId, url); 139 | return layerId; 140 | }; 141 | 142 | layer.destroy = function(layerId) { 143 | layerId = layerId || layer.getCurrentId(); 144 | apiFn('destroyLayer', [ 145 | layerId 146 | ]); 147 | // layer.fire('layerDestroyEvent',false,layerId); 148 | return layerId; 149 | }; 150 | 151 | layer.setPullRefresh = function(layerId, isCan, options) { 152 | layerId = layerId || layer.getCurrentId(); 153 | options = JSON.stringify(options); 154 | apiFn('layerSetPullRefresh', [ 155 | layerId, 156 | isCan, 157 | options 158 | ]); 159 | }; 160 | 161 | layer.stopPullRefresh = function(layerId) { 162 | layerId = layerId || layer.getCurrentId(); 163 | apiFn('layerStopRefresh', [ 164 | layerId 165 | ]); 166 | return layerId; 167 | }; 168 | 169 | /** 170 | * 检测layerId是否存在, 171 | * @param {string} layerId 检测layerId 172 | * @return {boolean} 是否存在 173 | */ 174 | layer.isAvailable = function(layerId) { 175 | return apiFn('isLayerAvailable', arguments); 176 | }; 177 | 178 | /** 179 | * 当前页面所在的layer id 180 | * @return {string} layerId 181 | */ 182 | layer.getCurrentId = function() { 183 | return apiFn('currentLayerId', arguments); 184 | }; 185 | 186 | /** 187 | * 当前页面的url 188 | * @return {string} url 189 | */ 190 | layer.getCurrentUrl = function() { 191 | return apiFn('currentLayerUrl', arguments); 192 | }; 193 | 194 | layer.stopLoading = function(layerId) { 195 | layerId = layerId || layer.getCurrentId(); 196 | return apiFn('layerStopLoading', [ 197 | layerId 198 | ]); 199 | }; 200 | 201 | layer.on = event.on; 202 | layer.once = event.once; 203 | 204 | 205 | layer.off = event.off; 206 | 207 | 208 | layer.fire = function(type, targetId, message, callback) { 209 | if (!targetId) { 210 | targetId = ''; 211 | } 212 | else if (targetId === 'top') { 213 | targetId = initLayerId; 214 | } 215 | var sender = layer.getCurrentId(); 216 | var messData = {}; 217 | messData.data = message || ''; 218 | messData.target = targetId; 219 | messData.origin = sender; 220 | if (callback) { 221 | messData.callEvent = 'call_' + sender + '_' + (1 * new Date()); 222 | var handler = function(event) { 223 | callback(event['data']); 224 | layer.off(messData.callEvent); 225 | }; 226 | layer.on(messData.callEvent, handler); 227 | } 228 | 229 | apiFn('layerPostMessage', [ 230 | sender, 231 | targetId, 232 | type, 233 | encodeURIComponent(JSON.stringify(messData, stringifyFilter)) 234 | ]); 235 | }; 236 | 237 | layer.postMessage = function(message, targetId, callback) { 238 | layer.fire('message', targetId, message, callback); 239 | }; 240 | 241 | 242 | // 获取layer原始url 243 | layer.getOriginalUrl = function(layerId) { 244 | layerId = layerId || layer.getCurrentId(); 245 | return apiFn('layerGetOriginalUrl', [ 246 | layerId 247 | ]); 248 | }; 249 | 250 | // 获取layer当前url 251 | layer.getUrl = function(layerId) { 252 | layerId = layerId || layer.getCurrentId(); 253 | return apiFn('layerGetUrl', [ 254 | layerId 255 | ]); 256 | }; 257 | 258 | // 当前url是否可以回退 259 | layer.canGoBack = function(layerId) { 260 | layerId = layerId || layer.getCurrentId(); 261 | return apiFn('layerCanGoBack', [ 262 | layerId 263 | ]); 264 | }; 265 | 266 | // 当前url是否可以回退或者前进 267 | layer.canGoBackOrForward = function(steps, layerId) { 268 | layerId = layerId || layer.getCurrentId(); 269 | return apiFn('layerCanGoBackOrForward', [ 270 | layerId, 271 | steps || 1 272 | ]); 273 | }; 274 | 275 | /** 276 | * 当前layer是否处于激活状态 277 | * @param {string} [layerId] 要测试的layerId 278 | * @return {boolean} native返回值 279 | */ 280 | layer.isActive = function(layerId) { 281 | layerId = layerId || layer.getCurrentId(); 282 | return apiFn('isLayerActive', [ 283 | layerId 284 | ]); 285 | }; 286 | 287 | /** 288 | * 清除layer history历史 289 | * @param {string} [layerId] 要清除的layerId 290 | * @return {Object} native返回码 291 | */ 292 | layer.clearHistory = function(layerId) { 293 | layerId = layerId || layer.getCurrentId(); 294 | return apiFn('layerClearHistory', [ 295 | layerId 296 | ]); 297 | }; 298 | // showSlider 299 | layer.showSlider = function(layerId) { 300 | layerId = layerId || layer.getCurrentId(); 301 | return apiFn('showSlider', [ 302 | layerId 303 | ]); 304 | }; 305 | 306 | // hideSlider 307 | layer.hideSlider = function(layerId) { 308 | layerId = layerId || layer.getCurrentId(); 309 | return apiFn('hideSlider', [ 310 | layerId 311 | ]); 312 | }; 313 | 314 | // 设置subLayer的大小 315 | layer.setLayout = function(layerId, options) { 316 | var _options = filterOption(options); 317 | return apiFn('layerSetLayout', [ 318 | layerId, 319 | JSON.stringify(_options) 320 | ]); 321 | }; 322 | 323 | return layer; 324 | } 325 | ); 326 | -------------------------------------------------------------------------------- /src/hybrid/api/layerGroup.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file layerGroup.js 3 | * @path hybrid/api/layerGroup.js 4 | * @desc native layerGroup相关api; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class blend.Api.layer 12 | * @singleton 13 | * @private 14 | */ 15 | var layer = require('./layer'); 16 | var util = require('./util'); 17 | 18 | var layerGroup = {}; 19 | 20 | var getBasePath = util.getBasePath; 21 | 22 | var filterOption = util.filterPositionOption; 23 | 24 | var apiFn = util.apiFn; 25 | 26 | /** 27 | * 通知runtime创建pagerGroup,成功回掉返回 runtime句柄winid 28 | * 29 | * @param {string} groupId id 30 | * @param {Array} layers 本地或网络url链接组成的Array 31 | * @param {Object} options pager数组 32 | * @return {string} groupId 33 | * @private 34 | */ 35 | layerGroup.create = function(groupId, layers, options) { 36 | var layerInfo = { 37 | id: groupId, 38 | infos: layers 39 | 40 | }; 41 | layers.forEach(function(n, i) { 42 | n.url = getBasePath(n.url); 43 | }); 44 | 45 | if (options.active) { 46 | layerInfo.active = options.active; 47 | } 48 | 49 | var groupOptions = filterOption(options, [ 50 | 'active' 51 | ]); 52 | 53 | apiFn('addLayerGroup', [ 54 | JSON.stringify(layerInfo), 55 | JSON.stringify(groupOptions) 56 | ]); 57 | return groupId; 58 | }; 59 | 60 | // 激活GroupId下面的对应的layerId 61 | layerGroup.showLayer = function(groupId, layerId) { 62 | apiFn('showLayerInGroup', arguments); 63 | return groupId; 64 | }; 65 | 66 | // 在group中增加layer 67 | layerGroup.addLayer = function(groupId, options) { 68 | apiFn('addLayerInGroup', [ 69 | groupId, 70 | JSON.stringify(options) 71 | ]); 72 | // @todo return 73 | return groupId; 74 | }; 75 | 76 | // 在group中删除layer 77 | layerGroup.removeLayer = function(groupId, layerId) { 78 | apiFn('removeLayerInGroup', arguments); 79 | // @todo return 80 | return groupId; 81 | }; 82 | 83 | // 在group中更新layer 84 | layerGroup.updateLayer = function(groupId, layerOptions) { 85 | apiFn('updateLayerInGroup', arguments); 86 | // @todo return 87 | return groupId; 88 | }; 89 | 90 | layerGroup.toggleScroll = function(layerId, groupId) { 91 | if (arguments.length === 1) { 92 | groupId = layerId; 93 | layerId = layer.getCurrentId(); 94 | } 95 | layerGroup.setScroll(layerId, groupId, !layerGroup.isScroll(layerId, groupId)); 96 | }; 97 | 98 | layerGroup.isScroll = function(layerId, groupId) { 99 | if (arguments.length === 1) { 100 | groupId = layerId; 101 | layerId = layer.getCurrentId(); 102 | } 103 | return apiFn('canLayerGroupScroll', [ 104 | layerId, 105 | groupId 106 | ]); 107 | }; 108 | 109 | layerGroup.setScroll = function(layerId, groupId, isCan) { 110 | if (arguments.length === 2) { 111 | isCan = groupId; 112 | groupId = layerId; 113 | layerId = layer.getCurrentId(); 114 | } 115 | setTimeout(function() { 116 | apiFn('setCanLayerGroupScroll', [ 117 | layerId, 118 | groupId, 119 | isCan 120 | ]); 121 | }, 100); 122 | }; 123 | 124 | layerGroup.removeLayerGroup = function(groupId) { 125 | apiFn('removeLayerGroup', arguments); 126 | }; 127 | 128 | layerGroup.hideLayerGroup = function(groupId) { 129 | apiFn('hideLayerGroup', arguments); 130 | }; 131 | 132 | layerGroup.showLayerGroup = function(groupId) { 133 | apiFn('showLayerGroup', arguments); 134 | }; 135 | 136 | layerGroup.layerGroupSetLayout = function(groupId, options) { 137 | var _options = filterOption(options); 138 | return apiFn('layerGroupSetLayout', [ 139 | groupId, 140 | JSON.stringify(_options) 141 | ]); 142 | }; 143 | 144 | return layerGroup; 145 | } 146 | ); 147 | -------------------------------------------------------------------------------- /src/hybrid/api/uix.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file uix.js 3 | * @path hybrid/api/uix.js 4 | * @desc native uix相关api;@todo remove 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class blend.api.core 12 | * @blendui native核心接口层 13 | * @private 14 | */ 15 | var util = require('./util'); 16 | var event = require('./event'); 17 | 18 | var apiFn = util.apiFn; 19 | 20 | var uix = {}; 21 | 22 | uix.setData = function(options) { 23 | apiFn('UIXSetData', arguments); 24 | }; 25 | 26 | uix.exeMethod = function(id, method, args) { 27 | apiFn('UIXExe', arguments); 28 | }; 29 | 30 | var _clickHandlers = false; 31 | uix.on = function(name, handler) { 32 | if (!_clickHandlers) { 33 | _clickHandlers = {}; 34 | _clickHandlers[name] = handler; 35 | event.on('UIXClick',function(e) { 36 | var originData = JSON.parse(decodeURIComponent(event.data)); 37 | var _name = originData && originData.name; 38 | if (_clickHandlers[_name]) { 39 | _clickHandlers[_name](originData.data); 40 | } 41 | }); 42 | } 43 | else { 44 | _clickHandlers[name] = handler; 45 | } 46 | }; 47 | 48 | return uix; 49 | } 50 | ); 51 | -------------------------------------------------------------------------------- /src/hybrid/api/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file util.js 3 | * @path hybrid/api/util.js 4 | * @desc 工具类函数集合; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | var config = require('./config'); 10 | var devPR = config.DEVICE_PR; 11 | 12 | var util = {}; 13 | 14 | util.getBasePath = function(link) { 15 | var a = document.createElement('a'); 16 | a.href = link; 17 | return a.href; 18 | }; 19 | 20 | util.stringifyFilter = function(key, val) { 21 | if (typeof val === 'function') { 22 | return val.toString(); 23 | } 24 | return val; 25 | }; 26 | 27 | util.filterPositionOption = function(options, delKeys, defaultOptions) { 28 | var layerOut = [ 29 | 'left', 30 | 'top', 31 | 'width', 32 | 'height', 33 | 'right', 34 | 'bottom' 35 | ]; 36 | var _options = defaultOptions || {}; 37 | for (var n in options) { 38 | if (options[n] === undefined 39 | || (delKeys && delKeys.indexOf(n) >= 0)) { 40 | continue; 41 | } 42 | _options[n] = layerOut.indexOf(n) >= 0 ? options[n] * devPR : options[n]; 43 | } 44 | return _options; 45 | }; 46 | 47 | util.apiFn = function(handler, args) { 48 | try { 49 | var api = window.nuwa_core || window.nuwa_runtime; 50 | var api2 = window.nuwa_widget || window.lc_bridge; 51 | var fn; 52 | var value; 53 | if (api2 && (fn = api2[handler])) { 54 | api = api2; 55 | } 56 | else { 57 | fn = api[handler]; 58 | } 59 | value = fn.apply(api, args); 60 | // android 4.4 true false返回为字符串 61 | if (value === 'true') { 62 | value = true; 63 | } 64 | else if (value === 'false') { 65 | value = false; 66 | } 67 | return value; 68 | } 69 | catch (e) { 70 | window.console.log('BlendUI_Api_Error:' + handler + '======' + fn); 71 | window.console.log(e); 72 | } 73 | }; 74 | 75 | return util; 76 | } 77 | ); 78 | -------------------------------------------------------------------------------- /src/hybrid/blend.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file blend.js 3 | * @path hybrid/blend.js 4 | * @desc blendui 全局命名空间 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | var lib = require('../common/lib'); 10 | var runtime = require('./runtime'); 11 | 12 | /** 13 | * @class blend 14 | * @singleton 15 | */ 16 | var blend = {}; 17 | var controls = {}; 18 | 19 | // 有些android手机只执行一次runtimeready 20 | document.addEventListener('runtimeready', function() { 21 | blend.readyState = true; 22 | },false); 23 | 24 | /** 25 | * 版本信息 26 | * 27 | * @property {string} version info 28 | */ 29 | blend.version = '0.2'; 30 | 31 | /** 32 | * 开放的Api接口类,后期不对外开发 33 | * 34 | * @property {object} Api接口 35 | */ 36 | blend.api = {}; 37 | 38 | /** 39 | * 注册控件到系统中 40 | * @param {Control} control 控件实例 41 | */ 42 | blend.register = function(control) { 43 | if (controls[control.id]) { 44 | throw (control.type || '') + 'New Object Already Exists'; 45 | } 46 | else { 47 | controls[control.id] = control; 48 | } 49 | }; 50 | 51 | /** 52 | * 注销控件 53 | * @param {Control} control 控件实例 54 | */ 55 | blend.cancel = function(control) { 56 | delete controls[control.id]; 57 | }; 58 | 59 | /** 60 | * 根据id获取实例 61 | * 62 | * @param {string} id 控件id 63 | * @return {Control} control类 64 | */ 65 | blend.get = function(id) { 66 | return controls[id]; 67 | }; 68 | 69 | /** 70 | * runtime ready事件,是对native runtimeready事件的封装 71 | * @param {Function} callback ready之后触发函数 72 | */ 73 | blend.ready = function(callback) { 74 | var outTimeFun; 75 | var handler = function() { 76 | outTimeFun && clearTimeout(outTimeFun); 77 | if (/complete|loaded|interactive/i.test(document.readyState)) { 78 | callback(blend); 79 | } 80 | else { 81 | document.addEventListener('DOMContentLoaded', function() { 82 | callback(blend); 83 | }, false); 84 | } 85 | document.removeEventListener('runtimeready', handler); 86 | }; 87 | if (blend.readyState || window.nuwa_runtime || window.lc_bridge) { 88 | handler(); 89 | } 90 | else { 91 | outTimeFun = setTimeout(handler, 200000); 92 | document.addEventListener('runtimeready', handler, false); 93 | } 94 | }; 95 | 96 | /** 97 | * runtime layer接口 98 | * @todo remove 99 | * @property {Object} 100 | */ 101 | blend.api.layer = runtime.layer; 102 | 103 | /** 104 | * runtime layerGroup接口 105 | * @todo remove 106 | * @property {Object} layerGroup 107 | */ 108 | blend.api.layerGroup = runtime.layerGroup; 109 | 110 | /** 111 | * runtime core接口 112 | * @todo remove 113 | * @property {Object} core 114 | */ 115 | blend.api.core = runtime.core; 116 | 117 | // 把layer的事件触发绑定到blend上快捷使用 118 | ['on', 'off', 'fire', 'once', 'postMessage'].forEach(function(n, i) { 119 | blend[n] = function() { 120 | runtime.layer[n].apply(runtime.layer,arguments); 121 | }; 122 | }); 123 | 124 | // 把coreapi和layer上可直接操作的接口直接暴露给blend; 125 | lib.extend(blend,runtime.core); 126 | lib.extend(blend, { 127 | 'layerStopRefresh': runtime.layer.stopPullRefresh, 128 | 'layerBack': runtime.layer.back, 129 | 'layerStopLoading': runtime.layer.stopLoading, 130 | 'getLayerId': runtime.layer.getCurrentId, 131 | 'layerSetPullRefresh': runtime.layer.setPullRefresh 132 | }); 133 | 134 | // 触发函数 135 | var mainCall = {}; 136 | 137 | blend.layerInit = function(id, callback) { 138 | mainCall[id] = callback; 139 | }; 140 | 141 | // 私有方法 供其他文件调用; 142 | blend._lanch = function(id, dom) { 143 | mainCall[id] && mainCall[id].call(blend, dom); 144 | }; 145 | 146 | // unload的时候注销所有组件,可销毁native相应组件释放内存; 147 | window.addEventListener('unload', function(e) { 148 | var i; 149 | for (i in controls) { 150 | if (controls.hasOwnProperty(i)) { 151 | controls[i].destroy && controls[i].destroy(); 152 | } 153 | } 154 | }); 155 | 156 | return blend; 157 | } 158 | ); 159 | -------------------------------------------------------------------------------- /src/hybrid/delegateLayer.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file delegateLayer.js 3 | * @path hybrid/delegateLayer.js 4 | * @desc 通过插件的形式为blend添加delegate,使其在底层layer上集中创建layer和 5 | * 使用layer方法,通过消息传递在集中创建layer,为JSON.stringify和 6 | * parse增加filter支持函数传递 7 | * @author clouda-team(https://github.com/clouda-team) 8 | */ 9 | define( 10 | function(require) { 11 | var blend = require('./blend'); 12 | var LayerClass = require('./Layer'); 13 | var protos = new LayerClass(); 14 | var delegateMethod = 'delegateMethod'; 15 | var delegateCreate = 'delegateCreate'; 16 | var DelegateLayer = function(id) { 17 | this.id = id || blend.getLayerId(); 18 | }; 19 | // 必须等ready后触发; 20 | blend.ready(function() { 21 | var layerId = blend.getLayerId() + ''; 22 | if (layerId === '0') { 23 | // 触发函数 24 | blend.on(delegateMethod, function(e) { 25 | var data = e.data; 26 | var method = data.method; 27 | var args = data.args; 28 | var id = data.id; 29 | blend.get(id)[method].apply(blend.get(id), args); 30 | }); 31 | // 创建layer 32 | blend.on(delegateCreate, function(e) { 33 | new LayerClass(e.data); 34 | }); 35 | } 36 | }); 37 | // 循环查找各个属性和函数; 38 | for (var i in protos) { 39 | // 方法可以通过delegate进行操作,属性不能直接获取 40 | DelegateLayer.prototype[i] = (function(attr) { 41 | var fn; 42 | if (typeof protos[attr] === 'function') { 43 | fn = function() { 44 | var me = this; 45 | blend.fire(delegateMethod, '0', { 46 | id: me.id, 47 | args: arguments, 48 | method: attr 49 | 50 | }); 51 | }; 52 | } 53 | else { 54 | fn = function() { 55 | console.log('delegate error'); 56 | }; 57 | } 58 | return fn; 59 | })(i); 60 | } 61 | 62 | blend.getLayer = function(id) { 63 | var layer = blend.get(id); 64 | if (layer) { 65 | return layer; 66 | } 67 | return new DelegateLayer(id); 68 | }; 69 | 70 | blend.createLayer = function(options) { 71 | var layer; 72 | if (blend.getLayerId() === '0') { 73 | layer = new LayerClass(options); 74 | } 75 | else { 76 | blend.fire(delegateCreate, '0', options); 77 | layer = blend.getLayer(options.id); 78 | } 79 | return layer; 80 | }; 81 | } 82 | ); 83 | -------------------------------------------------------------------------------- /src/hybrid/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file main.js 3 | * @path hybrid/main.js 4 | * @desc requirejs合并头文件; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | require([ 8 | 'src/hybrid/blend', 9 | 'src/hybrid/Layer', 10 | 'src/hybrid/LayerGroup', 11 | 'src/hybrid/Slider', 12 | 'src/hybrid/Footbar', 13 | 'src/hybrid/CascadingMenu' 14 | ], function(blend, Layer, LayerGroup, Slider, Footbar, CascadingMenu) { 15 | blend = blend || {}; 16 | blend.Layer = Layer; 17 | blend.LayerGroup = LayerGroup; 18 | blend.Slider = Slider; 19 | blend.Footbar = Footbar; 20 | blend.CascadingMenu = CascadingMenu; 21 | 22 | // 初始化命名空间; 23 | window.Blend = window.Blend || {}; 24 | window.Blend.ui = blend; 25 | window.console && window.console.log('====BlendUI Ok======'); 26 | // 自定义blendready事件 27 | var _event; 28 | if (window.CustomEvent) { 29 | _event = new window.CustomEvent('blendready', { 30 | bubbles: false, 31 | cancelable: false 32 | }); 33 | } 34 | else { 35 | _event = document.createEvent('Event'); 36 | _event.initEvent('blendready', false, false); 37 | } 38 | blend.ready(function() { 39 | document.dispatchEvent(_event); 40 | blend._lanch(blend.getLayerId(), document.querySelector('body')); 41 | }); 42 | }, null, true); 43 | -------------------------------------------------------------------------------- /src/hybrid/runtime.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file runtime.js 3 | * @path hybrid/runtime.js 4 | * @desc native相关代码入口文件; 5 | * @author clouda-team(https://github.com/clouda-team) 6 | */ 7 | define( 8 | function(require) { 9 | 10 | /** 11 | * @class runtime 12 | * @private 13 | * @singleton 14 | */ 15 | var runtime = {}; 16 | var core = require('./api/core'); 17 | var layer = require('./api/layer'); 18 | var layerGroup = require('./api/layerGroup'); 19 | var component = require('./api/component'); 20 | 21 | runtime.core = core; 22 | runtime.layer = layer; 23 | runtime.layerGroup = layerGroup; 24 | runtime.component = component; 25 | 26 | return runtime; 27 | } 28 | ); 29 | -------------------------------------------------------------------------------- /src/web/Actions.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * @class Popup 5 | * @extends Dialog 6 | * @inheritable 7 | */ 8 | 9 | function(require) { 10 | 11 | var blend = require('./blend'); 12 | var lib = require('./../common/lib'); 13 | var Dialog = require('./Dialog'); 14 | var Control = require('./Control'); 15 | 16 | 17 | 18 | /** 19 | * @constructor 20 | * 21 | * @param {Array} options 22 | * 23 | * @param {Array} [options[0]] Groups 24 | * @param {Array} [options] buttons 25 | * @param {String} [options[0][0].text] 26 | * @param {Boolean} [options[0][0].red] 27 | * @param {Boolean} [options[0][0].bold] 28 | * @param {Function} [options[0][0].onclick] 29 | * @param {Boolean} [options[0][0].close] 30 | * @param {Boolean} [options[0][0].label] 31 | * 32 | * @return this 33 | */ 34 | 35 | var Actions = function(options) { 36 | var me = this; 37 | options = options || []; 38 | 39 | 40 | if (options.length > 0 && !$.isArray(options[0])) { 41 | options = [options]; 42 | } 43 | 44 | //CAUTION: 这里不继承dialog的构造函数,自己构造 45 | Control.call(this, options); 46 | 47 | var actionsTemplate = '
{{buttons}}
'; 48 | var buttonsHTML = ''; 49 | for (var i = 0; i < options.length; i++) { 50 | for (var j = 0; j < options[i].length; j++) { 51 | if (j === 0) { 52 | buttonsHTML += '
'; 53 | } 54 | var button = options[i][j]; 55 | var buttonClass = button.label ? 'actions-modal-label' : 'actions-modal-button'; 56 | if (button.bold) buttonClass += ' actions-modal-button-bold'; 57 | if (button.red) buttonClass += ' actions-modal-button-red'; 58 | buttonsHTML += '' + button.text + ''; 59 | if (j === options[i].length - 1) { 60 | buttonsHTML += '
'; 61 | } 62 | } 63 | } 64 | 65 | this.main.innerHTML = actionsTemplate.replace(/{{buttons}}/g, buttonsHTML); 66 | 67 | var modal = $(this.main).children(); 68 | 69 | $('body').append(this.main); 70 | 71 | var groups = modal.find('.actions-modal-group'); 72 | groups.each(function(index, el) { 73 | var groupIndex = index; 74 | $(el).children().each(function(index, el) { 75 | var buttonParams = options[groupIndex][index]; 76 | if ($(el).hasClass('actions-modal-button')) { 77 | $(el).on('click', function(e) { 78 | if (buttonParams.close !== false) { 79 | me.close(); 80 | } 81 | if (buttonParams.onClick) { 82 | buttonParams.onClick(modal, e); 83 | } 84 | }); 85 | } 86 | }); 87 | }); 88 | this.open(); 89 | return modal[0]; 90 | }; 91 | lib.inherits(Actions, Dialog); 92 | 93 | 94 | return Actions; 95 | } 96 | ); 97 | -------------------------------------------------------------------------------- /src/web/Dialog.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * @class Dialog 5 | * @inheritable 6 | */ 7 | 8 | function(require) { 9 | 10 | var blend = require('./blend'); 11 | var lib = require('./../common/lib'); 12 | var Control = require('./Control'); 13 | //var underscroe = require('../../third_party/underscore.js'); 14 | 15 | /** 16 | * @constructor 17 | * 18 | * @param {Object} options 19 | * 20 | * @param {String} options.title 21 | * @param {String} [options.content] 22 | * @param {String} [options.afterContent] 23 | * 24 | * @param {Object} [options.buttons] 25 | * @param {String} [options.buttons.text] 26 | * @param {Boolean} [options.buttons.bold] 27 | * @param {Function} [options.buttons.onclick] 28 | * @param {Boolean} [options.buttons.close] 29 | * 30 | * 31 | * @return this 32 | */ 33 | var Dialog = function(options) { 34 | console.log('dialog init'); 35 | Control.call(this, options); 36 | this._init(); 37 | return this; 38 | }; 39 | lib.inherits(Dialog, Control); 40 | Dialog.prototype.type = 'dialog'; 41 | Dialog.prototype.content = ''; 42 | Dialog.prototype.title = ''; 43 | Dialog.prototype.afterContent = ''; 44 | 45 | 46 | Dialog.prototype._init = function() { 47 | 48 | var me = this; 49 | 50 | var buttonsHTML = ''; 51 | if (this.buttons && this.buttons.length > 0) { 52 | for (var i = 0; i < this.buttons.length; i++) { 53 | buttonsHTML += '' + this.buttons[i].text + ''; 54 | } 55 | } 56 | var template = ''; 64 | if (!this.title) { 65 | template = template.split('{{if title}}')[0] + template.split('{{/if title}}')[1]; 66 | } else { 67 | template = template.replace(/{{if\ title}}/g, '').replace(/{{\/if\ title}}/g, ''); 68 | } 69 | var html = template 70 | .replace(/{{title}}/g, this.title) 71 | .replace(/{{content}}/g, this.content) 72 | .replace(/{{afterContent}}/g, this.afterContent) 73 | .replace(/{{buttons}}/g, buttonsHTML) 74 | .replace(/{{noButtons}}/g, !this.buttons || this.buttons.length === 0 ? 'modal-no-buttons' : ''); 75 | 76 | this.main.innerHTML = html; 77 | 78 | $('body').append(this.main); 79 | 80 | $(this.main).find('.modal-button').each(function(index, el) { 81 | $(el).on('click', _.bind(function(e) { 82 | if (this.buttons[index].close !== false) { 83 | this.close(); 84 | } 85 | if (this.buttons[index].onclick) { 86 | this.buttons[index].onclick.call(this, e); 87 | } 88 | }, me)); 89 | }); 90 | this.open(); 91 | return this; 92 | }; 93 | Dialog.prototype.open = function() { 94 | Dialog.__open($(this.main).children()); 95 | }; 96 | Dialog.__open = function(main) { 97 | main = $(main); 98 | if ($('.modal-overlay').length === 0) { 99 | var overlay = document.createElement('div'); 100 | overlay.className = 'modal-overlay'; 101 | $('body').append(overlay); 102 | } 103 | var isPopover = main.hasClass('popover'); 104 | var isPopup = main.hasClass('popup'); 105 | if (!isPopover && !isPopup) { 106 | main.css({marginTop: -main.outerHeight() / 2 + 'px'}); 107 | } 108 | 109 | var clientLeft = main[0].clientLeft;//触发浏览器repaint 110 | 111 | main.trigger('open'); 112 | 113 | // Classes for transition in 114 | $('.modal-overlay').addClass('modal-overlay-visible'); 115 | 116 | main.removeClass('modal-out').addClass('modal-in').transitionEnd(function(e) { 117 | if (main.hasClass('modal-out')) { 118 | main.trigger('closed'); 119 | } else { 120 | main.trigger('opened'); 121 | } 122 | }); 123 | return true; 124 | }; 125 | Dialog.prototype.close = function() { 126 | return Dialog.__close($(this.main).children()); 127 | }; 128 | Dialog.__close = function(main) { 129 | main = $(main) || '.modal-in'; 130 | 131 | $('.modal-overlay').removeClass('modal-overlay-visible'); 132 | main.trigger('close'); 133 | var isPopup = main.hasClass('popup'); 134 | var removeOnClose = main.hasClass('remove-on-close'); 135 | if (!main.hasClass('popover')) { 136 | main.removeClass('modal-in').addClass('modal-out').transitionEnd(function(e) { 137 | if (main.hasClass('modal-out')) { 138 | main.trigger('closed'); 139 | } else { 140 | main.trigger('opened'); 141 | } 142 | if (!isPopup) { 143 | main.remove(); 144 | } 145 | if (isPopup) { 146 | main.removeClass('modal-out').hide(); 147 | } 148 | if (removeOnClose) { 149 | main.remove(); 150 | } 151 | }); 152 | } else { 153 | main.removeClass('modal-in modal-out').trigger('closed').hide(); 154 | if (removeOnClose) { 155 | main.remove(); 156 | } 157 | } 158 | return true; 159 | }; 160 | return Dialog; 161 | } 162 | ); 163 | -------------------------------------------------------------------------------- /src/web/Navbar.js: -------------------------------------------------------------------------------- 1 | define( 2 | function(require) { 3 | var lib = require('./../common/lib'); 4 | var Component = require('./Component'); 5 | var blend = require('./blend'); 6 | 7 | 8 | 9 | var Navbar = function(options) { 10 | 11 | Component.apply(this, arguments); 12 | this.init(options); 13 | 14 | console.log('navbar done'); 15 | }; 16 | 17 | Navbar.prototype.type = 'navbar'; 18 | 19 | 20 | Navbar.prototype.init = function(options) { 21 | options = options ? options : {}; 22 | 23 | $(options.dom).hide(); 24 | 25 | if (blend.inRuntime()) { 26 | var nativeObj = runtime.createNavbar(options); 27 | nativeObj.bindEvent('ontouch', this.ontouchHandler); 28 | 29 | this.main = nativeObj; 30 | }else { 31 | this.render(); 32 | } 33 | return this.main; 34 | }; 35 | 36 | Navbar.prototype.paint = function() { 37 | for (var i = 0, len = this.navbar.length; i < len; i++) { 38 | this.add(this.navbar[i]); 39 | } 40 | 41 | console.log((this.main)); 42 | lib.$(this.main).appendTo('body'); 43 | 44 | lib.$(this.main).css({ 45 | 'position' : 'fixed', 46 | 'top': this.top, 47 | 'background-color': 'red' 48 | }); 49 | //todo:这里的return是不是要跟上面的nativeObj统一? 50 | }; 51 | 52 | Navbar.prototype.add = function(item) { 53 | console.log('add'); 54 | 55 | if (blend.inRuntime()) { 56 | this.nativeObj.add(item); 57 | }else { 58 | var iconString = item.icon ? "" : ''; 59 | var id = lib.getUniqueID(); 60 | 61 | 62 | $('') 63 | .attr({'id': 'test-btn' + id, 'class': 'ui-btn-active', href: item.url}) 64 | .css({ 65 | 'display' : 'inline-block', 66 | 'border' : '1px #000 solid', 67 | 'padding' : 2, 68 | 'width' : item.width ? item.width : '' 69 | }) 70 | .html(iconString + item.text) 71 | .appendTo(this.main); 72 | } 73 | }; 74 | 75 | Navbar.prototype.remove = function(id) { 76 | this.main.remove(id); 77 | }; 78 | 79 | Navbar.prototype.ontouchHandler = function() { 80 | 81 | }; 82 | 83 | Navbar.prototype.bindEvent = function() { 84 | 85 | 86 | }; 87 | Navbar.prototype.remove = function() { 88 | 89 | 90 | }; 91 | 92 | lib.inherits(Navbar, Component); 93 | 94 | return Navbar; 95 | } 96 | ); 97 | -------------------------------------------------------------------------------- /src/web/Popover.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * @class Popup 5 | * @extends Dialog 6 | * @inheritable 7 | */ 8 | 9 | function(require) { 10 | 11 | var blend = require('./blend'); 12 | var lib = require('./../common/lib'); 13 | var Dialog = require('./Dialog'); 14 | var Control = require('./Control'); 15 | 16 | 17 | var Popover = function(modal, target, removeOnClose) { 18 | //CAUTION: 这里不继承dialog的构造函数,自己构造 19 | Control.call(this, { 20 | modal: modal, 21 | target: target, 22 | removeOnClose: removeOnClose 23 | }); 24 | 25 | if (typeof this.modal === 'string' && this.modal.indexOf('<') >= 0) { 26 | var _modal = document.createElement('div'); 27 | _modal.innerHTML = this.modal; 28 | if (_modal.childNodes.length > 0) { 29 | modal = _modal.childNodes[0]; 30 | if (this.removeOnClose) { 31 | this.modal.classList.add('remove-on-close'); 32 | } 33 | $('body').append(this.modal); 34 | } else { 35 | return false; 36 | } 37 | } 38 | modal = $(modal); 39 | target = $(target); 40 | if (modal.length === 0 || target.length === 0) { 41 | return false; 42 | } 43 | 44 | $(this.main).append(modal).appendTo('body'); 45 | 46 | 47 | if (modal.find('.popover-angle').length === 0) { 48 | modal.append('
'); 49 | } 50 | modal.show(); 51 | 52 | this._resizeHandler(); 53 | 54 | $(window).on('resize', _.bind(this._resizeHandler, this)); 55 | modal.on('close', function() { 56 | $(window).off('resize', _.bind(this._resizeHandler, this)); 57 | }); 58 | 59 | this.open(); 60 | return this; 61 | }; 62 | 63 | lib.inherits(Popover, Dialog); 64 | 65 | Popover.prototype.removeOnClose = true; 66 | Popover.prototype.type = 'popover'; 67 | 68 | 69 | 70 | Popover.prototype._resizeHandler = function() { 71 | var modal = $(this.modal); 72 | var target = $(this.target); 73 | 74 | 75 | modal.css({left: '', top: ''}); 76 | var modalWidth = modal.width(); 77 | var modalHeight = modal.height(); // 13 - height of angle 78 | var modalAngle = modal.find('.popover-angle'); 79 | var modalAngleSize = modalAngle.width() / 2; 80 | modalAngle.removeClass('on-left on-right on-top on-bottom').css({left: '', top: ''}); 81 | 82 | var targetWidth = target.outerWidth(); 83 | var targetHeight = target.outerHeight(); 84 | var targetOffset = target.offset(); 85 | var targetParentPage = target.parents('.page'); 86 | if (targetParentPage.length > 0) { 87 | targetOffset.top = targetOffset.top - targetParentPage[0].scrollTop; 88 | } 89 | 90 | var windowHeight = $(window).height(); 91 | var windowWidth = $(window).width(); 92 | 93 | var modalTop = 0; 94 | var modalLeft = 0; 95 | var diff = 0; 96 | // Top Position 97 | var modalPosition = 'top'; 98 | 99 | if ((modalHeight + modalAngleSize) < targetOffset.top) { 100 | // On top 101 | modalTop = targetOffset.top - modalHeight - modalAngleSize; 102 | } 103 | else if ((modalHeight + modalAngleSize) < windowHeight - targetOffset.top - targetHeight) { 104 | // On bottom 105 | modalPosition = 'bottom'; 106 | modalTop = targetOffset.top + targetHeight + modalAngleSize; 107 | } 108 | else { 109 | // On middle 110 | modalPosition = 'middle'; 111 | modalTop = targetHeight / 2 + targetOffset.top - modalHeight / 2; 112 | diff = modalTop; 113 | if (modalTop < 0) { 114 | modalTop = 5; 115 | } 116 | else if (modalTop + modalHeight > windowHeight) { 117 | modalTop = windowHeight - modalHeight - 5; 118 | } 119 | diff = diff - modalTop; 120 | } 121 | // Horizontal Position 122 | if (modalPosition === 'top' || modalPosition === 'bottom') { 123 | modalLeft = targetWidth / 2 + targetOffset.left - modalWidth / 2; 124 | diff = modalLeft; 125 | if (modalLeft < 5) modalLeft = 5; 126 | if (modalLeft + modalWidth > windowWidth) modalLeft = windowWidth - modalWidth - 5; 127 | if (modalPosition === 'top') modalAngle.addClass('on-bottom'); 128 | if (modalPosition === 'bottom') modalAngle.addClass('on-top'); 129 | diff = diff - modalLeft; 130 | modalAngle.css({left: (modalWidth / 2 - modalAngleSize + diff) + 'px'}); 131 | } 132 | else if (modalPosition === 'middle') { 133 | modalLeft = targetOffset.left - modalWidth - modalAngleSize; 134 | modalAngle.addClass('on-right'); 135 | if (modalLeft < 5) { 136 | modalLeft = targetOffset.left + targetWidth + modalAngleSize; 137 | modalAngle.removeClass('on-right').addClass('on-left'); 138 | } 139 | if (modalLeft + modalWidth > windowWidth) { 140 | modalLeft = windowWidth - modalWidth - 5; 141 | modalAngle.removeClass('on-right').addClass('on-left'); 142 | } 143 | modalAngle.css({top: (modalHeight / 2 - modalAngleSize + diff) + 'px'}); 144 | } 145 | 146 | // Apply Styles 147 | modal.css({top: modalTop + 'px', left: modalLeft + 'px'}); 148 | }; 149 | 150 | return Popover; 151 | } 152 | ); 153 | -------------------------------------------------------------------------------- /src/web/Popup.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * @class Popup 5 | * @extends Dialog 6 | * @inheritable 7 | */ 8 | 9 | function(require) { 10 | 11 | var blend = require('./blend'); 12 | var lib = require('./../common/lib'); 13 | var Dialog = require('./Dialog'); 14 | var Control = require('./Control'); 15 | 16 | 17 | var Popup = function(modal, removeOnClose) { 18 | console.log('popup init'); 19 | 20 | //CAUTION: 这里不继承dialog的构造函数,自己构造 21 | Control.call(this, { 22 | modal: modal, 23 | removeOnClose: removeOnClose 24 | }); 25 | 26 | this._init(); 27 | return this; 28 | }; 29 | 30 | lib.inherits(Popup, Dialog); 31 | 32 | Popup.prototype.removeOnClose = true; 33 | Popup.prototype.type = 'popup'; 34 | 35 | Popup.prototype._init = function() { 36 | if (typeof this.modal === 'string' && this.modal.indexOf('<') >= 0) {//如果是HTML 37 | var _modal = document.createElement('div'); 38 | _modal.innerHTML = this.modal; 39 | if (_modal.childNodes.length > 0) { 40 | modal = _modal.childNodes[0]; 41 | if (this.removeOnClose) { 42 | modal.classList.add('remove-on-close'); 43 | } 44 | $('body').append(modal); 45 | } else { 46 | return false; 47 | } 48 | } 49 | var modal = $(this.modal); 50 | if (modal.length === 0) { 51 | return false; 52 | } 53 | $(this.main).append(modal).appendTo('body'); 54 | 55 | modal.show(); 56 | // if (modal.find('.' + blend.configs.layerClass).length > 0) { 57 | // //app.sizeNavbars(modal.find('.' + app.params.viewClass)[0]); 58 | // } 59 | this.open(); 60 | return this; 61 | }; 62 | 63 | return Popup; 64 | } 65 | ); 66 | -------------------------------------------------------------------------------- /src/web/WebControl.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | 4 | function(require) { 5 | var blend = require('./blend'); 6 | var lib = require('./../common/lib'); 7 | var Control = require('./Control'); 8 | var events = require('./events'); 9 | 10 | 11 | /** 12 | * todo:这个WebControl不能合并到Control中么? 13 | * @class blend.webControl 14 | * @extends Control 15 | * @static 16 | */ 17 | 18 | function WebControl(options) { 19 | 20 | Control.apply(this, arguments); 21 | } 22 | 23 | 24 | //重新实现 25 | 26 | WebControl.prototype.type = 'layout'; 27 | 28 | // var myevents = []; 29 | 30 | //覆盖control的方法 31 | //js事件 32 | //处理内部事宜,也可用来指定dom事件 33 | //@params id for runtime use,useless for web 34 | WebControl.prototype.on = events.on; 35 | // function(type, callback, id, context) { 36 | // if (typeof context === 'undefined') { 37 | // context = blend.get(id || this.id).main; 38 | // } 39 | 40 | // //继承父类的on事件 FIXME 父类此方法会引起多重绑定的bug 41 | // // Control.prototype.on(type, callback,(id||this.id) , context); 42 | 43 | // //细化 web端 事件的处理 44 | // //事件on 45 | 46 | // if (typeof callback === 'function') { 47 | // context.addEventListener(type, callback, false); 48 | // } 49 | // // myevents.push([type, callback]); 50 | 51 | // }; 52 | //监听一次 53 | WebControl.prototype.once = events.once; 54 | // function(type, callback, id, context) { 55 | // if (typeof context === 'undefined') { 56 | // // context = this.main; 57 | // context = blend.get(id || this.id).main; 58 | // } 59 | 60 | // //继承父类的on事件 FIXME 父类此方法会引起多重绑定的bug 61 | // // Control.prototype.on(type, callback,(id||this.id) , context); 62 | 63 | // //细化 web端 事件的处理 64 | // //事件on 65 | 66 | // if (typeof callback === 'function') { 67 | // var cb = function() { 68 | // callback.apply(context, arguments); 69 | // context.removeEventListener(type, cb, false); 70 | // }; 71 | // context.addEventListener(type, cb, false); 72 | // } 73 | 74 | // }; 75 | //@params id for runtime use,useless for web 76 | WebControl.prototype.off = events.off; 77 | // function(type, callback, id, context) { 78 | // if (typeof context === 'undefined') { 79 | // // context = this.main; 80 | // context = blend.get(id || this.id).main; 81 | // } 82 | // //继承父类的on事件 83 | // // Control.prototype.off(type, callback,(id||this.id) , context); 84 | 85 | // //细化 web端 事件的处理 86 | // //事件off 87 | 88 | // if (typeof callback === 'function') { 89 | // context.removeEventListener(type, callback, false); 90 | // } 91 | // }; 92 | 93 | WebControl.prototype.fire = events.fire; 94 | // function(type, argAry, context) { 95 | // //继承父类的fire 事件 96 | // // Control.prototype.fire(type, argAry, context); 97 | 98 | // //细化 web端 事件的处理 99 | // //事件 fire,事件可以冒泡 100 | // try { 101 | // var e; 102 | // if (!argAry)argAry = this.id; 103 | 104 | // if (typeof context === 'undefined') { 105 | // // context = this.main; 106 | // context = blend.get(argAry).main; 107 | // } 108 | // if (typeof CustomEvent !== 'undefined') { 109 | // var opt = { 110 | // bubbles: true, 111 | // cancelable: true, 112 | // detail: argAry 113 | // }; 114 | // e = new CustomEvent(type, opt); 115 | // console.log(type, opt); 116 | // } else { 117 | // e = document.createEvent('CustomEvent'); 118 | // e.initCustomEvent(type, true, true, argAry); 119 | // } 120 | // if (context) { 121 | // (context).dispatchEvent(e); 122 | // } 123 | 124 | 125 | // } catch (ex) { 126 | // console.warn('Touch.js is not supported by environment.', ex.stack); 127 | // } 128 | 129 | // }; 130 | 131 | WebControl.prototype.animationEnd = function(callback) { 132 | var events = ['webkitAnimationEnd', 'OAnimationEnd', 'MSAnimationEnd', 'animationend'], 133 | i, j, me = this; 134 | function fireCallBack(e) { 135 | callback(e); 136 | for (i = 0; i < events.length; i++) { 137 | me.off(events[i], fireCallBack); 138 | } 139 | } 140 | if (callback) { 141 | for (i = 0; i < events.length; i++) { 142 | me.on(events[i], fireCallBack); 143 | } 144 | } 145 | return this; 146 | }; 147 | WebControl.prototype.transitionEnd = function(callback) { 148 | var events = ['webkitTransitionEnd'], 149 | i, j, me = this; 150 | function fireCallBack(e) { 151 | callback(e); 152 | for (i = 0; i < events.length; i++) { 153 | me.off(events[i], fireCallBack); 154 | } 155 | } 156 | if (callback) { 157 | for (i = 0; i < events.length; i++) { 158 | me.on(events[i], fireCallBack); 159 | } 160 | } 161 | return this; 162 | }; 163 | 164 | lib.inherits(WebControl, Control); 165 | 166 | return WebControl; 167 | } 168 | ); -------------------------------------------------------------------------------- /src/web/api.js: -------------------------------------------------------------------------------- 1 | define( 2 | function(require) { 3 | 4 | var api = {}; 5 | var noop = function(){}; 6 | 7 | // var events = require('./events'); 8 | // var layerapi = require('./layer/layerapi'); 9 | // var blend = require('./blend'); 10 | 11 | // api.core={}; 12 | // api.core.removeSplashScreen = noop; 13 | 14 | // api.layer = {}; 15 | // api.layer.on = events.on; 16 | // api.layer.off = events.off; 17 | // api.layer.fire = events.fire; 18 | // api.layer.once = events.once; 19 | 20 | api.layerStopRefresh = function(id){ 21 | if (!id) {//默认使用active layer的id 22 | id = Blend.ui.activeLayer.attr("data-blend-id") || '0'; 23 | } 24 | Blend.ui.get(id).endPullRefresh(); 25 | // layerapi.endPullRefresh(Blend.ui.get(id)); 26 | }; 27 | 28 | api.layerBack = function(id){ 29 | if (!id) {//默认使用active layer的id 30 | id = Blend.ui.activeLayer.attr("data-blend-id") || '0'; 31 | } 32 | // layerapi.endPullRefresh(Blend.ui.get(id)); 33 | 34 | Blend.ui.get(id).out(); 35 | }; 36 | 37 | api.removeSplashScreen = noop; 38 | 39 | return api; 40 | 41 | } 42 | ); -------------------------------------------------------------------------------- /src/web/blend.js: -------------------------------------------------------------------------------- 1 | define(["require",'./../common/lib',"./configs","./events",'../../usecase/js/lib/jquery-2.1.1.js','./api'], 2 | function(require) { 3 | var lib = require('./../common/lib'); 4 | var configs = require('./configs'); 5 | var events = require('./events'); 6 | 7 | // require('../../third_party/jquery-2.1.1.js'); 8 | 9 | /** 10 | * @class blend 11 | * @singleton 12 | */ 13 | var blend = {}; 14 | var controls = {}; 15 | var cbs={};//临时存储,blend.layerInit 的 layerId对应的执行函数 16 | 17 | 18 | /** 19 | * 版本信息 20 | * 21 | * @property {String} version info 22 | */ 23 | blend.version = 'alpha'; 24 | 25 | /** 26 | * 开放的Api接口entend到blend中 27 | * 28 | * @property {Object} Api接口 29 | */ 30 | 31 | // blend.api = require('./api'); 32 | lib.extend(blend,require('./api')); 33 | 34 | 35 | // {}; 36 | // //main.api.core.removeSplashScreen 37 | // var noop = function(){}; 38 | // blend.api.core={}; 39 | // blend.api.core.removeSplashScreen = noop; 40 | 41 | // blend.api.layer = {}; 42 | // blend.api.layer.on = events.on; 43 | // blend.api.layer.off = events.off; 44 | // blend.api.layer.fire = events.fire; 45 | // blend.api.layer.once = events.once; 46 | 47 | // blend.api.layerStopRefresh = function(id){ 48 | // Layer.prototype.endPullRefresh(blend.get(id)); 49 | // }; 50 | 51 | 52 | 53 | /** 54 | * 开放的Api接口entend到blend中 55 | * 56 | * @property {Object} Api接口 57 | */ 58 | blend.layerInit = function(layerId,callback){ 59 | if (layerId == '0') { 60 | blend.activeLayer = $(".page:first"); 61 | callback && callback(); 62 | } 63 | cbs[layerId] = callback; 64 | }; 65 | document.addEventListener("onrender",function(eve){ 66 | if (eve.detail && cbs[eve.detail]) { 67 | cbs[eve.detail].call(blend.get(eve.detail),blend.get(eve.detail).main);//native 无法传递 layer 对象,所以无法使用 this 68 | } 69 | }); 70 | 71 | /** 72 | * 当前的active apge 记录到blend中 73 | * 74 | * @property {Object} activeLayer 75 | */ 76 | blend.activeLayer = $('.page'); 77 | 78 | 79 | /** 80 | * 是否处于Runtime环境中 81 | * 82 | * @property {boolean} inRuntime 83 | */ 84 | blend.inRuntime = function() { 85 | return false; 86 | };//runtime.inRuntime(); 87 | 88 | 89 | var config = { 90 | DOMPrefix: 'data-ui', 91 | classPrefix: { 92 | 'ui' : 'ui', 93 | 'skin' : 'skin', 94 | 'state' : 'state' 95 | } 96 | }; 97 | 98 | 99 | /** 100 | * 设置config 101 | * 102 | * @property {Object} info 103 | */ 104 | blend.config = function(info) { 105 | lib.extend(config, info); 106 | }; 107 | 108 | /** 109 | * 获取config 110 | * 111 | * @property {String} name 112 | */ 113 | blend.getConfig = function(name) { 114 | return config[name]; 115 | }; 116 | 117 | /** 118 | * 从ID获取Control 119 | * 120 | * @param {String} element 121 | * 122 | * @return {Control} control 123 | */ 124 | blend.getUI = function(element) { 125 | element = $(element)[0]; 126 | do { 127 | //如果元素是document 128 | if (!element || element.nodeType == 9) { 129 | return null; 130 | } 131 | if (element.getAttribute('data-blend')) { 132 | return controls[element.getAttribute('data-blend-id')]; 133 | } 134 | }while ((element = element.parentNode) != document.body); 135 | }; 136 | 137 | /** 138 | * 注册控件到系统中 139 | * 140 | * @param {Control} control 控件实例 141 | * @return null 142 | */ 143 | blend.register = function(control) { 144 | console.log('reg: ' + control.id); 145 | controls[control.id] = control; 146 | }; 147 | 148 | /** 149 | * 注销控件 150 | * 151 | * @param {Control} control 控件实例 152 | * @return null 153 | */ 154 | blend.cancel = function(control) { 155 | //console.log("reg: " + control.id); 156 | delete controls[control.id]; 157 | }; 158 | 159 | blend.create = function(type, options) { 160 | 161 | }; 162 | 163 | /** 164 | * 根据id获取实例 165 | * 166 | * @param {string} id 控件id 167 | * @return {Control} 168 | */ 169 | blend.get = function(id) { 170 | if (id === "0") { 171 | if (!controls[id]) { 172 | controls[id] = new blend.Layer({id:"0"}); 173 | if ($(".page").length){ 174 | controls[id].main = $(".page")[0]; 175 | }else{ 176 | console.warn(" '0' page need to have classes .pages>.page>.page-content "); 177 | } 178 | 179 | } 180 | } 181 | return controls[id]; 182 | }; 183 | 184 | blend.on = events.on; 185 | blend.once = events.once; 186 | blend.off = events.off; 187 | blend.fire = events.fire; 188 | 189 | /** 190 | * 添加运行版本判断 191 | * 192 | */ 193 | (function(){ 194 | var ua = navigator.userAgent; 195 | 196 | var android = ua.match(/(Android);?[\s\/]+([\d.]+)?/); 197 | var ipad = ua.match(/(iPad).*OS\s([\d_]+)/); 198 | var ipod = ua.match(/(iPod)(.*OS\s([\d_]+))?/); 199 | var iphone = !ipad && ua.match(/(iPhone\sOS)\s([\d_]+)/); 200 | 201 | // Android 202 | if (android) { 203 | $("html").addClass('android'); 204 | } 205 | // iOS 206 | if (ipad || iphone || ipod) { 207 | $("html").addClass('ios'); 208 | } 209 | if (iphone || ipod) { 210 | $("html").addClass('iphone'); 211 | } 212 | if (ipad) { 213 | $("html").addClass('ipad'); 214 | } 215 | 216 | })(); 217 | 218 | blend.layerStack = []; 219 | 220 | 221 | blend.configs = configs; 222 | 223 | return blend; 224 | } 225 | ); 226 | -------------------------------------------------------------------------------- /src/web/configs.js: -------------------------------------------------------------------------------- 1 | define( 2 | function(require) { 3 | 4 | /** 5 | * @class configs 6 | * @private 7 | * @static 8 | */ 9 | 10 | var configs = { 11 | cache: true, 12 | dialogTitle: 'BlendUI', 13 | dialogBtnOK: '确认', 14 | dialogBtnCancel: '取消', 15 | layerClass: 'layer', 16 | layerMainClass: 'layer-main', 17 | layersClass: 'layers' 18 | }; 19 | 20 | return configs; 21 | 22 | } 23 | 24 | ); 25 | -------------------------------------------------------------------------------- /src/web/dialog/Confirm.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * @class Confirm 5 | * @extends Dialog 6 | * @inheritable 7 | */ 8 | 9 | function(require) { 10 | 11 | var blend = require('../blend'); 12 | var lib = require('../../common/lib'); 13 | var Dialog = require('../Dialog'); 14 | 15 | 16 | var Confirm = function(content, title, callbackOk, callbackCancel) { 17 | 18 | Dialog.call(this, { 19 | content: content, 20 | title: title, 21 | buttons: [{ 22 | text: blend.configs.dialogBtnCancel, 23 | onclick: callbackCancel 24 | }, { 25 | text: blend.configs.dialogBtnOK, 26 | bold: true, 27 | onclick: callbackOk 28 | } 29 | ] 30 | }); 31 | 32 | return this; 33 | }; 34 | 35 | lib.inherits(Confirm, Dialog); 36 | 37 | 38 | Confirm.prototype.title = blend.configs.dialogTitle; 39 | Confirm.prototype.content = ''; 40 | 41 | 42 | return Confirm; 43 | } 44 | ); 45 | -------------------------------------------------------------------------------- /src/web/dialog/Preloader.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * @class Preloader 5 | * @extends Dialog 6 | * @inheritable 7 | */ 8 | 9 | function(require) { 10 | 11 | var blend = require('../blend'); 12 | var lib = require('../../common/lib'); 13 | var Dialog = require('../Dialog'); 14 | // var Control = require('../Control'); 15 | 16 | 17 | var Preloader = function(title) { 18 | 19 | Dialog.prototype.setProperties.call(this, { 20 | title: title 21 | }); 22 | 23 | Dialog.call(this, { 24 | content: this.content, 25 | title: this.title 26 | }); 27 | return this; 28 | }; 29 | 30 | lib.inherits(Preloader, Dialog); 31 | 32 | Preloader.prototype.title = '载入中...'; 33 | Preloader.prototype.content = '
'; 34 | 35 | 36 | Preloader.prototype.open = function() { 37 | if (!$(this.main).children().length) {//由于loader一直需要,所以open会自动加载被关闭的元素 38 | Dialog.call(this, { 39 | content: this.content, 40 | title: this.title 41 | }); 42 | }else { 43 | Dialog.prototype.open.call(this); 44 | } 45 | }; 46 | 47 | return Preloader; 48 | } 49 | ); 50 | -------------------------------------------------------------------------------- /src/web/dialog/Prompt.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * @class Prompt 5 | * @extends Dialog 6 | * @inheritable 7 | */ 8 | 9 | function(require) { 10 | 11 | var blend = require('../blend'); 12 | var lib = require('../../common/lib'); 13 | var Control = require('../Control'); 14 | var Dialog = require('../Dialog'); 15 | 16 | 17 | var Prompt = function(content, title, callbackOk, callbackCancel) { 18 | Control.prototype.setProperties.call(this, { 19 | content: content, 20 | title: title 21 | }); 22 | 23 | Dialog.call(this, { 24 | content: content, 25 | title: title, 26 | afterContent: '', 27 | buttons: [ 28 | { 29 | text: blend.configs.dialogBtnCancel, 30 | onclick: function() { 31 | if (callbackCancel) { 32 | callbackCancel($(this.main).find('.modal-prompt-input').val()); 33 | } 34 | } 35 | }, 36 | { 37 | text: blend.configs.dialogBtnOK, 38 | bold: true, 39 | onclick: function(modal) { 40 | if (callbackOk) { 41 | callbackOk($(this.main).find('.modal-prompt-input').val()); 42 | } 43 | } 44 | } 45 | ] 46 | }); 47 | 48 | return this; 49 | }; 50 | lib.inherits(Prompt, Dialog); 51 | 52 | Prompt.prototype.title = blend.configs.dialogTitle; 53 | Prompt.prototype.content = ''; 54 | 55 | 56 | return Prompt; 57 | } 58 | ); 59 | -------------------------------------------------------------------------------- /src/web/dialog/alert.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * @class Alert 5 | * @extends Dialog 6 | * @inheritable 7 | */ 8 | 9 | function(require) { 10 | 11 | var blend = require('../blend'); 12 | var lib = require('../../common/lib'); 13 | var Dialog = require('../Dialog'); 14 | var Control = require('../Dialog'); 15 | 16 | 17 | var Alert = function(content, title, buttonText) { 18 | 19 | 20 | Control.prototype.setProperties.call(this, { 21 | content: content, 22 | title: title, 23 | buttonText: buttonText 24 | }); 25 | 26 | Dialog.call(this, { 27 | content: this.content, 28 | title: this.title, 29 | buttons: [{ 30 | text: this.buttonText, 31 | bold: true 32 | }] 33 | }); 34 | return this; 35 | }; 36 | lib.inherits(Alert, Dialog); 37 | 38 | Alert.prototype.title = blend.configs.dialogTitle; 39 | Alert.prototype.content = ''; 40 | Alert.prototype.buttonText = blend.configs.dialogBtnOK; 41 | 42 | return Alert; 43 | } 44 | ); 45 | -------------------------------------------------------------------------------- /src/web/events.js: -------------------------------------------------------------------------------- 1 | define( 2 | function(require) { 3 | 4 | var events = {}; 5 | 6 | // var white_list = [""]; 7 | 8 | var _type = [//原生事件 9 | 'layerCreateSuccess', //layer创建成功 10 | 'layerLoadFinish', //layer 页面载入成功 11 | 'layerPullDown', //下拉刷新loading 12 | 'layerPoped',//layer返回事件 13 | 'tap', //slider点击 14 | 'slide',//slider 滑动切换 15 | 'menuPressed',//菜单建事件 16 | 'layerGoBack',//layer中返回键goBack回调 17 | 'backPressedBeforeExit'//返回键退出事件回调 18 | ]; 19 | 20 | 21 | events.on = function(type, callback, id, context) { 22 | if (typeof context === 'undefined') { 23 | if (id || this.id) { 24 | context = Blend.ui.get(id || this.id).main; 25 | }else{ 26 | if (_type.contains(type)) { 27 | context = Blend.ui.activeLayer[0];//只选active layer 的dom 28 | }else{ 29 | context = document; 30 | } 31 | 32 | } 33 | 34 | } 35 | 36 | //继承父类的on事件 FIXME 父类此方法会引起多重绑定的bug 37 | //细化 web端 事件的处理 38 | //事件on 39 | 40 | if (typeof callback === 'function') { 41 | context.addEventListener(type, callback, false); 42 | } 43 | 44 | }; 45 | //监听一次 46 | events.once = function(type, callback, id, context) { 47 | if (typeof context === 'undefined') { 48 | // context = this.main; 49 | // context = Blend.ui.get(id || this.id).main; 50 | if (id || this.id) { 51 | context = Blend.ui.get(id || this.id).main; 52 | }else{ 53 | context = document; 54 | } 55 | } 56 | 57 | //继承父类的on事件 FIXME 父类此方法会引起多重绑定的bug 58 | // Control.prototype.on(type, callback,(id||this.id) , context); 59 | 60 | //细化 web端 事件的处理 61 | //事件on 62 | 63 | if (typeof callback === 'function') { 64 | var cb = function() { 65 | callback.apply(context, arguments); 66 | context.removeEventListener(type, cb, false); 67 | }; 68 | context.addEventListener(type, cb, false); 69 | } 70 | 71 | }; 72 | //@params id for runtime use,useless for web 73 | events.off = function(type, callback, id, context) { 74 | if (typeof context === 'undefined') { 75 | // context = this.main; 76 | context = Blend.ui.get(id || this.id).main; 77 | } 78 | //继承父类的on事件 79 | // Control.prototype.off(type, callback,(id||this.id) , context); 80 | 81 | //细化 web端 事件的处理 82 | //事件off 83 | 84 | if (typeof callback === 'function') { 85 | context.removeEventListener(type, callback, false); 86 | } 87 | }; 88 | 89 | events.fire = function(type, argAry, message, callback,context) { 90 | //继承父类的fire 事件 91 | // Control.prototype.fire(type, argAry, context); 92 | 93 | //细化 web端 事件的处理 94 | //事件 fire,事件可以冒泡 95 | try { 96 | var e; 97 | if (!argAry)argAry = this.id; 98 | 99 | if (typeof argAry === 'undefined') { 100 | console.warn("cant find fire object. "); 101 | return ; 102 | } 103 | 104 | if (typeof context === 'undefined' && typeof argAry !== 'undefined') { 105 | // context = this.main; 106 | context = Blend.ui.get(argAry).main; 107 | } 108 | if (typeof CustomEvent !== 'undefined') { 109 | var opt = { 110 | bubbles: true, 111 | cancelable: true, 112 | detail: argAry 113 | }; 114 | e = new CustomEvent(type, opt); 115 | console.log(type, opt); 116 | } else { 117 | e = document.createEvent('CustomEvent'); 118 | e.initCustomEvent(type, true, true, argAry); 119 | } 120 | 121 | 122 | 123 | if (typeof message !== 'undefined') { 124 | e.data = message; 125 | } 126 | 127 | if (typeof callback === 'function') { 128 | callback(e); 129 | } 130 | //!!注意 这里的fire 可能在webcontrol里面,也可能在blend.fire 直接触发,所以,不一定有this 方法, 131 | //仅在webcontrol中调用fire时,有这个方法 132 | 133 | // 触发直接挂在对象上的方法,除了需要冒泡的方法需要注册on事件以外,其他事件一律不需要绑定on 方法 134 | var handler = Blend.ui.get(argAry)[ type]; 135 | if (typeof handler === 'function') { 136 | handler.call(this, e); 137 | } 138 | 139 | if (context) { 140 | // e.srcElement = context;//修改无效 141 | (context).dispatchEvent(e); 142 | } 143 | 144 | 145 | } catch (ex) { 146 | console.warn('Events fire errors.please check the runtime environment.', ex.stack); 147 | } 148 | 149 | }; 150 | return events; 151 | } 152 | ); -------------------------------------------------------------------------------- /src/web/layer/layerGroupApi.js: -------------------------------------------------------------------------------- 1 | define( 2 | 3 | /** 4 | * layerGroupApi,封装group api 事件。 5 | * 本api依赖于 crema css, index.html页面需要有 div.pages 容器。 6 | * refresh 需要设定refresh的容易:page-content 7 | * 8 | */ 9 | 10 | function(require) { 11 | 12 | var api = {}; 13 | 14 | var layerapi = require('./layerapi'); 15 | /** 16 | * @class blend.Api.layer 17 | * @singleton 18 | * @private 19 | */ 20 | var devPR = window.devicePixelRatio || 2; 21 | 22 | 23 | /** 24 | * 创建pagerGroup,成功回掉返回 runtime句柄winid 25 | * 26 | * @param {String} groupId id 27 | * @param {Array} layers 本地或网络url链接组成的Array 28 | * @param {Object} options pager数组 29 | * @return null 30 | * @private 31 | */ 32 | 33 | api.create = function(groupId, layers, options, context) { 34 | var layerInfo = { 35 | id: groupId || uniqid(), 36 | infos: layers 37 | }; 38 | if (options.active) { 39 | layerInfo.active = options.active; 40 | } 41 | var groupOptions = {}; 42 | 43 | ['left', 'top', 'width', 'height'].forEach(function(n, i) { 44 | if (options[n] !== undefined) { 45 | groupOptions[n] = options[n] * devPR; 46 | } 47 | }); 48 | var dom; 49 | for (var i = 0, len = layers.length; i < len; i++) { 50 | //load these apis. 51 | dom = document.createElement('div'); 52 | dom.className = 'page'; 53 | 54 | layerapi.prepare(layers[i].id, options, dom); 55 | 56 | context.main.appendChild(dom); 57 | 58 | } 59 | 60 | // window.lc_bridge.addLayerGroup(JSON.stringify(layerInfo), JSON.stringify(groupOptions)); 61 | 62 | 63 | return groupId; 64 | }; 65 | 66 | /** 67 | * 激活GroupId下面的对应的layerId 68 | * @method {Function} showLayer 69 | * @return groupId 70 | * @private 71 | */ 72 | api.showLayer = function(groupId, layerId, context) {//groupId no use 73 | 74 | if (context.__layers[layerId]) { 75 | context.__layers[layerId].in(); 76 | 77 | context.activeId = layerId; 78 | }else { 79 | console.warn('no layerid found...' + layerId); 80 | } 81 | // window.lc_bridge.showLayerInGroup(groupId, layerId); 82 | //@todo return 83 | return groupId; 84 | }; 85 | 86 | /** 87 | * 在group中增加layer 88 | * @private 89 | * @return groupId 90 | */ 91 | api.addLayer = function(groupId, layerGroup) { 92 | window.lc_bridge.addLayerInGroup(groupId, layerGroup, index); 93 | //@todo return 94 | return groupId; 95 | }; 96 | 97 | /** 98 | * 在group中删除layer 99 | * @private 100 | * @return groupId 101 | */ 102 | api.removeLayer = function(groupId, layerId) { 103 | window.lc_bridge.removeLayerInGroup(groupId, layerId); 104 | //@todo return 105 | return groupId; 106 | }; 107 | 108 | /** 109 | * 在group中更新layer 110 | * @private 111 | * @return groupId 112 | */ 113 | api.updateLayer = function(groupId, layerId, layerOptions) { 114 | window.lc_bridge.updateLayerInGroup(groupId, layerId, layerOptions); 115 | //@todo return 116 | return groupId; 117 | }; 118 | 119 | 120 | 121 | return api; 122 | } 123 | ); 124 | -------------------------------------------------------------------------------- /src/web/main.js: -------------------------------------------------------------------------------- 1 | require(['src/web/blend','src/web/dialog/alert','src/web/slider','src/web/Layer.js','src/web/LayerGroup.js'], function (blend, alert,slider,layer,layergroup) { 2 | "use strict"; 3 | 4 | blend = blend||{}; 5 | 6 | //dialogs 7 | blend.dialog = {}; 8 | blend.dialog.alert = alert; 9 | 10 | //components 11 | blend.component = {}; 12 | // blend.component.slider = slider; 13 | blend.Slider = slider; 14 | 15 | //layer 16 | blend.Layer = layer; 17 | blend.LayerGroup = layergroup; 18 | 19 | // window.Blend = blend; 20 | window.Blend = window.Blend || {};//初始化window的blend 对象 , 将 blend 作为模块 绑定到 Blend.ui 上 21 | window.Blend.ui = blend; 22 | 23 | //等到dom ready之后回调 24 | var e; 25 | if (typeof CustomEvent !== 'undefined') { 26 | e = new CustomEvent('blendready', { 27 | // detail: { slideNumber: Math.abs(slideNumber) }, 28 | bubbles: false, 29 | cancelable: true 30 | }); 31 | }else{ 32 | e = document.createEvent("Event"); 33 | e.initEvent("blendready",false,false); 34 | } 35 | 36 | 37 | if (/complete|loaded|interactive/.test(document.readyState)) { 38 | document.dispatchEvent(e); 39 | } else { 40 | document.addEventListener('DOMContentLoaded', function() { 41 | document.dispatchEvent(e); 42 | }, false); 43 | } 44 | 45 | },null,true); 46 | -------------------------------------------------------------------------------- /test/autotest.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Clouda自动化测试 6 | 7 | 8 | 9 | 10 | 11 | 12 | 19 | 20 | 21 |
22 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/autotest.js: -------------------------------------------------------------------------------- 1 | require([ 2 | 'autotest/Layer.js', 3 | 'autotest/uatest.js', 4 | 'autotest/native-api.js' 5 | ],function(){ 6 | if(window.lc_bridge){ 7 | mocha.run(); 8 | }else if(navigator.userAgent.match(/BaiduLightAppRuntime/)){ 9 | document.addEventListener('runtimeready', function(){ 10 | mocha.run(); 11 | }, false); 12 | }else if(window.mochaPhantomJS){ 13 | mochaPhantomJS.run(); 14 | }else{ 15 | mocha.run(); 16 | } 17 | }); -------------------------------------------------------------------------------- /test/autotest/Layer.js: -------------------------------------------------------------------------------- 1 | define(['../../src/hybrid/Layer'],function(Layer){ 2 | var expect = chai.expect; 3 | describe('Layer测试', function () { 4 | this.timeout(50000); 5 | describe('Layer',function(){ 6 | it('验证Layer类', function () { 7 | expect(Layer).to.be.a('function'); 8 | expect(Layer).to.be.instanceof(Function); 9 | expect(Layer).to.throw(Error); 10 | expect(Layer.prototype.type).to.equal('layer'); 11 | }); 12 | it('创建Layerd对象', function () { 13 | var testLayer; 14 | var createLayerNoUrl = function(){ 15 | testLayer = new Layer({ 16 | 'id':"test" 17 | }); 18 | } 19 | var createLayerHaveUrl = function(){ 20 | testLayer = new Layer({ 21 | 'id':"test1", 22 | 'url':'a.html' 23 | }); 24 | } 25 | var activeLayer = function(){ 26 | testLayer.in(); 27 | } 28 | var outLayer = function(){ 29 | testLayer.out(); 30 | } 31 | expect(createLayerNoUrl).to.throw(Error); 32 | expect(createLayerHaveUrl).to.not.throw(Error); 33 | expect(testLayer).to.be.instanceof(Layer); 34 | expect(testLayer.id).to.equal('test1'); 35 | expect(testLayer.in).to.be.a('function'); 36 | expect(activeLayer).to.not.throw(Error); 37 | expect(testLayer.isActive()).to.be.true; 38 | expect(outLayer).to.not.throw(Error); 39 | expect(testLayer.isActive()).to.not.be.true; 40 | expect(testLayer.canGoBack()).to.not.be.true; 41 | //testLayer.getUrl() 是异步的; 42 | expect(testLayer.url).to.match(/^http:/); 43 | 44 | }); 45 | }); 46 | 47 | describe('layer的侧边栏',function(){ 48 | var createSideNotarg = function(){ 49 | Layer.addSidebar() 50 | } 51 | var createSide = function(){ 52 | Layer.addSidebar({ 53 | url:'http://baidu.com' 54 | }) 55 | } 56 | it('侧边栏addSidebar方法', function () { 57 | expect(Layer).to.have.property('addSidebar'); 58 | expect(Layer.addSidebar).to.be.a('function'); 59 | expect(Layer.addSidebar).to.be.instanceof(Function); 60 | expect(createSideNotarg).to.throw(Error); 61 | expect(createSide).to.not.throw(Error); 62 | }); 63 | 64 | it('侧边栏showSidebar方法', function () { 65 | expect(Layer).to.have.property('showSidebar') 66 | }); 67 | 68 | it('侧边栏hideSidebar方法', function () { 69 | expect(Layer).to.have.property('hideSidebar') 70 | }); 71 | 72 | it('侧边栏destorySidebar方法', function () { 73 | expect(Layer).to.have.property('destorySidebar') 74 | }); 75 | }); 76 | }); 77 | }); -------------------------------------------------------------------------------- /test/autotest/native-api.js: -------------------------------------------------------------------------------- 1 | define(function(require){ 2 | var expect = chai.expect; 3 | var assert = chai.assert; 4 | describe('Naitve层接口存在', function () { 5 | var bridge = window.nuwa_frame || window.lc_bridge ||{}; 6 | 7 | it('组件接口', function () { 8 | assert.ok(bridge.addComponent, "addComponent接口"); 9 | assert.ok(bridge.componentExecuteNative, "componentExecuteNative接口"); 10 | }); 11 | 12 | it('Layer接口', function () { 13 | assert.ok(bridge.prepareLayer, "prepareLayer接口"); 14 | assert.ok(bridge.resumeLayer, "resumeLayer接口"); 15 | assert.ok(bridge.backToPreviousLayer, "backToPreviousLayer接口"); 16 | assert.ok(bridge.hideSubLayer, "hideSubLayer接口"); 17 | assert.ok(bridge.layerLoadUrl, "layerLoadUrl接口"); 18 | assert.ok(bridge.destroyLayer, "destroyLayer接口"); 19 | assert.ok(bridge.layerSetPullRefresh, "layerSetPullRefresh接口"); 20 | assert.ok(bridge.layerStopRefresh, "layerStopRefresh接口"); 21 | assert.ok(bridge.isLayerAvailable, "isLayerAvailable接口"); 22 | assert.ok(bridge.currentLayerUrl, "currentLayerUrl接口"); 23 | assert.ok(bridge.currentLayerId, "layerStopRefresh接口"); 24 | assert.ok(bridge.layerStopLoading, "layerStopLoading接口"); 25 | assert.ok(bridge.layerPostMessage, "layerPostMessage接口"); 26 | assert.ok(bridge.layerGetOriginalUrl, "layerGetOriginalUrl接口"); 27 | 28 | assert.ok(bridge.layerGetUrl, "layerGetUrl接口"); 29 | assert.ok(bridge.layerCanGoBack, "layerCanGoBack接口"); 30 | assert.ok(bridge.layerCanGoBackOrForward, "layerCanGoBackOrForward接口"); 31 | 32 | assert.ok(bridge.isLayerActive, "isLayerActive接口"); 33 | assert.ok(bridge.layerClearHistory, "layerClearHistory接口"); 34 | assert.ok(bridge.layerSetLayout, "layerSetLayout接口"); 35 | }); 36 | 37 | }); 38 | 39 | }); -------------------------------------------------------------------------------- /test/autotest/uatest.js: -------------------------------------------------------------------------------- 1 | define(function(require){ 2 | var expect = chai.expect; 3 | /* 4 | http://chaijs.com/guide/styles/ 5 | chai.use(function(chai, utils){ 6 | var Assertion = chai.Assertion; 7 | console.log(chai); 8 | console.log(utils); 9 | var myAssert = new Assertion('Arthur Dent'); 10 | utils.flag(myAssert, 'owner', 'me'); 11 | var owner = utils.flag(myAssert, 'owner'); 12 | console.log(myAssert._obj,owner); 13 | var myAssert = new Assertion('Arthur Dent'); 14 | var obj = utils.flag(myAssert, 'object'); // obj === 'Arthur Dent'; 15 | console.log(myAssert._obj); 16 | }); 17 | */ 18 | describe('Runtime ua测试', function () { 19 | beforeEach(function () { 20 | console.log(this); 21 | }); 22 | before(function () { 23 | console.log(this); 24 | }); 25 | afterEach(function () { 26 | console.log(this); 27 | }); 28 | after(function () { 29 | console.log(this); 30 | }); 31 | var ua = navigator.userAgent; 32 | it('ua含有BlendUI', function () { 33 | expect(ua).to.match(/BlendUI/); 34 | }); 35 | 36 | it('ua含有BaiduLightAppRuntime', function () { 37 | expect(ua).to.match(/BaiduLightAppRuntime/); 38 | }); 39 | 40 | it('Runtime版本号是4位', function () { 41 | var _v = ua.match(/BaiduLightAppRuntime\/([^\s]+)/)[1]; 42 | var _v = _v.split('.'); 43 | expect(_v).to.have.length(4); 44 | }); 45 | 46 | it('Runtime版本是否大于2.4', function () { 47 | var _v = ua.match(/BaiduLightAppRuntime\/([^\s]+)/); 48 | expect(parseFloat(_v[1])).to.be.greaterThan(2.4); 49 | }); 50 | }); 51 | 52 | }); -------------------------------------------------------------------------------- /test/case-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 13 | 14 | 15 | 16 |

自动化测试:

17 |
整体自动化测试 18 |
19 | 20 | 21 |
22 |

case功能性测试:

23 | 24 | layer功能测试 25 |
26 | subLayer功能测试 27 |
28 | layerGroup功能 29 |
30 | footbar菜单 31 |
32 | slider功能测试 33 |
34 | 侧边栏功能 35 |
36 | 级联菜单测试 37 |
38 | 调用键盘 39 |
40 | 41 | 42 | 49 | 50 | -------------------------------------------------------------------------------- /test/case/dialog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 全局接口 7 | 8 | 9 | 10 | 11 | 12 |

dialog



13 | 14 | 15 |
 16 | Blend.ui.dialog.alert({
 17 |     "title":"测试",
 18 |     "msg":"萨芬的撒点粉的说法",
 19 |     "button":"ok"
 20 | },function(event){
 21 |     alert("alertId:"+event.alertId);
 22 |     alert("result:"+event.result);
 23 | });
 24 | 
25 | 26 |
 27 | Blend.ui.dialog.prompt({
 28 |     "title":"bbbbb",
 29 |     "msg":"sdfasdlfjdsajfdsafdsafsssssssssssssssssssssssss",
 30 |     "buttons":['确定','取消']
 31 | },function(event){
 32 |    alert(event.promptId);
 33 |     alert(event.result);
 34 |     alert(event.buttonIndex);
 35 |     alert(event.inputContxt);
 36 | });
 37 | 
38 | 39 |
 40 | Blend.ui.dialog.confirm({
 41 |     "title":"测试",
 42 |     "msg":"你确定撒旦法舒服",
 43 |     "buttons":['确定','取消']
 44 | },function(event){
 45 |     alert(event.promptId);
 46 |     alert(event.result);
 47 |     alert(event.buttonIndex);
 48 | });
 49 | 
50 | 51 |
 52 | Blend.ui.dialog.toast({
 53 |     "msg":"测试网络连接"
 54 | });
 55 | 
56 | 58 |
59 | 60 |
61 |



62 | 63 | 123 |
124 |
125 |
126 |
127 |
128 | 129 | -------------------------------------------------------------------------------- /test/case/footbar.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | footbar菜单 7 | 8 | 9 | 10 | 11 | 12 |

footbar功能测试



13 | 14 | 15 |
16 | 17 |
18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 |
29 | 30 |



















































31 | 32 | 133 |
134 |
135 |
136 |
137 |
138 | 139 | -------------------------------------------------------------------------------- /test/case/index_demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | layer功能测试 7 | 8 | 9 | 10 | 12 | 13 | 14 | layer测试

15 | 16 | 17 | 19 | 20 | 21 | 22 |
23 |
24 | 25 |
26 |
27 |
28 | 29 |
30 |
31 |
32 | 可测试changeUrl事件 33 | 可测试back事件 34 | 35 | 96 | -------------------------------------------------------------------------------- /test/case/index_demo1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | layer功能测试 7 | 8 | 9 | 10 | 11 | 12 | layer测试

13 | 14 | 15 | 17 | 18 | 19 | 20 |
21 |
22 | 23 |
24 |
25 |
26 | 27 |
28 |
29 |
30 | 可测试changeUrl事件 31 | 32 | 67 | -------------------------------------------------------------------------------- /test/case/keyboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 全局接口 7 | 8 | 9 | 10 | 11 | 12 |

全局接口



13 | 14 | 15 | 16 | 17 |
18 | 19 |
20 |
21 |
22 |



















































23 | 24 | 82 |
83 |
84 |
85 |
86 |
87 | 88 | -------------------------------------------------------------------------------- /test/case/layerGroup.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | LayerGroup功能测试 7 | 8 | 9 | 10 | 11 | 12 | 13 |

layerGroup功能测试

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 | 156 |
157 |
158 |
159 |
160 |
161 | 162 | 163 | -------------------------------------------------------------------------------- /test/case/layerload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | layer功能测试 7 | 8 | 9 | 10 | 11 | 12 | 下拉刷新下拉刷新下拉刷新 13 |
14 | 15 | 35 |
36 |
37 |
38 |
39 |
40 | 41 | -------------------------------------------------------------------------------- /test/case/menuLayer-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 侧边栏 7 | 8 | 9 | 10 | 11 | 12 | 13 |

侧边栏layer



14 | 15 | test 16 |

17 | 18 | 19 | 20 | 21 |                          22 |                          23 | 24 | 25 |
26 |



















































27 | 28 | 74 |
75 |
76 |
77 |
78 |
79 | 80 | 81 | -------------------------------------------------------------------------------- /test/case/menuLayer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 侧边栏 7 | 8 | 9 | 10 | 11 | 12 | 13 |

侧边栏layer



14 | 15 | 16 | 17 | 18 | 19 | 163 20 |
21 |



















































22 | 23 | 65 |
66 |
67 |
68 |
69 |
70 | 71 | 72 | -------------------------------------------------------------------------------- /test/case/silder.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | sliderbar菜单 7 | 8 | 9 | 10 | 11 | 12 |

slider功能测试



13 | 14 | 15 | 16 | 17 | 18 |
19 |
20 |























































21 | 22 | 88 |
89 |
90 |
91 |
92 |
93 | 94 | -------------------------------------------------------------------------------- /test/case/sms.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | sms验证码 9 | 10 | 11 | 12 | 16 | 17 | 18 |

短信验证码



19 | 20 |
21 |

"来自支付宝 123456"

22 |
23 |

"来自支付宝 1234"

24 |
25 |

"来自dazzle 123456"

26 |
27 | 28 | 29 | 30 | 31 | 68 |
69 |
70 |
71 |
72 |
73 | 74 | 75 | -------------------------------------------------------------------------------- /test/case/subLayer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 子layer 7 | 8 | 9 | 10 | 11 | 12 |

局部layer功能



13 | 14 | 15 | 16 | 17 | 18 | 163 19 |



















































20 | 21 | 71 |
72 |
73 |
74 |
75 |
76 | 77 | 78 | -------------------------------------------------------------------------------- /third_party/mocha.css: -------------------------------------------------------------------------------- 1 | @charset "utf-8"; 2 | 3 | body { 4 | margin:0; 5 | } 6 | 7 | #mocha { 8 | font: 20px/1.5 "Helvetica Neue", Helvetica, Arial, sans-serif; 9 | margin:60px 10px; 10 | } 11 | 12 | #mocha ul, 13 | #mocha li { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | #mocha ul { 19 | list-style: none; 20 | } 21 | 22 | #mocha h1, 23 | #mocha h2 { 24 | margin: 0; 25 | } 26 | 27 | #mocha h1 { 28 | margin-top: 15px; 29 | font-size: 1em; 30 | font-weight:600; 31 | padding:2px 10px; 32 | margin-bottom: 2px; 33 | background-color: #ddd; 34 | } 35 | 36 | #mocha h1 a { 37 | text-decoration: none; 38 | color: inherit; 39 | } 40 | 41 | #mocha h1 a:hover { 42 | text-decoration: underline; 43 | } 44 | 45 | #mocha .suite .suite h1 { 46 | margin-top: 0; 47 | font-size: .8em; 48 | } 49 | 50 | #mocha .hidden { 51 | display: none; 52 | } 53 | 54 | #mocha h2 { 55 | font-size: 12px; 56 | color:#666; 57 | font-weight: normal; 58 | cursor: pointer; 59 | } 60 | 61 | #mocha .suite { 62 | /*margin-left: 15px;*/ 63 | } 64 | 65 | #mocha .test { 66 | /*margin-left: 15px;*/ 67 | overflow: hidden; 68 | margin-bottom: 5px; 69 | } 70 | 71 | #mocha .test.pending:hover h2::after { 72 | content: '(pending)'; 73 | font-family: arial, sans-serif; 74 | } 75 | 76 | #mocha .test.pass.medium .duration { 77 | background: #c09853; 78 | } 79 | 80 | #mocha .test.pass.slow .duration { 81 | background: #b94a48; 82 | } 83 | 84 | #mocha .test.pass::before { 85 | content: '✓'; 86 | font-size: 12px; 87 | display: block; 88 | float: left; 89 | margin-right: 5px; 90 | color: #00d6b2; 91 | } 92 | 93 | #mocha .test.pass .duration { 94 | font-size: 9px; 95 | margin-left: 5px; 96 | padding: 2px 5px; 97 | color: #fff; 98 | -webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 99 | -moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 100 | box-shadow: inset 0 1px 1px rgba(0,0,0,.2); 101 | -webkit-border-radius: 5px; 102 | -moz-border-radius: 5px; 103 | -ms-border-radius: 5px; 104 | -o-border-radius: 5px; 105 | border-radius: 5px; 106 | } 107 | 108 | #mocha .test.pass.fast .duration { 109 | display: none; 110 | } 111 | 112 | #mocha .test.pending { 113 | color: #0b97c4; 114 | } 115 | 116 | #mocha .test.pending::before { 117 | content: '◦'; 118 | color: #0b97c4; 119 | } 120 | 121 | #mocha .test.fail { 122 | color: #c00; 123 | } 124 | 125 | #mocha .test.fail pre { 126 | color: black; 127 | } 128 | 129 | #mocha .test.fail::before { 130 | content: '✖'; 131 | font-size: 12px; 132 | display: block; 133 | float: left; 134 | margin-right: 5px; 135 | color: #c00; 136 | } 137 | 138 | #mocha .test pre.error { 139 | color: #c00; 140 | max-height: 300px; 141 | overflow: auto; 142 | } 143 | 144 | /** 145 | * (1): approximate for browsers not supporting calc 146 | * (2): 42 = 2*15 + 2*10 + 2*1 (padding + margin + border) 147 | * ^^ seriously 148 | */ 149 | #mocha .test pre { 150 | display: block; 151 | /*float: left; 152 | clear: left;*/ 153 | font: 12px/1.5 monaco, monospace; 154 | margin: 5px; 155 | padding: 15px; 156 | border: 1px solid #eee; 157 | max-width: 85%; /*(1)*/ 158 | max-width: calc(100% - 42px); /*(2)*/ 159 | word-wrap: break-word; 160 | border-bottom-color: #ddd; 161 | -webkit-border-radius: 3px; 162 | -webkit-box-shadow: 0 1px 3px #eee; 163 | -moz-border-radius: 3px; 164 | -moz-box-shadow: 0 1px 3px #eee; 165 | border-radius: 3px; 166 | } 167 | 168 | #mocha .test h2 { 169 | position: relative; background-color: #efefef; 170 | } 171 | 172 | #mocha .test a.replay { 173 | position: absolute; 174 | top: 3px; 175 | right: 0; 176 | text-decoration: none; 177 | vertical-align: middle; 178 | display: block; 179 | width: 15px; 180 | height: 15px; 181 | line-height: 15px; 182 | text-align: center; 183 | background: #eee; 184 | font-size: 15px; 185 | -moz-border-radius: 15px; 186 | border-radius: 15px; 187 | -webkit-transition: opacity 200ms; 188 | -moz-transition: opacity 200ms; 189 | transition: opacity 200ms; 190 | opacity: 0.3; 191 | color: #888; 192 | } 193 | 194 | #mocha .test:hover a.replay { 195 | opacity: 1; 196 | } 197 | 198 | #mocha-report.pass .test.fail { 199 | display: none; 200 | } 201 | 202 | #mocha-report.fail .test.pass { 203 | display: none; 204 | } 205 | 206 | #mocha-report.pending .test.pass, 207 | #mocha-report.pending .test.fail { 208 | display: none; 209 | } 210 | #mocha-report.pending .test.pass.pending { 211 | display: block; 212 | } 213 | 214 | #mocha-error { 215 | color: #c00; 216 | font-size: 1.5em; 217 | font-weight: 100; 218 | letter-spacing: 1px; 219 | } 220 | 221 | #mocha-stats { 222 | position: fixed; 223 | top: 15px; 224 | right: 10px; 225 | font-size: 12px; 226 | margin: 0; 227 | color: #888; 228 | z-index: 1; 229 | background-color: #fff; 230 | } 231 | 232 | #mocha-stats .progress { 233 | float: right; 234 | padding-top: 0; 235 | } 236 | 237 | #mocha-stats em { 238 | color: black; 239 | } 240 | 241 | #mocha-stats a { 242 | text-decoration: none; 243 | color: inherit; 244 | } 245 | 246 | #mocha-stats a:hover { 247 | border-bottom: 1px solid #eee; 248 | } 249 | 250 | #mocha-stats li { 251 | display: inline-block; 252 | margin: 0 5px; 253 | list-style: none; 254 | padding-top: 11px; 255 | } 256 | 257 | #mocha-stats canvas { 258 | width: 40px; 259 | height: 40px; 260 | } 261 | 262 | #mocha code .comment { color: #ddd; } 263 | #mocha code .init { color: #2f6fad; } 264 | #mocha code .string { color: #5890ad; } 265 | #mocha code .keyword { color: #8a6343; } 266 | #mocha code .number { color: #2f6fad; } 267 | 268 | @media screen and (max-device-width: 480px) { 269 | #mocha { 270 | margin: 60px 0px; 271 | } 272 | 273 | #mocha #stats { 274 | position: absolute; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /usecase/img/list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Clouda-team/BlendUI/dd7775c5b626c551c13e927e130a63de463ab05e/usecase/img/list.png -------------------------------------------------------------------------------- /usecase/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | new blend ui 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 42 | 43 | 44 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /usecase/item.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | new blend ui 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 62 |
63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /usecase/js/all.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | window.blend=Blend.ui; 3 | 4 | var layer = blend.Layer; 5 | 6 | // blend.layerInit("info",function(){ 7 | // console.log("info page......"); 8 | // //console.log(blend.get); 9 | // var mylayer = blend.get("info"); 10 | // $("a.back").click(function(e){ 11 | // console.log("click.... back... out....") 12 | // mylayer.out(); 13 | // return false; 14 | // }); 15 | 16 | // $("a.reload").click(function(e){ 17 | // mylayer.reload("item.html"); 18 | // // e.preventDefault(); 19 | // return false; 20 | // }); 21 | 22 | // }); 23 | 24 | blend.layerInit("0",function(){ 25 | $(document).click(function(e){ 26 | //blend.fire("layercreates","0",{data1:123,data2:[1,2,3]}); 27 | 28 | // var _layer = window._layer; 29 | var $t = $(e.target).closest('a'); 30 | if (!$t.length) return true; 31 | if ($t.attr("href")=='#')return ; 32 | // console.log($t.attr("href")) 33 | e.preventDefault(); 34 | window._layer && window._layer.destroy(); 35 | window._layer = new layer({ 36 | "id":"info", 37 | "url": $t.attr('href'), 38 | "active":true 39 | ,"afterrender":function(){ 40 | console.log("onrender..."); 41 | } 42 | ,"onload":function(event){ 43 | console.log("onload..."); 44 | } 45 | ,"changeUrl":function(event){ 46 | // console.log("onload"); 47 | console.log(event['url']) 48 | } 49 | ,"onhide":function(e){ 50 | console.log("onhide...") 51 | } 52 | ,"pullToRefresh":true 53 | ,"ptrFn":function(){ 54 | setTimeout(function(){ 55 | console.log("refresh callback"); 56 | window._layer.endPullRefresh(); 57 | 58 | },1500); 59 | 60 | } 61 | }); 62 | // $(".list-block a:first").click(); 63 | 64 | }); 65 | }); 66 | 67 | 68 | 69 | })(); 70 | -------------------------------------------------------------------------------- /usecase/js/blend.js: -------------------------------------------------------------------------------- 1 | if( !navigator.userAgent.match(/BlendUI/i) ){ 2 | document.write(''); 3 | }else{ 4 | document.write(''); 5 | document.write(''); 6 | } 7 | --------------------------------------------------------------------------------