├── test
└── ut
│ ├── ext_map
│ ├── widget
│ │ ├── b.js
│ │ ├── c.js
│ │ └── a.js
│ ├── server.conf
│ ├── plugin
│ │ ├── modifier.f_escape_xml.php
│ │ ├── compiler.cdn.php
│ │ ├── modifier.f_escape_path.php
│ │ ├── modifier.f_escape_js.php
│ │ ├── modifier.f_escape_data.php
│ │ ├── modifier.f_escape_callback.php
│ │ ├── modifier.f_escape_event.php
│ │ ├── compiler.uri.php
│ │ ├── compiler.body.php
│ │ ├── compiler.widget_block.php
│ │ ├── compiler.head.php
│ │ ├── compiler.title.php
│ │ ├── compiler.require.php
│ │ ├── block.widget_inline.php
│ │ ├── compiler.html.php
│ │ ├── function.http_header.php
│ │ ├── compiler.script.php
│ │ ├── compiler.style.php
│ │ ├── compiler.widget.php
│ │ ├── README.md
│ │ └── lib
│ │ │ ├── FISResource.class.php
│ │ │ └── FISPagelet.class.php
│ ├── page
│ │ └── index.tpl
│ ├── fis-conf.js
│ ├── webapp
│ │ └── webapp.php
│ └── static
│ │ └── mod-store.js
│ └── ext_map.js
├── .gitignore
├── README.md
├── package.json
└── index.js
/test/ut/ext_map/widget/b.js:
--------------------------------------------------------------------------------
1 | console.log('b');
--------------------------------------------------------------------------------
/test/ut/ext_map/widget/c.js:
--------------------------------------------------------------------------------
1 | console.log('c');
--------------------------------------------------------------------------------
/test/ut/ext_map/server.conf:
--------------------------------------------------------------------------------
1 | rewrite ^\/webapp\.php webapp/webapp.php
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /.idea
2 | /.settings
3 | /.project
4 |
5 | tmp
6 | ~*
7 | *.db
8 | *.bak
9 | *.tmp
10 | *.swp
11 | .DS_Store
12 |
--------------------------------------------------------------------------------
/test/ut/ext_map/widget/a.js:
--------------------------------------------------------------------------------
1 | console.log('a');
2 | console.log('b');
3 | console.log('c');
4 | alert('a');
5 | alert('b');
6 | alert('cccc');
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/modifier.f_escape_xml.php:
--------------------------------------------------------------------------------
1 | ', '\'', '"'),
5 | array('&', '<', '>', ''', '"'),
6 | strval($str)
7 | );
8 | }
--------------------------------------------------------------------------------
/test/ut/ext_map.js:
--------------------------------------------------------------------------------
1 | var project_root = __dirname + '/ext_map',
2 | fisp = require('fis-plus');
3 | fisp.cli.run([
4 | 'fisp release -cmpd output -r ' + project_root + ' --no-color',
5 | 'fisp',
6 | 'release',
7 | '-cmpd',
8 | 'output',
9 | '-r',
10 | project_root,
11 | '--no-color'
12 | ]);
13 |
--------------------------------------------------------------------------------
/test/ut/ext_map/page/index.tpl:
--------------------------------------------------------------------------------
1 | {%html framework="extmap:static/mod-store.js"%}
2 | {%head%}
3 |
This is a test
4 | {%/head%}
5 | {%body%}
6 | {%script%}
7 | require.async('/widget/a.js');
8 | require.async('/widget/b.js');
9 | {%/script%}
10 | {%/body%}
11 | {%/html%}
12 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.cdn.php:
--------------------------------------------------------------------------------
1 | ';
11 | return $strCode;
12 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/modifier.f_escape_path.php:
--------------------------------------------------------------------------------
1 | "\\\\",
6 | "'" => "\\x27",
7 | "\"" => "\\x22",
8 | "/" => "\\/",
9 | "\n" => "\\n",
10 | "\r" => "\\r"
11 | );
12 | $patterns = array_keys($char_map);
13 | $targets = array_values($char_map);
14 | return str_replace($patterns, $targets, $str);
15 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/modifier.f_escape_data.php:
--------------------------------------------------------------------------------
1 | '<',
6 | '>' => '>',
7 | "'" => "\\'",
8 | '"' => '\\"',
9 | "\\" => "\\\\",
10 | "\n" => "\\n",
11 | "\r" => "\\r",
12 | "/" => "\\/"
13 | );
14 | $patterns = array_keys($arr_js_char);
15 | $targets = array_values($arr_js_char);
16 | $ret = str_replace($patterns, $targets, $str);
17 | return $ret;
18 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/modifier.f_escape_callback.php:
--------------------------------------------------------------------------------
1 | ",
19 | "license": "MIT"
20 | }
21 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/modifier.f_escape_event.php:
--------------------------------------------------------------------------------
1 | '&',
6 | '<' => '<',
7 | '>' => '>',
8 | "\\" => "\\\\",
9 | "'" => "\\'",
10 | '"' => "\\"",
11 | "\n" => "\\n",
12 | "\r" => "\\r",
13 | "/" => "\\/"
14 | );
15 | $patterns = array_keys($char_map);
16 | $targets = array_values($char_map);
17 | return str_replace($patterns, $targets, $str);
18 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.uri.php:
--------------------------------------------------------------------------------
1 | smarty);';
10 | $strCode .= '?>';
11 | }
12 | return $strCode;
13 | }
14 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.body.php:
--------------------------------------------------------------------------------
1 | $_value) {
6 | $strAttr .= ' ' . $_key . '=""';
7 | }
8 | return '';
9 | }
10 |
11 | function smarty_compiler_bodyclose($arrParams, $smarty){
12 | $strCode = '';
13 | $strCode .= '';
18 | return $strCode;
19 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.widget_block.php:
--------------------------------------------------------------------------------
1 | ';
10 | return $strCode;
11 | }
12 |
13 | function smarty_compiler_widget_blockclose($arrParams, $smarty){
14 | $strCode = '';
15 | $strCode .= '';
20 | return $strCode;
21 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.head.php:
--------------------------------------------------------------------------------
1 | $_value) {
6 | $strAttr .= ' ' . $_key . '=""';
7 | }
8 | return '';
9 | }
10 |
11 | function smarty_compiler_headclose($arrParams, $smarty){
12 | $strResourceApiPath = preg_replace('/[\\/\\\\]+/', '/', dirname(__FILE__) . '/lib/FISPagelet.class.php');
13 | $strCode = '';
17 | $strCode .= '';
18 | return $strCode;
19 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.title.php:
--------------------------------------------------------------------------------
1 | " . '';
5 | }
6 |
7 | function smarty_compiler_titleclose($arrParams, $smarty){
8 | $strResourceApiPath = preg_replace('/[\\/\\\\]+/', '/', dirname(__FILE__) . '/lib/FISPagelet.class.php');
9 | $strCode = '';
18 | return $strCode . '';
19 | }
20 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.require.php:
--------------------------------------------------------------------------------
1 | smarty,'.$async.');';
20 | $strCode .= '?>';
21 | }
22 | return $strCode;
23 | }
24 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/block.widget_inline.php:
--------------------------------------------------------------------------------
1 | $value){
7 | if($value instanceof Smarty_Variable){
8 | $tpl_vars[$key] = $value;
9 | } else {
10 | $tpl_vars[$key] = new Smarty_Variable($value);
11 | }
12 | }
13 | }
14 | public static function pop(&$tpl_vars){
15 | $tpl_vars = array_pop(self::$_vars);
16 | }
17 | }
18 |
19 | function smarty_block_widget_inline($params, $content, Smarty_Internal_Template $template, &$repeat){
20 | if(!$repeat){//block 定义结束
21 | FISBlockFisWidget::pop($template->tpl_vars);
22 | return $content;
23 | }else{//block 定义开始
24 | FISBlockFisWidget::push($params, $template->tpl_vars);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.html.php:
--------------------------------------------------------------------------------
1 | smarty));';
16 | }
17 | $strCode .= 'FISPagelet::init('.$strMode.');';
18 | $strCode .= ' ?>';
19 | foreach ($arrParams as $_key => $_value) {
20 | if (is_numeric($_key)) {
21 | $strAttr .= ' ';
22 | } else {
23 | $strAttr .= ' ' . $_key . '=""';
24 | }
25 | }
26 | return $strCode . "";
27 | }
28 |
29 | function smarty_compiler_htmlclose($arrParams, $smarty){
30 | $strCode = 'registerFilter(\'output\', array(\'FISPagelet\', \'renderResponse\'));';
32 | $strCode .= '?>';
33 | $strCode .= '';
34 | return $strCode;
35 | }
36 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/function.http_header.php:
--------------------------------------------------------------------------------
1 | 'text/html',
26 | 'json' => 'application/json',
27 | 'javascript' => 'application/x-javascript',
28 | 'js' => 'application/x-javascript',
29 | 'xml' => 'text/xml',
30 | 'stream' => 'application/octet-stream'
31 | );
32 | if (array_key_exists($type, $mimeTypes)) {
33 | $mime = $mimeTypes[$type];
34 | } else {
35 | $mime = "text/plain";
36 | }
37 | header("Content-Type:$mime; charset=$charset;");
38 | }
39 |
40 |
41 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.script.php:
--------------------------------------------------------------------------------
1 | ';
11 | return $strCode;
12 | }
13 |
14 | function smarty_compiler_scriptclose($params, $smarty){
15 | $strResourceApiPath = preg_replace('/[\\/\\\\]+/', '/', dirname(__FILE__) . '/lib/FISPagelet.class.php');
16 | $strCode = '';
31 | return $strCode;
32 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.style.php:
--------------------------------------------------------------------------------
1 | ';
13 | return $strCode;
14 | }
15 |
16 | function smarty_compiler_styleclose($params, $smarty){
17 | $strResourceApiPath = preg_replace('/[\\/\\\\]+/', '/', dirname(__FILE__) . '/lib/FISPagelet.class.php');
18 | $strCode = '';
33 | return $strCode;
34 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * fis.baidu.com
3 | */
4 |
5 | 'use strict'
6 |
7 | var allRet;
8 |
9 | //project root
10 | var root;
11 | var ns;
12 |
13 | var _ = fis.util;
14 |
15 |
16 | // create .data.php
17 | function genData(opt) {
18 | var ids = allRet.ids;
19 | var data = {};
20 | _.map(ids, function(id, file) {
21 | //只对JS起效
22 | if (file.rExt == '.js') {
23 | data[id] = {
24 | 'uri': file.getUrl(opt.hash, opt.domain),
25 | 'content': file.getContent()
26 | };
27 | }
28 | });
29 | var dataFile = fis.file(root, '/webapp/' + (ns ? ns + '-' : '') + 'data.json');
30 | dataFile.useHash = false;
31 | dataFile.setContent(JSON.stringify(data), null, opt.optimize ? null : 4);
32 | allRet.pkg[dataFile.subpath] = dataFile;
33 | }
34 |
35 | module.exports = function(ret, conf, settings, opt) {
36 |
37 | root = fis.project.getProjectPath();
38 | ns = fis.config.get('namespace');
39 |
40 | _.map(ret.map.res, function(id, info) {
41 | //提供给@wangcheng的统计用
42 | info['hash'] = fis.util.md5(id, 7);
43 | });
44 |
45 | //处理自动打包插件传递的多维的打包数据(如国际化多国家map配置),生成md5及多维的map.json
46 | var retList = opt['retlist'];
47 | if(retList){
48 | //给多维度的资源添加md5
49 | _.map(retList,function(dimen,_ret){
50 | if(dimen!="default"){ //默认维度不处理
51 | _.map(_ret.res, function(id, info) {
52 | info['hash'] = fis.util.md5(id, 7);
53 | });
54 | //生成多个维度的map.json
55 | var map = fis.file(root, (ns ? ns + '-' : '') + 'map-' + dimen +'.json');
56 | //修正map文件放置目录,config下
57 | map.release = "/config" + map.subpath;
58 | map.setContent(JSON.stringify(_ret, null, opt.optimize ? null : 4));
59 | ret.pkg[map.subpath] = map;
60 | }
61 | });
62 | }
63 |
64 |
65 |
66 | //create map.json
67 | var map = fis.file(root, (ns ? ns + '-' : '') + 'map.json');
68 | map.useHash = false;
69 | map.setContent(JSON.stringify(ret.map), null, opt.optimize ? null : 4);
70 | ret.pkg[map.subpath] = map;
71 | //create data.json
72 | if (settings['create'] && settings['create'].indexOf('data.json') != -1) {
73 | allRet = ret;
74 | genData(opt);
75 | }
76 | };
77 |
--------------------------------------------------------------------------------
/test/ut/ext_map/webapp/webapp.php:
--------------------------------------------------------------------------------
1 | array(),
10 | 'time'=> 0,
11 | 'error'=> ''
12 | );//资源对象
13 |
14 | $g_start = microtime(true);
15 |
16 | //如果请求data
17 | $diff = $_GET['diff'];
18 |
19 | if (!$diff) {
20 | $g_res_obj = array(
21 | 'data' => '{}',
22 | );
23 | } else {
24 | $diffs = explode('|', $diff);
25 | $g_res_obj['data'] = Bd_Webapp_Reader::getData($diffs);
26 | Bd_Webapp_Reader::setHeader();
27 | $g_end = microtime(true);
28 | $g_res_obj['time'] = round(($g_end - $g_start)*1000);
29 |
30 | echo json_encode($g_res_obj);
31 | }
32 |
33 |
34 | class Bd_Webapp_Reader{
35 | public static $resMap = array();
36 | public static function setHeader(){
37 | //CORS header
38 | if(preg_match('/\([^)]*OS\s*[^)]*6/i',$_SERVER['HTTP_USER_AGENT'])){
39 | header('Cache-Control:no-cache,no-store,must-revalidate');
40 | }
41 | header("Content-type: text/javascript");
42 | //Gzip压缩 ,不能同时使用 ob_gzhandler() 和 zlib.output_compression。
43 | if (Extension_Loaded('zlib') && !ini_get('zlib.output_compression')){
44 | //解决PHP开启Gzip页面没有输出的故障
45 | if (strnatcasecmp(PHP_VERSION, '5.3') <= 0 && ini_get('output_buffering') == "4096"){
46 | ini_set('output_buffering', 0);
47 | }
48 | //如果zlib扩展已经加载到PHP中,用php的内置压缩函数
49 | ob_start('ob_gzhandler');
50 | }
51 | }
52 | /**
53 | * @static
54 | * @param $packageName
55 | * @return string
56 | */
57 | private static function getPath($packageName){
58 | $tokens = explode('_', $packageName, 2);
59 | $moduleName = $tokens[0];
60 | return WP_DATA_PATH . '/' . $moduleName . '/' . $packageName;
61 | }
62 |
63 | /*
64 | *获取资源数据
65 | */
66 | public static function getData($diff_list){
67 | $data = array();
68 | foreach ($diff_list as $id) {
69 | if ($pos = strpos($id, ':')) {
70 | $namespace = substr($id, 0, $pos);
71 | if (isset(self::$resMap[$namespace]) || self::register($namespace)) {
72 | $resMap = self::$resMap[$namespace];
73 | $data[$id] = $resMap[$id];
74 | } else {
75 | //error
76 | }
77 | }
78 | }
79 |
80 | return $data;
81 | }
82 |
83 | public static function register($namespace) {
84 | $path = WP_DATA_PATH . '/' . $namespace . '-data.json';
85 | if (is_file($path)) {
86 | self::$resMap[$namespace] = json_decode(file_get_contents($path), true);
87 | return true;
88 | }
89 | return false;
90 | }
91 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/compiler.widget.php:
--------------------------------------------------------------------------------
1 | smarty);';
44 | $strCode .= 'if(isset($_tpl_path)){';
45 | if($bHasCall){
46 | $strCode .= '$_smarty_tpl->getSubTemplate($_tpl_path, $_smarty_tpl->cache_id, $_smarty_tpl->compile_id, $_smarty_tpl->caching, $_smarty_tpl->cache_lifetime, ' . $strFuncParams . ', Smarty::SCOPE_LOCAL);';
47 | $strCode .= 'if(is_callable('. $strTplFuncName . ')){';
48 | $strCode .= $strCallTplFunc;
49 | $strCode .= '}else{';
50 | $strCode .= 'trigger_error(\'missing function define "\'.' . $strTplFuncName . '.\'" in tpl "\'.$_tpl_path.\'"\', E_USER_ERROR);';
51 | $strCode .= '}';
52 | } else {
53 | $strCode .= 'echo $_smarty_tpl->getSubTemplate($_tpl_path, $_smarty_tpl->cache_id, $_smarty_tpl->compile_id, $_smarty_tpl->caching, $_smarty_tpl->cache_lifetime, ' . $strFuncParams . ', Smarty::SCOPE_LOCAL);';
54 | }
55 | $strCode .= '}else{';
56 | $strCode .= 'trigger_error(\'unable to locale resource "\'.' . $strName . '.\'"\', E_USER_ERROR);';
57 | $strCode .= '}';
58 | $strCode .= 'FISPagelet::load('.$strName.', $_smarty_tpl->smarty);';
59 | $strCode .= '}';
60 | $strCode .= 'FISPagelet::end();';
61 | } else {
62 | trigger_error('undefined widget name in file "' . $smarty->_current_file . '"', E_USER_ERROR);
63 | }
64 |
65 | if($bHasCall){
66 | $strCode .= '}';
67 | }
68 |
69 | $strCode .= '?>';
70 | return $strCode;
71 | }
72 |
73 | function getWidgetStrCode($path, $arrParams){
74 | $strFuncParams = getFuncParams($arrParams);
75 | $path = trim($path,"\"");
76 | $fn = '"smarty_template_function_fis_' . strtr(substr($path, 0, strrpos($path, '/')), '/', '_') . '"';
77 | $strCode = 'getSubTemplate("' . $path . '", $_smarty_tpl->cache_id, $_smarty_tpl->compile_id, null, null, '. $strFuncParams.', Smarty::SCOPE_LOCAL);';
82 | $strCode .= 'if(is_callable(' . $fn .')){';
83 | $strCode .= 'return call_user_func('. $fn . ',$_smarty_tpl,' . $strFuncParams . ');';
84 | $strCode .= '}else{';
85 | $strCode .= 'echo $fis_widget_output;';
86 | $strCode .= '}';
87 | $strCode .= '}?>';
88 | return $strCode;
89 | }
90 |
91 | function getFuncParams($arrParams){
92 | $arrFuncParams = array();
93 | foreach ($arrParams as $_key => $_value) {
94 | if (is_int($_key)) {
95 | $arrFuncParams[] = "$_key=>$_value";
96 | } else {
97 | $arrFuncParams[] = "'$_key'=>$_value";
98 | }
99 | }
100 | $strFuncParams = 'array(' . implode(',', $arrFuncParams) . ')';
101 | return $strFuncParams;
102 | }
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/README.md:
--------------------------------------------------------------------------------
1 | #### Quickling
2 |
3 | 基于fis支持quickling的smarty插件
4 |
5 | #### Overview
6 |
7 | 插件支持两种渲染模式,
8 |
9 | + 正常渲染模式 `noscript`,不需要前端库进行控制
10 | + Pipeline渲染模式 `bigpipe`,需要前端库控制渲染
11 |
12 | -----
13 |
14 | #####`noscript` Render#####
15 |
16 | ######before######
17 |
18 | ```html
19 | {%html%}
20 | {%head%}
21 | This is a test
22 | {%/head%}
23 | {%body%}
24 | {%style%}
25 | body {
26 | background-color: #EEEEEE;
27 | }
28 | {%/style%}
29 | {%script%}
30 | console.log("foo, foo, foo");
31 | {%/script%}
32 | {%widget_block pagelet_id="pager"%}
33 | {%widget name="widget/a.tpl"%}
34 |
35 | //balabala
36 |
37 | {%/widget_block%}
38 | {%/body%}
39 | {%/html%}
40 | ```
41 |
42 | #####after#####
43 |
44 | ```html
45 |
46 |
47 | This is a test
48 |
53 |
54 |
55 |
63 |
68 |
69 |
70 | ```
71 |
72 | -------
73 |
74 | ####`bigpipe` render == `pipeline`####
75 |
76 | #####before#####
77 |
78 | ```html
79 | {%html mode="bigpipe"%}
80 | {%head%}
81 | This is a test
82 | {%/head%}
83 | {%body%}
84 | {%style%}
85 | body {
86 | background-color: #EEEEEE;
87 | }
88 | {%/style%}
89 | {%script%}
90 | console.log("foo, foo, foo");
91 | {%/script%}
92 | {%widget_block pagelet_id="pager"%}
93 | {%widget name="widget/a.tpl"%}
94 |
95 | //balabala
96 |
97 | {%/widget_block%}
98 | {%/body%}
99 | {%/html%}
100 | ```
101 |
102 | #####after#####
103 |
104 | ```html
105 |
106 |
107 | This is a test
108 |
113 |
114 |
115 |
117 |
118 |
119 |
124 |
129 |
133 |
134 |
140 |
144 |
147 | ```
148 |
149 | 如上源码(before)和运行后的代码(after);
150 |
151 | #### Detail
152 |
153 | 在smarty里面为了控制输出,使用插件代替几个html标签。
154 |
155 | | 标签 | 插件 | |
156 | |:--------------:|:-----------------------------|:-----------------------------------------|
157 | | body | compiler.body.php | 确定JS的位置 |
158 | | html | compiler.html.php | 初始化数据 |
159 | | head | compiler.head.php | 确定css安放位置 |
160 | | script | compiler.script.php | 收集内联脚本 |
161 | | style | compiler.style.php | 收集内联样式 |
162 | | title | compiler.title.php | 获取title,以便异步请求切换页面 |
163 |
164 | 在`正常模式`渲染下,js和css加载的位置,css在head关闭标签前;js在body关闭标签前。
165 |
166 | 在`pipeline`渲染下,js和css的链接给前端加载器,前端负责加载。包括html内容,前端负责渲染。
167 |
168 | + lib/FISResource.class.php 收集静态资源,算法演示
169 | + lib/FISPagelet.class.php 初始化系统,输出模式控制
170 |
171 |
172 | #### 关于部署迁移
173 |
174 | OK, 各个插件的功能都已经说过了。现在说一下部署的问题
175 |
176 | -----
177 |
178 | **纯粹的FIS 2.0项目**
179 |
180 | 把所有插件放到smarty的 plugin_dir下。
181 |
182 | -----
183 |
184 | **和fis 1.0共存?**
185 |
186 | 把所有插件放到smarty的 plugin_dir下,并删除`compiler.widget_block.php`
187 |
188 | **2.0里面有一个插件和1.0有冲突,那就是`compiler.widget_block.php`,请删除2.0里面的.**
189 |
--------------------------------------------------------------------------------
/test/ut/ext_map/static/mod-store.js:
--------------------------------------------------------------------------------
1 | /**
2 | * file: mod-store.js
3 | * ver: 1.0.0
4 | * auth: zhangjiachen@baidu.com
5 | * modify: fansekey@gmail.com
6 | * update: 2013-11-13
7 | */
8 | var require, define;
9 |
10 | (function(self) {
11 | var head = document.getElementsByTagName('head')[0],
12 | loadingMap = {},
13 | factoryMap = {},
14 | modulesMap = {},
15 | scriptsMap = {},
16 | resMap = {},
17 | pkgMap = {};
18 |
19 | function checkLocalStorage() {
20 | try {
21 | //测试local
22 | localStorage.setItem('fis_test', 'good');
23 | localStorage.removeItem('fis_test');
24 | return true;
25 | } catch (e) {
26 | return false;
27 | }
28 | }
29 | function loadByXHR(id, url, callback) {
30 | var store = localStorage
31 | , content
32 | , has
33 | , diff = []
34 | , script = ''
35 | //保存包内组件内容
36 | , scripts = [];
37 |
38 | var res = resMap[id] || {};
39 | var pkg = res.pkg;
40 | var url = '/webapp.php';
41 |
42 | if (pkg) {
43 | if ((has = store.getItem(pkg))) {
44 | has = has.split('|');
45 | for (var i = 0, len = has.length; i < len; i++) {
46 | var oldUrl = store.getItem(has[i]);
47 | if (oldUrl) {
48 | //组件内容修改了
49 | if (oldUrl != resMap[has[i]]['url']) {
50 | diff[i] = has[i];
51 | store.removeItem(oldUrl);
52 | } else {
53 | scripts[i] = store.getItem(oldUrl);
54 | }
55 | } else {
56 | diff[i] = has[i];
57 | }
58 | }
59 | } else {
60 | has = pkgMap[pkg]['has'];
61 | diff = has;
62 | }
63 | store.setItem(pkg, pkgMap[pkg]['has'].join('|'));
64 | } else {
65 | if ((script = store.getItem(res.url))) {
66 | scripts.push(script);
67 | } else {
68 | diff.push(id);
69 | }
70 | }
71 |
72 | url = url + '?diff=' + diff.join('|');
73 |
74 | function _load(url, scripts, diff, cb) {
75 | var xhr = new window.XMLHttpRequest;
76 | xhr.onreadystatechange = function() {
77 | if (xhr.readyState == 4 ) {
78 | if (xhr.status == 200) {
79 | var obj = eval('(' + xhr.responseText + ')');
80 | if (!obj) {
81 | return;
82 | }
83 | var data = obj['data'];
84 | for (var resId in data) {
85 | if (data.hasOwnProperty(resId)) {
86 | store.setItem(data[resId]['uri'], data[resId]['content']);
87 | store.setItem(resId, data[resId]['uri']);
88 | scripts.push(data[resId]['content']);
89 | }
90 | }
91 | cb(scripts.join(''));
92 | } else {
93 | throw new Error('A unkown error occurred.');
94 | }
95 | }
96 | };
97 | xhr.open('get', url);
98 | xhr.send(null);
99 | }
100 |
101 | if (diff.length == 0) {
102 | callback(scripts.join(''));
103 | } else {
104 | _load(url, scripts, diff, callback);
105 | }
106 | }
107 |
108 |
109 |
110 | function createScript(url, onerror) {
111 | if (url in scriptsMap) return;
112 | scriptsMap[url] = true;
113 |
114 | var script = document.createElement('script');
115 | if (onerror) {
116 | var tid = setTimeout(onerror, require.timeout);
117 |
118 | script.onerror = function() {
119 | clearTimeout(tid);
120 | onerror();
121 | };
122 |
123 | script.onreadystatechange = function() {
124 | if (this.readyState == 'complete') {
125 | clearTimeout(tid);
126 | }
127 | }
128 | }
129 | script.type = 'text/javascript';
130 | script.src = url;
131 | head.appendChild(script);
132 | return script;
133 | }
134 |
135 |
136 | function loadScript(id, callback, onerror) {
137 | var queue = loadingMap[id] || (loadingMap[id] = []);
138 | queue.push(callback);
139 |
140 | //
141 | // resource map query
142 | //
143 | var res = resMap[id] || {};
144 | var pkg = res.pkg;
145 | var url;
146 |
147 | if (pkg) {
148 | url = pkgMap[pkg].url;
149 | } else {
150 | url = res.url || id;
151 | }
152 |
153 | if (!window.XMLHttpRequest || !checkLocalStorage()) {
154 | createScript(url, onerror && function() {
155 | onerror(id);
156 | });
157 | } else {
158 | if (! (url in scriptsMap)) {
159 | scriptsMap[url] = true;
160 | loadByXHR(id, url, function(content) {
161 | script = document.createElement('script');
162 | script.type = 'text/javascript';
163 | script.innerHTML = content;
164 | head.appendChild(script);
165 | });
166 | }
167 | }
168 | }
169 |
170 | define = function(id, factory) {
171 | factoryMap[id] = factory;
172 |
173 | var queue = loadingMap[id];
174 | if (queue) {
175 | for(var i = queue.length - 1; i >= 0; --i) {
176 | queue[i]();
177 | }
178 | delete loadingMap[id];
179 | }
180 | };
181 |
182 | require = function(id) {
183 | id = require.alias(id);
184 |
185 | var mod = modulesMap[id];
186 | if (mod) {
187 | return mod.exports;
188 | }
189 |
190 | //
191 | // init module
192 | //
193 | var factory = factoryMap[id];
194 | if (!factory) {
195 | throw Error('Cannot find module `' + id + '`');
196 | }
197 |
198 | mod = modulesMap[id] = {
199 | exports: {}
200 | };
201 |
202 | //
203 | // factory: function OR value
204 | //
205 | var ret = (typeof factory == 'function')
206 | ? factory.apply(mod, [require, mod.exports, mod])
207 | : factory;
208 |
209 | if (ret) {
210 | mod.exports = ret;
211 | }
212 | return mod.exports;
213 | };
214 |
215 | require.async = function(names, onload, onerror) {
216 | if (typeof names == 'string') {
217 | names = [names];
218 | }
219 |
220 | for(var i = names.length - 1; i >= 0; --i) {
221 | names[i] = require.alias(names[i]);
222 | }
223 |
224 | var needMap = {};
225 | var needNum = 0;
226 |
227 | function findNeed(depArr) {
228 | for(var i = depArr.length - 1; i >= 0; --i) {
229 | //
230 | // skip loading or loaded
231 | //
232 | var dep = depArr[i];
233 | if (dep in factoryMap || dep in needMap) {
234 | continue;
235 | }
236 |
237 | needMap[dep] = true;
238 | needNum++;
239 | loadScript(dep, updateNeed, onerror);
240 |
241 | var child = resMap[dep];
242 | if (child && 'deps' in child) {
243 | findNeed(child.deps);
244 | }
245 | }
246 | }
247 |
248 | function updateNeed() {
249 | if (0 == needNum--) {
250 | var i, n, args = [];
251 | for(i = 0, n = names.length; i < n; ++i) {
252 | args[i] = require(names[i]);
253 | }
254 | onload && onload.apply(self, args);
255 | }
256 | }
257 |
258 | findNeed(names);
259 | updateNeed();
260 | };
261 |
262 | require.resourceMap = function(obj) {
263 | var k, col;
264 |
265 | // merge `res` & `pkg` fields
266 | col = obj.res;
267 | for(k in col) {
268 | if (col.hasOwnProperty(k)) {
269 | resMap[k] = col[k];
270 | }
271 | }
272 |
273 | col = obj.pkg;
274 | for(k in col) {
275 | if (col.hasOwnProperty(k)) {
276 | pkgMap[k] = col[k];
277 | }
278 | }
279 | };
280 |
281 | require.loadJs = function(url) {
282 | createScript(url);
283 | };
284 |
285 | require.loadCss = function(cfg) {
286 | if (cfg.content) {
287 | var sty = document.createElement('style');
288 | sty.type = 'text/css';
289 |
290 | if (sty.styleSheet) { // IE
291 | sty.styleSheet.cssText = cfg.content;
292 | } else {
293 | sty.innerHTML = cfg.content;
294 | }
295 | head.appendChild(sty);
296 | }
297 | else if (cfg.url) {
298 | var link = document.createElement('link');
299 | link.href = cfg.url;
300 | link.rel = 'stylesheet';
301 | link.type = 'text/css';
302 | head.appendChild(link);
303 | }
304 | };
305 |
306 |
307 | require.alias = function(id) {return id};
308 |
309 | require.timeout = 5000;
310 |
311 | define.amd = {
312 | 'jQuery': true,
313 | 'version': '1.0.0'
314 | };
315 |
316 | })(this);
317 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/lib/FISResource.class.php:
--------------------------------------------------------------------------------
1 | $val) {
59 | foreach ($val as $id => $info) {
60 | unset(self::$arrLoaded[$id]);
61 | unset(self::$arrAsyncDeleted[$id]);
62 | }
63 | }
64 | $ret['async'] = self::getResourceMap(self::$arrWidgetRequireAsync);
65 | }
66 |
67 | foreach (self::$arrWidgetStatic as $key => $val) {
68 | foreach ($val as $uri) {
69 | foreach (array_keys(self::$arrLoaded, $uri) as $id) {
70 | unset(self::$arrLoaded[$id]);
71 | unset(self::$arrAsyncDeleted[$id]);
72 | }
73 | }
74 | }
75 | //}}}
76 | if (self::$arrWidgetStatic['js']) {
77 | $ret['js'] = self::$arrWidgetStatic['js'];
78 | }
79 | if (self::$arrWidgetStatic['css']) {
80 | $ret['css'] = self::$arrWidgetStatic['css'];
81 | }
82 | if (self::$arrWidgetScript) {
83 | $ret['script'] = self::$arrWidgetScript;
84 | }
85 | if (self::$arrWidgetStyle) {
86 | $ret['style'] = self::$arrWidgetStyle;
87 | }
88 | return $ret;
89 | }
90 |
91 | public static function addStatic($uri, $type) {
92 | if (self::$isInnerWidget) {
93 | self::$arrWidgetStatic[$type][] = $uri;
94 | } else {
95 | self::$arrStaticCollection[$type][] = $uri;
96 | }
97 | }
98 |
99 | public static function addAsync($id, $info, $type) {
100 | if (self::$isInnerWidget) {
101 | self::$arrWidgetRequireAsync[$type][$id] = $info;
102 | } else {
103 | self::$arrRequireAsyncCollection[$type][$id] = $info;
104 | }
105 | }
106 |
107 | public static function delAsync($id, $type) {
108 | if (self::$isInnerWidget) {
109 | unset(self::$arrWidgetRequireAsync[$type][$id]);
110 | } else {
111 | unset(self::$arrRequireAsyncCollection[$type][$id]);
112 | }
113 | }
114 |
115 | public static function getAsync($id, $type) {
116 | if (self::$isInnerWidget) {
117 | return self::$arrWidgetRequireAsync[$type][$id];
118 | } else {
119 | return self::$arrRequireAsyncCollection[$type][$id];
120 | }
121 | }
122 |
123 | //设置framewok mod.js
124 | public static function setFramework($strFramework) {
125 | self::$framework = $strFramework;
126 | }
127 |
128 | public static function getFramework() {
129 | return self::$framework;
130 | }
131 |
132 | public static function addScriptPool($code) {
133 | if (!self::$isInnerWidget) {
134 | self::$arrScriptPool[] = $code;
135 | } else {
136 | self::$arrWidgetScript[] = $code;
137 | }
138 | }
139 |
140 | public static function addStylePool($code) {
141 | if (!self::$isInnerWidget) {
142 | self::$arrStylePool[] = $code;
143 | } else {
144 | self::$arrWidgetStyle[] = $code;
145 | }
146 | }
147 |
148 | public static function getArrStaticCollection() {
149 | //内嵌脚本
150 | if (self::$arrScriptPool) {
151 | self::$arrStaticCollection['script'] = self::$arrScriptPool;
152 | }
153 |
154 | if (self::$arrStylePool) {
155 | self::$arrStaticCollection['style'] = self::$arrStylePool;
156 | }
157 |
158 | //异步脚本
159 | if (self::$arrRequireAsyncCollection) {
160 | self::$arrStaticCollection['async'] = self::getResourceMap(self::$arrRequireAsyncCollection);
161 | }
162 | unset(self::$arrStaticCollection['tpl']);
163 | return self::$arrStaticCollection;
164 | }
165 |
166 | //获取异步js资源集合,变为json格式的resourcemap
167 | public static function getResourceMap($arr, $cdn = '') {
168 | $ret = '';
169 | $arrResourceMap = array();
170 | if (isset($arr['res'])) {
171 | foreach ($arr['res'] as $id => $arrRes) {
172 | $deps = array();
173 | if (!empty($arrRes['deps'])) {
174 | foreach ($arrRes['deps'] as $strName) {
175 | if (preg_match('/\.js$/i', $strName)) {
176 | $deps[] = $strName;
177 | }
178 | }
179 | }
180 |
181 | $arrResourceMap['res'][$id] = array(
182 | 'url' => $cdn . $arrRes['uri'],
183 | );
184 |
185 | if (!empty($arrRes['pkg'])) {
186 | $arrResourceMap['res'][$id]['pkg'] = $arrRes['pkg'];
187 | //如果包含到了某一个包,则模块的url是多余的
188 | if (!isset($_GET['fis_debug'])) {
189 | //@TODO
190 | //unset($arrResourceMap['res'][$id]['url']);
191 | }
192 | }
193 |
194 | if (!empty($deps)) {
195 | $arrResourceMap['res'][$id]['deps'] = $deps;
196 | }
197 | }
198 | }
199 | if (isset($arr['pkg'])) {
200 | foreach ($arr['pkg'] as $id => $arrRes) {
201 | $arrResourceMap['pkg'][$id] = array(
202 | 'url' => $cdn . $arrRes['uri'],
203 | 'has' => $arrRes['has']
204 | );
205 | }
206 | }
207 | if (!empty($arrResourceMap)) {
208 | $ret = $arrResourceMap;
209 | }
210 | return $ret;
211 | }
212 |
213 | //获取命名空间的map.json
214 | public static function register($strNamespace, $smarty){
215 | if($strNamespace === '__global__'){
216 | $strMapName = 'map';
217 | } else {
218 | $strMapName = $strNamespace . '-map';
219 | }
220 | $arrConfigDir = $smarty->getConfigDir();
221 | foreach ($arrConfigDir as $strDir) {
222 | $strPath = preg_replace('/[\\/\\\\]+/', '/', $strDir . '/' . $strMapName);
223 | if(is_file($strPath . '.php')){
224 | self::$arrMap[$strNamespace] = require($strPath . '.php');
225 | return true;
226 | } else if (is_file($strPath . '.json')) {
227 | self::$arrMap[$strNamespace] = json_decode(file_get_contents($strPath . '.json'), true);
228 | return true;
229 | }
230 | }
231 | return false;
232 | }
233 |
234 | public static function getUri($strName, $smarty) {
235 | $intPos = strpos($strName, ':');
236 | if($intPos === false){
237 | $strNamespace = '__global__';
238 | } else {
239 | $strNamespace = substr($strName, 0, $intPos);
240 | }
241 | if(isset(self::$arrMap[$strNamespace]) || self::register($strNamespace, $smarty)) {
242 | $arrMap = &self::$arrMap[$strNamespace];
243 | $arrRes = &$arrMap['res'][$strName];
244 | if (isset($arrRes)) {
245 | return $arrRes['uri'];
246 | }
247 | }
248 | }
249 |
250 | /**
251 | * 分析组件依赖
252 | * @param array $arrRes 组件信息
253 | * @param Object $smarty smarty对象
254 | * @param bool $async 是否异步
255 | */
256 | private static function loadDeps($arrRes, $smarty, $async) {
257 | if(isset($arrRes['deps'])){
258 | foreach ($arrRes['deps'] as $strDep) {
259 | self::load($strDep, $smarty, $async);
260 | }
261 | }
262 |
263 | //require.async
264 | if (isset($arrRes['extras']) && isset($arrRes['extras']['async'])) {
265 | foreach ($arrRes['extras']['async'] as $uri) {
266 | self::load($uri, $smarty, true);
267 | }
268 | }
269 | }
270 |
271 | /**
272 | * 已经分析到的组件在后续被同步使用时在异步组里删除。
273 | * @param $strName
274 | * @return bool
275 | */
276 | private static function delAsyncDeps($strName) {
277 | if (isset(self::$arrAsyncDeleted[$strName])) {
278 | return true;
279 | } else {
280 | self::$arrAsyncDeleted[$strName] = true;
281 | $arrRes = self::getAsync($strName, 'res');
282 | if ($arrRes['pkg']) {
283 | $arrPkg = self::getAsync($arrRes['pkg'], 'pkg');
284 | if ($arrPkg) {
285 | self::addStatic($arrPkg['uri'], 'js');
286 | self::delAsync($arrRes['pkg'], 'pkg');
287 | foreach ($arrPkg['has'] as $strHas) {
288 | self::$arrLoaded[$strHas] = $arrPkg['uri'];
289 | if (self::getAsync($strHas, 'res')) {
290 | self::delAsyncDeps($strHas);
291 | }
292 | }
293 | } else {
294 | self::delAsync($strName, 'res');
295 | }
296 | } else {
297 | //已经分析过的并且在其他文件里同步加载的组件,重新收集在同步输出组
298 | $res = self::getAsync($strName, 'res');
299 | self::addStatic($res['uri'], 'js');
300 | self::$arrLoaded[$strName] = $res['uri'];
301 | self::delAsync($strName, 'res');
302 | }
303 | if ($arrRes['deps']) {
304 | foreach ($arrRes['deps'] as $strDep) {
305 | //if (isset(self::$arrRequireAsyncCollection['res'][$strDep])) {
306 | if (self::getAsync($strDep, 'res')) {
307 | self::delAsyncDeps($strDep);
308 | }
309 | }
310 | }
311 | }
312 | }
313 |
314 | /**
315 | * 加载组件以及组件依赖
316 | * @param $strName id
317 | * @param $smarty smarty对象
318 | * @param bool $async 是否为异步组件(only JS)
319 | * @return mixed
320 | */
321 | public static function load($strName, $smarty, $async = false){
322 | if(isset(self::$arrLoaded[$strName])) {
323 | //同步组件优先级比异步组件高
324 | if (!$async && self::getAsync($strName, 'res')) {
325 | self::delAsyncDeps($strName);
326 | }
327 | return self::$arrLoaded[$strName];
328 | } else {
329 | $intPos = strpos($strName, ':');
330 | if($intPos === false){
331 | $strNamespace = '__global__';
332 | } else {
333 | $strNamespace = substr($strName, 0, $intPos);
334 | }
335 | if(isset(self::$arrMap[$strNamespace]) || self::register($strNamespace, $smarty)){
336 | $arrMap = &self::$arrMap[$strNamespace];
337 | $arrRes = &$arrMap['res'][$strName];
338 | $arrPkg = null;
339 | $arrPkgHas = array();
340 | if(isset($arrRes)) {
341 | if(!array_key_exists('fis_debug', $_GET) && isset($arrRes['pkg'])){
342 | $arrPkg = &$arrMap['pkg'][$arrRes['pkg']];
343 | $strURI = $arrPkg['uri'];
344 | foreach ($arrPkg['has'] as $strResId) {
345 | self::$arrLoaded[$strResId] = $strURI;
346 | }
347 | foreach ($arrPkg['has'] as $strResId) {
348 | $arrHasRes = &$arrMap['res'][$strResId];
349 | if ($arrHasRes) {
350 | $arrPkgHas[$strResId] = $arrHasRes;
351 | self::loadDeps($arrHasRes, $smarty, $async);
352 | }
353 | }
354 | } else {
355 | $strURI = $arrRes['uri'];
356 | self::$arrLoaded[$strName] = $strURI;
357 | self::loadDeps($arrRes, $smarty, $async);
358 | }
359 |
360 | if ($async && $arrRes['type'] === 'js') {
361 | if ($arrPkg) {
362 | self::addAsync($arrRes['pkg'], $arrPkg, 'pkg');
363 | foreach ($arrPkgHas as $id => $val) {
364 | self::addAsync($id, $val, 'res');
365 | }
366 | } else {
367 | self::addAsync($strName, $arrRes, 'res');
368 | }
369 | } else {
370 | self::addStatic($strURI, $arrRes['type']);
371 | }
372 | return $strURI;
373 | } else {
374 | self::triggerError($strName, 'undefined resource "' . $strName . '"', E_USER_NOTICE);
375 | }
376 | } else {
377 | self::triggerError($strName, 'missing map file of "' . $strNamespace . '"', E_USER_NOTICE);
378 | }
379 | }
380 | self::triggerError($strName, 'unknown resource "' . $strName . '" load error', E_USER_NOTICE);
381 | }
382 |
383 | /**
384 | * 用户代码自定义js组件,其没有对应的文件
385 | * 只有有后缀的组件找不到时进行报错
386 | * @param $strName 组件ID
387 | * @param $strMessage 错误信息
388 | * @param $errorLevel 错误level
389 | */
390 | private static function triggerError($strName, $strMessage, $errorLevel) {
391 | $arrExt = array(
392 | 'js',
393 | 'css',
394 | 'tpl',
395 | 'html',
396 | 'xhtml',
397 | );
398 | if (preg_match('/\.('.implode('|', $arrExt).')$/', $strName)) {
399 | trigger_error(date('Y-m-d H:i:s') . ' ' . $strName . ' ' . $strMessage, $errorLevel);
400 | }
401 | }
402 |
403 | }
404 |
--------------------------------------------------------------------------------
/test/ut/ext_map/plugin/lib/FISPagelet.class.php:
--------------------------------------------------------------------------------
1 | ';
77 | const JS_SCRIPT_HOOK = '';
78 |
79 | const MODE_NOSCRIPT = 0;
80 | const MODE_QUICKLING = 1;
81 | const MODE_BIGPIPE = 2;
82 |
83 | /**
84 | * 收集widget内部使用的静态资源
85 | * array(
86 | * 0: array(), 1: array(), 2: array()
87 | * )
88 | * @var array
89 | */
90 | static protected $inner_widget = array(
91 | array(),
92 | array(),
93 | array()
94 | );
95 |
96 | /**
97 | * 记录pagelet_id
98 | * @var string
99 | */
100 | static private $_pagelet_id = null;
101 |
102 | static private $_session_id = 0;
103 | static private $_context = array();
104 | static private $_contextMap = array();
105 | static private $_pagelets = array();
106 | static private $_title = '';
107 | static private $_pagelet_group = array();
108 | /**
109 | * 解析模式
110 | * @var number
111 | */
112 | static protected $mode = null;
113 |
114 | static protected $default_mode = null;
115 |
116 | /**
117 | * 某一个widget使用那种模式渲染
118 | * @var number
119 | */
120 | static protected $widget_mode;
121 |
122 | static protected $filter;
123 |
124 | static public $cp;
125 | static public $arrEmbeded = array();
126 |
127 | static public $cdn;
128 |
129 | /**
130 | * 设置渲染模式及其需要渲染的widget
131 | * @param $default_mode string 设置默认渲染模式
132 | */
133 | static public function init($default_mode) {
134 | if (is_string($default_mode)
135 | && in_array(
136 | self::_parseMode($default_mode),
137 | array(self::MODE_BIGPIPE, self::MODE_NOSCRIPT))
138 | ) {
139 | self::$default_mode = self::_parseMode($default_mode);
140 | } else {
141 | self::$default_mode = self::MODE_NOSCRIPT;
142 | }
143 |
144 | $is_ajax = isset($_SERVER['HTTP_X_REQUESTED_WITH'])
145 | && (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
146 | if ($is_ajax) {
147 | self::setMode(self::MODE_QUICKLING);
148 | } else {
149 | self::setMode(self::$default_mode);
150 | }
151 | self::setFilter($_GET['pagelets']);
152 | }
153 |
154 | static public function setMode($mode){
155 | if (self::$mode === null) {
156 | self::$mode = isset($mode) ? intval($mode) : 1;
157 | }
158 | }
159 |
160 | static public function setFilter($ids) {
161 | if (!is_array($ids)) {
162 | $ids = array($ids);
163 | }
164 | foreach ($ids as $id) {
165 | self::$filter[$id] = true;
166 | }
167 | }
168 |
169 | static public function setTitle($title) {
170 | self::$_title = $title;
171 | }
172 |
173 | static public function getUri($strName, $smarty) {
174 | return FISResource::getUri($strName, $smarty);
175 | }
176 |
177 | static public function addScript($code) {
178 | if(self::$_context['hit'] || self::$mode == self::$default_mode){
179 | FISResource::addScriptPool($code);
180 | }
181 | }
182 |
183 | static public function addStyle($code) {
184 | if(self::$_context['hit'] || self::$mode == self::$default_mode){
185 | FISResource::addStylePool($code);
186 | }
187 | }
188 |
189 | public static function cssHook() {
190 | return self::CSS_LINKS_HOOK;
191 | }
192 |
193 | public static function jsHook() {
194 | return self::JS_SCRIPT_HOOK;
195 | }
196 |
197 | static function load($str_name, $smarty, $async = false) {
198 | if(self::$_context['hit'] || self::$mode == self::$default_mode){
199 | FISResource::load($str_name, $smarty, $async);
200 | }
201 | }
202 |
203 | static private function _parseMode($str_mode) {
204 | $str_mode = strtoupper($str_mode);
205 | $mode = self::$mode;
206 | switch($str_mode) {
207 | case 'BIGPIPE':
208 | $mode = self::MODE_BIGPIPE;
209 | break;
210 | case 'QUICKLING':
211 | $mode = self::MODE_QUICKLING;
212 | break;
213 | case 'NOSCRIPT':
214 | $mode = self::MODE_NOSCRIPT;
215 | break;
216 | }
217 | return $mode;
218 | }
219 | /**
220 | * WIDGET START
221 | * 解析参数,收集widget所用到的静态资源
222 | * @param $id
223 | * @param $mode
224 | * @param $group
225 | * @return bool
226 | */
227 | static public function start($id, $mode = null, $group = null) {
228 | $has_parent = !empty(self::$_context);
229 |
230 | //这个是用来判断是否widget是async加载的。
231 | $special_flag = false;
232 | if ($mode !== null) {
233 | $special_flag = true;
234 | }
235 |
236 | if ($mode) {
237 | self::$widget_mode = self::_parseMode($mode);
238 | } else {
239 | self::$widget_mode = self::$mode;
240 | }
241 | self::$_pagelet_id = $id;
242 | $parent_id = $has_parent ? self::$_context['id'] : '';
243 | $qk_flag = self::$mode == self::MODE_QUICKLING ? '_qk_' : '';
244 | $id = empty($id) ? '__elm_' . $parent_id . '_' . $qk_flag . self::$_session_id ++ : $id;
245 |
246 | //widget是否命中,默认命中
247 | $hit = true;
248 |
249 | switch(self::$widget_mode) {
250 | case self::MODE_NOSCRIPT:
251 | if (self::$_pagelet_id) {
252 | echo '';
253 | }
254 | break;
255 | case self::MODE_QUICKLING:
256 | $hit = self::$filter[$id];
257 | case self::MODE_BIGPIPE:
258 | $context = array( 'id' => $id, 'async' => false);
259 | //widget调用时mode='quickling',so,打出异步加载代码
260 | if ($special_flag && !$hit) {
261 | if (!$group) {
262 | echo '
';
265 | } else {
266 | if (isset(self::$_pagelet_group[$group])) {
267 | self::$_pagelet_group[$group][] = $id;
268 | } else {
269 | self::$_pagelet_group[$group] = array($id);
270 | echo "";
271 | }
272 | }
273 | $context['async'] = true;
274 | }
275 |
276 | $parent = self::$_context;
277 | if(!empty($parent)) {
278 | $parent_id = $parent['id'];
279 | self::$_contextMap[$parent_id] = $parent;
280 | $context['parent_id'] = $parent_id;
281 | if($parent['hit'] && !$special_flag) {
282 | $hit = true;
283 | } else if($hit && self::$mode === self::MODE_QUICKLING){
284 | unset($context['parent_id']);
285 | }
286 | }
287 | $context['hit'] = $hit;
288 |
289 |
290 | self::$_context = $context;
291 |
292 | if (empty($parent) && $hit) {
293 | FISResource::widgetStart();
294 | } else if (!empty($parent) && !$parent['hit'] && $hit) {
295 | FISResource::widgetStart();
296 | }
297 | echo '
';
298 | ob_start();
299 | break;
300 | }
301 | return $hit;
302 | }
303 |
304 | /**
305 | * WIDGET END
306 | * 收集html,收集静态资源
307 | */
308 | static public function end() {
309 | $ret = true;
310 | if (self::$widget_mode !== self::MODE_NOSCRIPT) {
311 | $html = ob_get_clean();
312 | $pagelet = self::$_context;
313 | //end
314 | if (isset($pagelet['parent_id'])) {
315 | $parent = self::$_contextMap[$pagelet['parent_id']];
316 | if (!$parent['hit'] && $pagelet['hit']) {
317 | self::$inner_widget[self::$widget_mode][] = FISResource::widgetEnd();
318 |
319 | }
320 | } else {
321 | if ($pagelet['hit']) {
322 | self::$inner_widget[self::$widget_mode][] = FISResource::widgetEnd();
323 | }
324 | }
325 |
326 | if($pagelet['hit'] && !$pagelet['async']){
327 | unset($pagelet['hit']);
328 | unset($pagelet['async']);
329 | $pagelet['html'] = $html;
330 | self::$_pagelets[] = &$pagelet;
331 | unset($pagelet);
332 | } else {
333 | $ret = false;
334 | }
335 | $parent_id = self::$_context['parent_id'];
336 | if(isset($parent_id)){
337 | self::$_context = self::$_contextMap[$parent_id];
338 | unset(self::$_contextMap[$parent_id]);
339 | } else {
340 | self::$_context = null;
341 | }
342 | self::$widget_mode = self::$mode;
343 | echo '
';
344 | } else {
345 | if (self::$_pagelet_id) {
346 | echo '
';
347 | }
348 | }
349 |
350 | return $ret;
351 | }
352 |
353 | /**
354 | * 设置cdn
355 | */
356 | static public function setCdn($cdn) {
357 | $cdn = trim($cdn);
358 | self::$cdn = $cdn;
359 | }
360 |
361 | static public function getCdn() {
362 | return self::$cdn;
363 | }
364 |
365 |
366 | /**
367 | * 渲染静态资源
368 | * @param $html
369 | * @param $arr
370 | * @param bool $clean_hook
371 | * @return mixed
372 | */
373 | static public function renderStatic($html, $arr, $clean_hook = false) {
374 | if (!empty($arr)) {
375 | $code = '';
376 | $resource_map = $arr['async'];
377 | $loadModJs = (FISResource::getFramework() && ($arr['js'] || $resource_map));
378 | if ($loadModJs) {
379 | foreach ($arr['js'] as $js) {
380 | $code .= '';
381 | if ($js == FISResource::getFramework()) {
382 | if ($resource_map) {
383 | $code .= '';
386 | }
387 | }
388 | }
389 | }
390 |
391 | if (!empty($arr['script'])) {
392 | $code .= '';
397 | }
398 | $html = str_replace(self::JS_SCRIPT_HOOK, $code . self::JS_SCRIPT_HOOK, $html);
399 | $code = '';
400 | if (!empty($arr['css'])) {
401 | $code = '';
404 | }
405 | if (!empty($arr['style'])) {
406 | $code .= '';
411 | }
412 | //替换
413 | $html = str_replace(self::CSS_LINKS_HOOK, $code . self::CSS_LINKS_HOOK, $html);
414 | }
415 | if ($clean_hook) {
416 | $html = str_replace(array(self::CSS_LINKS_HOOK, self::JS_SCRIPT_HOOK), '', $html);
417 | }
418 | return $html;
419 | }
420 |
421 | /**
422 | * @param $html string html页面内容
423 | * @return mixed
424 | */
425 | static public function insertPageletGroup($html) {
426 | if (empty(self::$_pagelet_group)) {
427 | return $html;
428 | }
429 | $search = array();
430 | $replace = array();
431 | foreach (self::$_pagelet_group as $group => $ids) {
432 | $search[] = '';
433 | $replace[] = '';
436 | }
437 | return str_replace($search, $replace, $html);
438 | }
439 |
440 | static public function display($html) {
441 | $html = self::insertPageletGroup($html);
442 | $pagelets = self::$_pagelets;
443 | $mode = self::$mode;
444 | $res = array(
445 | 'js' => array(),
446 | 'css' => array(),
447 | 'script' => array(),
448 | 'style' => array(),
449 | 'async' => array(
450 | 'res' => array(),
451 | 'pkg' => array()
452 | )
453 | );
454 |
455 | //{{{
456 | foreach (self::$inner_widget[$mode] as $item) {
457 | foreach ($res as $key => $val) {
458 | if (isset($item[$key]) && is_array($item[$key])) {
459 | if ($key != 'async') {
460 | $arr = array_merge($res[$key], $item[$key]);
461 | $arr = array_merge(array_unique($arr));
462 | } else {
463 | $arr = array(
464 | 'res' => array_merge($res['async']['res'], (array)$item['async']['res']),
465 | 'pkg' => array_merge($res['async']['pkg'], (array)$item['async']['pkg'])
466 | );
467 | }
468 | //合并收集
469 | $res[$key] = $arr;
470 | }
471 | }
472 | }
473 | //if empty, unset it!
474 | foreach ($res as $key => $val) {
475 | if (empty($val)) {
476 | unset($res[$key]);
477 | }
478 | }
479 | //}}}
480 |
481 | //add cdn
482 | foreach ((array)$res['js'] as $key => $js) {
483 | $res['js'][$key] = self::getCdn() . $js;
484 | }
485 |
486 | foreach ((array)$res['css'] as $key => $css) {
487 | $res['css'][$key] = self::getCdn() . $css;
488 | }
489 |
490 | //tpl信息没有必要打到页面
491 | switch($mode) {
492 | case self::MODE_NOSCRIPT:
493 | //渲染widget以外静态文件
494 | $all_static = FISResource::getArrStaticCollection();
495 | $html = self::renderStatic(
496 | $html,
497 | $all_static,
498 | true
499 | );
500 | break;
501 | case self::MODE_QUICKLING:
502 | header('Content-Type: text/json;charset: utf-8');
503 | if ($res['script']) {
504 | $res['script'] = convertToUtf8(implode("\n", $res['script']));
505 | }
506 | if ($res['style']) {
507 | $res['style'] = convertToUtf8(implode("\n", $res['style']));
508 | }
509 | foreach ($pagelets as &$pagelet) {
510 | $pagelet['html'] = convertToUtf8(self::insertPageletGroup($pagelet['html']));
511 | }
512 | unset($pagelet);
513 | $title = convertToUtf8(self::$_title);
514 | $html = json_encode(array(
515 | 'title' => $title,
516 | 'pagelets' => $pagelets,
517 | 'resource_map' => $res
518 | ));
519 | break;
520 | case self::MODE_BIGPIPE:
521 | $external = FISResource::getArrStaticCollection();
522 | $page_script = $external['script'];
523 | unset($external['script']);
524 | $html = self::renderStatic(
525 | $html,
526 | $external,
527 | true
528 | );
529 | $html .= "\n";
530 | $html .= '';
535 | $html .= "\n";
536 |
537 | if ($res['script']) {
538 | $res['script'] = convertToUtf8(implode("\n", $res['script']));
539 | }
540 | if ($res['style']) {
541 | $res['style'] = convertToUtf8(implode("\n", $res['style']));
542 | }
543 | $html .= "\n";
544 | foreach($pagelets as $index => $pagelet){
545 | $id = '__cnt_' . $index;
546 | $html .= ''),
549 | array('\\\\', '--\\>'),
550 | self::insertPageletGroup($pagelet['html'])
551 | );
552 | unset($pagelet['html']);
553 | $pagelet['html_id'] = $id;
554 | $html .= ' -->';
555 | $html .= "\n";
556 | $html .= '';
563 | $html .= "\n";
564 | }
565 | $html .= '';
576 | break;
577 | }
578 |
579 | return $html;
580 | }
581 |
582 | //smarty output filter
583 | static function renderResponse($content, $smarty) {
584 | return self::display($content);
585 | }
586 | }
587 |
--------------------------------------------------------------------------------