diff --git a/.jshintrc b/.jshintrc
index 2fb199d..7d55548 100644
--- a/.jshintrc
+++ b/.jshintrc
@@ -4,8 +4,8 @@
"latedef": false,
"newcap": true,
"quotmark": false,
- "undef": false,
- "unused": false,
+ "undef": true,
+ "unused": true,
"trailing": true,
"lastsemic": true,
"asi": false,
diff --git a/Gruntfile.js b/Gruntfile.js
index cabd0e2..37e6b99 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -9,8 +9,8 @@
module.exports = function(grunt) {
- var style = require('./').style.init(grunt);
- var css2jsParser = style.css2jsParser;
+ var css2js = require('./').css2js.init(grunt);
+ var css2jsParser = css2js.cssParser;
var jsParser = require('./').script.init(grunt).jsParser;
grunt.initConfig({
@@ -18,11 +18,11 @@ module.exports = function(grunt) {
all: [
'Gruntfile.js',
'tasks/**/*.js',
- '<%= mochaTest.test.src %>',
+ '<%= mochaTest.test.src %>'
],
options: {
- jshintrc: '.jshintrc',
- },
+ jshintrc: '.jshintrc'
+ }
},
clean: {
@@ -93,7 +93,10 @@ module.exports = function(grunt) {
// rely on other modules
rely: {
options: {
- paths: ['test/cases/assets']
+ paths: ['test/cases/assets'],
+ alias: {
+ foo: 'arale/class/foo'
+ }
},
files: [{
expand: true,
@@ -185,7 +188,7 @@ module.exports = function(grunt) {
'.css': [css2jsParser],
'.js': [jsParser]
},
- styleBox: ["a.css"],
+ styleBox: ['a.css'],
idleading: 'arale/widget/1.0.0/'
},
files: [{
@@ -248,6 +251,47 @@ module.exports = function(grunt) {
src: '**/*.js',
dest: 'test/expected/directory'
}]
+ },
+
+ 'hash': {
+ options: {
+ paths: ['test/cases/assets'],
+ alias: {
+ 'foo': 'arale/class/foo',
+ 'bar': 'family/bar/bar',
+ '$': '$'
+ },
+ idleading: 'family/name/',
+ hash: true,
+ debug: false
+ },
+ files: [{
+ expand: true,
+ cwd: 'test/cases/hash',
+ src: '*.js',
+ dest: 'test/expected/hash'
+ }]
+ },
+
+ project: {
+ options: {
+ paths: ['test/cases/project/sea-modules'],
+ alias: {
+ 'list': 'alice/list/1.0.1/list.css',
+ 'base': 'arale/base/1.1.1/base',
+ 'confirmbox': 'arale/dialog/1.3.1/confirmbox',
+ 'loading': 'alice/loading/1.0.0/loading.css'
+ },
+ idleading: 'family/name/',
+ hash: true,
+ debug: true
+ },
+ files: [{
+ expand: true,
+ cwd: 'test/cases/project',
+ src: ['*.*', '!*.expect'],
+ dest: 'test/expected/project'
+ }]
}
},
@@ -271,6 +315,6 @@ module.exports = function(grunt) {
// By default, lint and run all tests.
grunt.registerTask('default', ['jshint']);
- grunt.registerTask('test', ['clean', 'transport', 'mochaTest', 'clean']);
+ grunt.registerTask('test', ['clean', 'transport', 'mochaTest']);
};
diff --git a/README.md b/README.md
index a8335bc..09d6478 100644
--- a/README.md
+++ b/README.md
@@ -131,7 +131,11 @@ In lieu of a formal styleguide, take care to maintain the existing coding style.
## Release History
-**Dec 4th, 2013** `0.4.0`
+**Jan 22th, 2015** `0.5.0`
+
+Support hash
+
+**Dec 4th, 2013** `0.4.1`
fix Windows path #58
diff --git a/index.js b/index.js
index 1ee4e89..fefe13d 100644
--- a/index.js
+++ b/index.js
@@ -1,4 +1,7 @@
exports.style = require('./tasks/lib/style');
exports.script = require('./tasks/lib/script');
+exports.css2js = require('./tasks/lib/css2js');
exports.template = require('./tasks/lib/template');
exports.text = require('./tasks/lib/text');
+exports.handlebars = require('./tasks/lib/handlebars');
+exports.json = require('./tasks/lib/json');
diff --git a/package.json b/package.json
index 28f3ab4..f54eb4c 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
{
"name": "grunt-cmd-transport",
"description": "Transport javascript into cmd.",
- "version": "0.4.1",
+ "version": "0.5.0",
"homepage": "https://github.com/spmjs/grunt-cmd-transport",
"author": {
"name": "Hsiaoming Yang",
@@ -29,9 +29,12 @@
"dependencies": {
"clean-css": "~1.0.1",
"cmd-util": "~0.3.5",
+ "css": "~1.4.0",
+ "css2str": "~0.1.1",
"handlebars": "1.0.11",
- "uglify-js": "~2.2.5",
- "css": "~1.4.0"
+ "htmlclean": "~2.2.3",
+ "relative": "~1.2.0",
+ "uglify-js": "~2.2.5"
},
"devDependencies": {
"grunt-contrib-jshint": "~0.1.1",
@@ -43,4 +46,4 @@
"keywords": [
"gruntplugin"
]
-}
\ No newline at end of file
+}
diff --git a/tasks/lib/common.js b/tasks/lib/common.js
new file mode 100644
index 0000000..46d3acc
--- /dev/null
+++ b/tasks/lib/common.js
@@ -0,0 +1,81 @@
+exports.init = function(grunt, options) {
+ var extname = require('path').extname;
+ var format = require('util').format;
+ var ast = require('cmd-util').ast;
+ var md5 = require('./util').md5;
+ var type = options.type;
+ var factoryParser = options.factoryParser;
+ var depParser = options.depParser;
+ var regType = new RegExp('.' + type + '$');
+ var retTypeJs = new RegExp('.' + type + '.js$');
+
+ var exports = {};
+
+ exports[type + 'Parser'] = function(fileObj, options) {
+ var filepath, id = unixy(options.idleading + fileObj.name.replace(/\.js$/, ''));
+ var data = fileObj.srcData || grunt.file.read(fileObj.src);
+ var hash = md5(data);
+ var deps = depParser ? depParser(data, options) : '';
+ var factory = factoryParser ? factoryParser(data, options, fileObj) : '{}';
+ var file = {
+ contents: format('define("%s", [%s], %s)', id, deps, factory),
+ dest: fileObj.dest + '.js'
+ };
+
+ if (!options.hash) {
+
+ // create .{type}.js
+ data = ast.modify(file.contents, {
+ id: id
+ }).print_to_string(options.uglify);
+ filepath = file.dest;
+ writeFile(data, filepath);
+ } else {
+
+ // create hash file xxx-{hash}.{type}.js
+ if (options.hash) {
+ filepath = file.dest.replace(retTypeJs, '-' + hash + '.' + type + '.js');
+ data = ast.modify(file.contents, {
+ id: id.replace(regType, '-' + hash + '.' + type)
+ }).print_to_string(options.uglify);
+ writeFile(data, filepath);
+ }
+ }
+
+ // create debug file xxx-debug.{type}.js
+ if (options.debug) {
+ data = ast.modify(data, addDebug).print_to_string(options.uglify);
+ filepath = filepath.replace(retTypeJs, '-debug.' + type + '.js');
+ writeFile(data, filepath);
+ }
+
+// {
+// id: id.replace(regType, '-debug.' + type),
+// dependencies: function(id) {
+// return id + '-debug';
+// },
+// require: function(id) {
+// return id + '-debug';
+// }
+// }
+ function addDebug(v) {
+ var ext = extname(v);
+ if (ext && options.parsers[ext]) {
+ return v.replace(new RegExp('\\' + ext + '$'), '-debug' + ext);
+ } else {
+ return v + '-debug';
+ }
+ }
+
+ };
+ return exports;
+
+ function writeFile(data, dest) {
+ grunt.log.writeln('transport ' + dest + ' created');
+ grunt.file.write(dest, data + '\n');
+ }
+};
+
+function unixy(uri) {
+ return uri.replace(/\\/g, '/');
+}
diff --git a/tasks/lib/css2js.js b/tasks/lib/css2js.js
new file mode 100644
index 0000000..60ff376
--- /dev/null
+++ b/tasks/lib/css2js.js
@@ -0,0 +1,61 @@
+var format = require('util').format;
+var css2str = require('css2str');
+var cleancss = require('clean-css');
+var commonParser = require('./common');
+
+exports.init = function(grunt) {
+ return commonParser.init(grunt, {
+ type: 'css',
+ factoryParser: css2js
+ });
+};
+exports.css2js = function(code, id, options, fileObj) {
+ var tpl = [
+ 'define("%s", [], function() {',
+ "seajs.importStyle('%s')",
+ '});'
+ ].join('\n');
+ return format(tpl, id, css2js(code, options, fileObj));
+};
+
+function css2js(code, options, fileObj) {
+ var addStyleBox = false;
+ if (options.styleBox === true) {
+ addStyleBox = true;
+ } else if (options.styleBox && options.styleBox.length) {
+ options.styleBox.forEach(function(file) {
+ if (file === fileObj.name) {
+ addStyleBox = true;
+ }
+ });
+ }
+
+ // if outside css modules, fileObj would be undefined
+ // then dont add styleBox
+ var opt = {};
+ if (addStyleBox && fileObj) {
+ // ex. arale/widget/1.0.0/ => arale-widget-1_0_0
+ var styleId = unixy((options || {}).idleading || '')
+ .replace(/\/$/, '')
+ .replace(/\//g, '-')
+ .replace(/\./g, '_');
+ opt.prefix = ['.', styleId, ' '].join('');
+ }
+
+ code = css2str(code, opt);
+
+ // remove comment and format
+ code = cleancss.process(code, {
+ keepSpecialComments: 0,
+ removeEmpty: true
+ });
+
+ // transform css to js
+ // spmjs/spm#581
+ var template = 'function() {seajs.importStyle(\'%s\')}';
+ return format(template, code);
+}
+
+function unixy(uri) {
+ return uri.replace(/\\/g, '/');
+}
diff --git a/tasks/lib/handlebars.js b/tasks/lib/handlebars.js
new file mode 100644
index 0000000..8dd426f
--- /dev/null
+++ b/tasks/lib/handlebars.js
@@ -0,0 +1,60 @@
+var commonParser = require('./common');
+
+exports.init = function(grunt) {
+ var format = require('util').format;
+ var handlebars = require('handlebars');
+
+ return commonParser.init(grunt, {
+ type: 'handlebars',
+ depParser: function(data, options) {
+ // handlebars alias
+ return '"' + options.handlebars.id + '"';
+ },
+ factoryParser: function(data, options) {
+ patchHandlebars(handlebars);
+ var code = handlebars.precompile(data, options.handlebars);
+ var template = [
+ 'function(require, exports, module) {',
+ 'var Handlebars = require("%s");',
+ 'var template = Handlebars.template;',
+ 'module.exports = template(%s);',
+ '}'
+ ].join('\n');
+ return format(template, options.handlebars.id, code);
+ }
+ });
+};
+
+// patch for handlebars
+function patchHandlebars(Handlebars) {
+ Handlebars.JavaScriptCompiler.prototype.preamble = function() {
+ var out = [];
+
+ if (!this.isChild) {
+ var namespace = this.namespace;
+ // patch for handlebars
+ var copies = [
+ "helpers = helpers || {};",
+ "for (var key in " + namespace + ".helpers) {",
+ " helpers[key] = helpers[key] || " + namespace + ".helpers[key];",
+ "}"
+ ].join('\n');
+ if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
+ if (this.options.data) { copies = copies + " data = data || {};"; }
+ out.push(copies);
+ } else {
+ out.push('');
+ }
+
+ if (!this.environment.isSimple) {
+ out.push(", buffer = " + this.initializeBuffer());
+ } else {
+ out.push("");
+ }
+
+ // track the last context pushed into place to allow skipping the
+ // getContext opcode when it would be a noop
+ this.lastContext = 0;
+ this.source = out;
+ };
+}
diff --git a/tasks/lib/json.js b/tasks/lib/json.js
index 7c99bbe..0760f00 100644
--- a/tasks/lib/json.js
+++ b/tasks/lib/json.js
@@ -1,44 +1,10 @@
-exports.init = function(grunt) {
-
- var path = require('path');
- var format = require('util').format;
- var iduri = require('cmd-util').iduri;
- var ast = require('cmd-util').ast;
-
- var exports = {};
-
- exports.jsonParser = function(fileObj, options) {
- var dest = fileObj.dest + '.js';
- grunt.log.verbose.writeln('Transport ' + fileObj.src + ' -> ' + dest);
-
- var id = unixy(options.idleading + fileObj.name.replace(/\.js$/, ''));
- var data = fileObj.srcData || grunt.file.read(fileObj.src);
- var code = format('define("%s", [], %s)', id, data);
- var astCache = ast.getAst(code);
+var commonParser = require('./common');
- data = astCache.print_to_string(options.uglify);
- grunt.file.write(dest, data);
-
- // create debug file
- if (!options.debug) {
- return;
+exports.init = function(grunt) {
+ return commonParser.init(grunt, {
+ type: 'json',
+ factoryParser: function(data) {
+ return data || '{}';
}
- dest = dest.replace(/\.json\.js$/, '-debug.json.js');
-
- astCache = ast.modify(astCache, function(v) {
- var ext = path.extname(v);
- if (ext && options.parsers[ext]) {
- return v.replace(new RegExp('\\' + ext + '$'), '-debug' + ext);
- } else {
- return v + '-debug';
- }
- });
- data = astCache.print_to_string(options.uglify);
- grunt.file.write(dest, data);
- };
- return exports;
+ });
};
-
-function unixy(uri) {
- return uri.replace(/\\/g, '/');
-}
diff --git a/tasks/lib/script.js b/tasks/lib/script.js
index 88a7cb7..2d848fc 100644
--- a/tasks/lib/script.js
+++ b/tasks/lib/script.js
@@ -1,225 +1,326 @@
exports.init = function(grunt) {
+
var path = require('path');
+ var extname = path.extname;
var ast = require('cmd-util').ast;
var iduri = require('cmd-util').iduri;
+ var relative = require('relative');
+ var md5 = require('./util').md5;
var _ = grunt.util._;
+ return {
+ jsParser: jsParser
+ };
- var exports = {};
-
- exports.jsParser = function(fileObj, options) {
+ function jsParser(fileObj, options) {
grunt.log.verbose.writeln('Transport ' + fileObj.src + ' -> ' + fileObj.dest);
- var astCache, data = fileObj.srcData || grunt.file.read(fileObj.src);
- try {
- astCache = ast.getAst(data);
- } catch(e) {
- grunt.log.error('js parse error ' + fileObj.src.red);
- grunt.fail.fatal(e.message + ' [ line:' + e.line + ', col:' + e.col + ', pos:' + e.pos + ' ]');
- }
- var meta = ast.parseFirst(astCache);
- if (!meta) {
- grunt.log.warn('found non cmd module "' + fileObj.src + '"');
- // do nothing
- return;
- }
+ // cache every filepath content to generate hash
+ //
+ // {
+ // '/path/to/file': {
+ // id: undefined,
+ // dependencies: [],
+ // depMap: {},
+ // depsSpecified: false,
+ // contents: contents,
+ // path: path,
+ // hash: md5(contents, [])
+ // }
+ // }
+ var fileCache = {};
+
+ var file = getFileInfo(path.join(process.cwd(), fileObj.src));
+
+ if (!file) return;
+
+ var data, filepath;
+ if (!options.hash) {
+
+ // create original file, xxx.js
+ data = ast.modify(file.contents, {
+ id: unixy(options.idleading) + getId(file),
+ dependencies: getDeps(file),
+ require: function(v) {
+ // ignore when deps is specified by developer
+ return file.depsSpecified ? v : iduri.parseAlias(options, v);
+ }
+ }).print_to_string(options.uglify);
+ filepath = fileObj.dest;
+ writeFile(data, filepath);
+ } else {
- if (meta.id) {
- grunt.log.verbose.writeln('id exists in "' + fileObj.src + '"');
+ // create file with hash, xxx-2cio56s.js
+ var hash = file.hash;
+ data = ast.modify(file.contents, {
+ id: unixy(options.idleading) + getId(file) + '-' + hash,
+ dependencies: getDeps(file, addHash),
+ require: function(v) {
+ // ignore when deps is specified by developer
+ if (file.depsSpecified) return v;
+ var depFile = file.depMap[v];
+ if (depFile) {
+ return addHash(depFile);
+ }
+ return iduri.parseAlias(options, v);
+ }
+ }).print_to_string(options.uglify);
+ filepath = fileObj.dest.replace(/\.js$/, '-' + hash + '.js');
+ writeFile(data, filepath);
}
- var deps, depsSpecified = false;
- if (meta.dependencyNode) {
- deps = meta.dependencies;
- depsSpecified = true;
- grunt.log.verbose.writeln('dependencies exists in "' + fileObj.src + '"');
- } else {
- deps = parseDependencies(fileObj.src, options);
- grunt.log.verbose.writeln(deps.length ?
- 'found dependencies ' + deps : 'found no dependencies');
+ // create file with debug, xxx-debug.js
+ if (options.debug) {
+ data = ast.modify(data, addDebug).print_to_string(options.uglify);
+ writeFile(data, addDebug(filepath));
}
- // create .js file
- astCache = ast.modify(astCache, {
- id: meta.id ? meta.id : unixy(options.idleading + fileObj.name.replace(/\.js$/, '')),
- dependencies: deps,
- require: function(v) {
- // ignore when deps is specified by developer
- return depsSpecified ? v : iduri.parseAlias(options, v);
- }
- });
- data = astCache.print_to_string(options.uglify);
- grunt.file.write(fileObj.dest, addOuterBoxClass(data, options));
-
-
- // create -debug.js file
- if (!options.debug) {
- return;
+ function getId(file) {
+ return file.id || unixy(fileObj.name.replace(/\.js$/, ''));
}
- var dest = fileObj.dest.replace(/\.js$/, '-debug.js');
- astCache = ast.modify(data, function(v) {
- var ext = path.extname(v);
+ function getDeps(file, fn) {
+ return file.dependencies.map(function(dep) {
+ if (typeof dep !== 'string') {
+ dep = fn ? fn(dep) : dep.id;
+ }
+ return dep.replace(/\.js$/, '');
+ });
+ }
+ function addDebug(v) {
+ var ext = extname(v);
if (ext && options.parsers[ext]) {
return v.replace(new RegExp('\\' + ext + '$'), '-debug' + ext);
} else {
return v + '-debug';
}
- });
- data = astCache.print_to_string(options.uglify);
- grunt.file.write(dest, addOuterBoxClass(data, options));
- };
-
-
- // helpers
- // ----------------
- function unixy(uri) {
- return uri.replace(/\\/g, '/');
- }
-
- function getStyleId(options) {
- return unixy((options || {}).idleading || '')
- .replace(/\/$/, '')
- .replace(/\//g, '-')
- .replace(/\./g, '_');
- }
-
- function addOuterBoxClass(data, options) {
- // ex. arale/widget/1.0.0/ => arale-widget-1_0_0
- var styleId = getStyleId(options);
- if (options.styleBox && styleId) {
- data = data.replace(/(\}\)[;\n\r ]*$)/, 'module.exports.outerBoxClass="' + styleId + '";$1');
}
- return data;
- }
- function moduleDependencies(id, options) {
- var alias = iduri.parseAlias(options, id);
+ function addHash(v) {
+ if (v.id.charAt(0) !== '.') return v.id;
+ if (!v.hash) return v.id;
- if (iduri.isAlias(options, id) && alias === id) {
- // usually this is "$"
- return [];
+ var ext = extname(v.id);
+ if (ext && options.parsers[ext]) {
+ return v.id.replace(new RegExp('\\' + ext + '$'), '-' + v.hash + ext);
+ } else {
+ return v.id + '-' + v.hash;
+ }
}
- // don't resolve text!path/to/some.xx, same as seajs-text
- if (/^text!/.test(id)) {
- return [];
+ function writeFile(data, dest) {
+ grunt.log.writeln('transport ' + dest + ' created');
+ grunt.file.write(dest, addOuterBoxClass(data + '\n', options));
}
- var file = iduri.appendext(alias);
+ // helpers
+ // ----------------
+ function unixy(uri) {
+ return uri.replace(/\\/g, '/');
+ }
- if (!/\.js$/.test(file)) {
- return [];
+ function getStyleId() {
+ return unixy((options || {}).idleading || '')
+ .replace(/\/$/, '')
+ .replace(/\//g, '-')
+ .replace(/\./g, '_');
}
- var fpath;
- options.paths.some(function(base) {
- var filepath = path.join(base, file);
- if (grunt.file.exists(filepath)) {
- grunt.log.verbose.writeln('find module "' + filepath + '"');
- fpath = filepath;
- return true;
+ function addOuterBoxClass(data) {
+ // ex. arale/widget/1.0.0/ => arale-widget-1_0_0
+ var styleId = getStyleId(options);
+ if (options.styleBox && styleId) {
+ data = data.replace(/(\}\)[;\n\r ]*$)/, 'module.exports.outerBoxClass="' + styleId + '";$1');
}
- });
-
- if (!fpath) {
- grunt.fail.warn("can't find module " + alias);
- return [];
- }
- if (!grunt.file.exists(fpath)) {
- grunt.fail.warn("can't find " + fpath);
- return [];
+ return data;
}
- var data = grunt.file.read(fpath);
- var parsed = ast.parse(data);
- var deps = [];
-
- var ids = parsed.map(function(meta) {
- return meta.id;
- });
-
- parsed.forEach(function(meta) {
- meta.dependencies.forEach(function(dep) {
- dep = iduri.absolute(alias, dep);
- if (!_.contains(deps, dep) && !_.contains(ids, dep) && !_.contains(ids, dep.replace(/\.js$/, ''))) {
- deps.push(dep);
- }
- });
- });
- return deps;
- }
- function parseDependencies(fpath, options) {
- var rootpath = fpath;
-
- function relativeDependencies(fpath, options, basefile) {
- if (basefile) {
- fpath = path.join(path.dirname(basefile), fpath);
+ function parseFileDependencies(filepath, depMap) {
+ if (!grunt.file.exists(filepath)) {
+ if (!/\{\w+\}/.test(filepath)) {
+ grunt.log.warn('can\'t find ' + filepath);
+ }
+ return [];
}
- fpath = iduri.appendext(fpath);
-
- var deps = [];
- var moduleDeps = {};
- if (!grunt.file.exists(fpath)) {
- if (!/\{\w+\}/.test(fpath)) {
- grunt.log.warn("can't find " + fpath);
- }
+ if (extname(filepath) !== '.js') {
return [];
}
- var parsed, data = grunt.file.read(fpath);
+
+ var parsed, data = grunt.file.read(filepath);
try {
parsed = ast.parseFirst(data);
} catch(e) {
grunt.log.error(e.message + ' [ line:' + e.line + ', col:' + e.col + ', pos:' + e.pos + ' ]');
return [];
}
- parsed.dependencies.map(function(id) {
- return id.replace(/\.js$/, '');
- }).forEach(function(id) {
+ return parsed.dependencies.map(function(id) {
if (id.charAt(0) === '.') {
- // fix nested relative dependencies
- if (basefile) {
- var altId = path.join(path.dirname(fpath), id).replace(/\\/g, '/');
- var dirname = path.dirname(rootpath).replace(/\\/g, '/');
- if ( dirname !== altId ) {
- altId = path.relative(dirname, altId);
- } else {
- // the same name between file and directory
- altId = path.relative(dirname, altId + '.js').replace(/\.js$/, '');
- }
- altId = altId.replace(/\\/g, '/');
- if (altId.charAt(0) !== '.') {
- altId = './' + altId;
- }
- deps.push(altId);
- } else {
- deps.push(id);
- }
- if (/\.js$/.test(iduri.appendext(id))) {
- deps = grunt.util._.union(deps, relativeDependencies(id, options, fpath));
- }
- } else if (!moduleDeps[id]) {
- var alias = iduri.parseAlias(options, id);
- deps.push(alias);
+ var origId = id;
+ id = iduri.appendext(id);
+ var depFilepath = path.join(path.dirname(filepath), id);
+ var depFile = getFileInfo(depFilepath);
+ if (!depFile) return;
+ var obj = {
+ id: origId,
+ path: depFile.path,
+ contents: depFile.contents,
+ hash: depFile.hash,
+ relative: true
+ };
+ if (depMap) depMap[origId] = obj;
+ return [obj].concat(parseFileDependencies(depFilepath));
+ } else {
+ return parseModuleDependencies(id);
+ }
+ });
+ }
+
+ function parseModuleDependencies(id) {
+ var alias = iduri.parseAlias(options, id);
+
+ if (iduri.isAlias(options, id) && alias === id) {
+ // usually this is "$"
+ return [{id: id}];
+ }
+
+ // don't resolve text!path/to/some.xx, same as seajs-text
+ if (/^text!/.test(id)) {
+ return [{id: id}];
+ }
- // don't parse no javascript dependencies
- var ext = path.extname(alias);
- if (ext && ext !== '.js') return;
+ var fpath = iduri.appendext(alias);
- var mdeps = moduleDependencies(id, options);
- moduleDeps[id] = mdeps;
- deps = grunt.util._.union(deps, mdeps);
+ var fbase;
+ options.paths.some(function(base) {
+ var filepath = path.join(base, fpath);
+ if (grunt.file.exists(filepath)) {
+ grunt.log.verbose.writeln('find module "' + filepath + '"');
+ fbase = base;
+ fpath = filepath;
+ return true;
}
});
- return deps;
+
+ if (!fpath) {
+ grunt.fail.warn('can\'t find module ' + alias);
+ return [{id: id}];
+ }
+
+ var file = getFileInfo(fpath);
+
+ if (!file) {
+ return [{id: id}];
+ }
+
+ // don't parse no javascript dependencies
+ if (!/\.js$/.test(fpath)) {
+ return [{
+ id: alias,
+ path: file.path,
+ hash: file.hash,
+ contents: file.contents
+ }];
+ }
+
+ var parsed = ast.parse(file.contents);
+ var ids = parsed.map(function(meta) {
+ return meta.id;
+ });
+ var deps = parsed.map(function(meta) {
+ return meta.dependencies.map(function(id) {
+ id = iduri.absolute(alias, id);
+ // won't return if deps were loaded(defined in file)
+ if (!_.contains(ids, id) && !_.contains(ids, id.replace(/\.js$/, ''))) {
+ id = iduri.appendext(id);
+ var file = getFileInfo(path.join(fbase, id));
+ if (!file) return;
+ return {
+ id: id,
+ path: file.path,
+ hash: file.hash,
+ contents: file.contents
+ };
+ }
+ });
+ });
+ return [{
+ id: alias,
+ path: file.path,
+ hash: file.hash,
+ contents: file.contents
+ }].concat(deps);
}
- return relativeDependencies(fpath, options);
- }
+ function getFileInfo(path){
+ if (fileCache[path]) {
+ return fileCache[path];
+ }
+
+ if (!grunt.file.exists(path)) return;
+ var astCache, deps, contents = grunt.file.read(path);
+
+ if (extname(path) !== '.js') {
+ return fileCache[path] = {
+ id: undefined,
+ dependencies: [],
+ depMap: {},
+ depsSpecified: false,
+ contents: contents,
+ path: path,
+ hash: md5(contents, [])
+ };
+ }
+
+ try {
+ astCache = ast.getAst(contents);
+ } catch(e) {
+ grunt.log.error('js parse error ' + path.red);
+ grunt.fail.fatal(e.message + ' [ line:' + e.line + ', col:' + e.col + ', pos:' + e.pos + ' ]');
+ }
+
+ // get id/deps of origin cmd module
+ var meta = ast.parseFirst(astCache), depMap = {};
- return exports;
+ if (!meta) {
+ grunt.log.warn('found non cmd module "' + path + '"');
+ // do nothing
+ return;
+ }
+
+ if (meta.id) {
+ grunt.log.verbose.writeln('id exists in "' + path + '"');
+ }
+
+ if (meta.dependencyNode) {
+ deps = meta.dependencies;
+ grunt.log.verbose.writeln('dependencies exists in "' + path + '"');
+ } else {
+ deps = parseFileDependencies(path, depMap);
+ deps = grunt.util._.chain(deps)
+ .flatten()
+ .filter(function(item) {return typeof item !== 'undefined'})
+ .uniq(function(item) {return item.path})
+ .each(function(item) {
+ if (item.relative) item.id = relative(path, item.path);
+ })
+ .value();
+ grunt.log.verbose.writeln(deps.length ?
+ 'found dependencies ' + deps : 'found no dependencies');
+ }
+
+ return fileCache[path] = {
+ id: meta.id,
+ dependencies: deps,
+ depMap: depMap,
+ depsSpecified: typeof meta.dependencyNode !== 'undefined',
+ contents: contents,
+ path: path,
+ hash: typeof meta.dependencyNode !== 'undefined' ? '' : md5(contents, deps)
+ };
+ }
+ }
};
diff --git a/tasks/lib/style.js b/tasks/lib/style.js
index 7200de7..ed2ac9b 100644
--- a/tasks/lib/style.js
+++ b/tasks/lib/style.js
@@ -1,182 +1,92 @@
-var path = require('path');
-var format = require('util').format;
-var css = require('cmd-util').css;
-var cssParse = require('css').parse;
-var cssStringify = require('css').stringify;
exports.init = function(grunt) {
- var ast = require('cmd-util').ast;
var iduri = require('cmd-util').iduri;
+ var format = require('util').format;
+ var css = require('cmd-util').css;
+ var md5 = require('./util').md5;
+ var join = require('path').join;
+ var dirname = require('path').dirname;
var exports = {};
- exports.css2jsParser = function(fileObj, options) {
- // don't transport debug css files
- if (/\-debug\.css$/.test(fileObj.src)) return;
- grunt.log.verbose.writeln('Transport ' + fileObj.src + ' -> ' + fileObj.dest);
-
- // transport css to js
- var data = fileObj.srcData || grunt.file.read(fileObj.src);
- var id = unixy(options.idleading + fileObj.name.replace(/\.js$/, ''));
+ // the real css parser
+ exports.cssParser = function(fileObj, options) {
+ var id, filepath, data = fileObj.srcData || grunt.file.read(fileObj.src);
+
+ if (!options.hash) {
+
+ var ret = parseCss(data);
+ id = unixy(options.idleading + fileObj.name);
+ var code = format('/*! define %s */\n%s', id, ret);
+ filepath = fileObj.dest;
+ writeFile(code, filepath);
+ } else {
+
+ var hash = md5(data);
+ ret = parseCss(data, addHash);
+ id = unixy(options.idleading + fileObj.name.replace(/\.css$/, '-' + hash + '.css'));
+ code = format('/*! define %s */\n%s', id, ret);
+ filepath = fileObj.dest.replace(/\.css$/, '-' + hash + '.css');
+ writeFile(code, filepath);
+ }
- data = css2js(data, id, options, fileObj);
- data = ast.getAst(data).print_to_string(options.uglify);
- var dest = fileObj.dest + '.js';
- grunt.file.write(dest, data);
+ if (options.debug) {
+ ret = parseCss(data, addDebug);
+ id = id.replace(/\.css$/, '-debug.css');
+ code = format('/*! define %s */\n%s', id, ret);
+ filepath = filepath.replace(/\.css$/, '-debug.css');
+ writeFile(code, filepath);
+ }
- if (!options.debug) {
- return;
+ function addDebug(node) {
+ node.id = node.id.replace(/\.css$/, '-debug.css');
}
- dest = dest.replace(/\.css\.js$/, '-debug.css.js');
- data = ast.modify(data, function(v) {
- var ext = path.extname(v);
- if (ext && options.parsers[ext]) {
- return v.replace(new RegExp('\\' + ext + '$'), '-debug' + ext);
- } else {
- return v + '-debug';
+ function addHash(node) {
+ if (node.id.charAt(0) === '.') {
+ var code = grunt.file.read(join(dirname(fileObj.src), node.id));
+ node.id = node.id.replace(/\.css$/, '-' + md5(code) + '.css');
}
- });
- data = data.print_to_string(options.uglify);
- grunt.file.write(dest, data);
- };
+ }
- // the real css parser
- exports.cssParser = function(fileObj, options) {
- var data = fileObj.srcData || grunt.file.read(fileObj.src);
- data = css.parse(data);
+ function parseCss(data, editId) {
+ if (!editId) {
+ editId = function() {};
+ }
- grunt.log.verbose.writeln('Transport ' + fileObj.src + ' -> ' + fileObj.dest);
- var ret = css.stringify(data[0].code, function(node) {
- if (node.type === 'import' && node.id) {
- if (node.id.charAt(0) === '.') {
- return node;
- }
- if (/^https?:\/\//.test(node.id)) {
- return node;
- }
- if (!iduri.isAlias(options, node.id)) {
- grunt.log.warn('alias ' + node.id + ' not defined.');
- } else {
- node.id = iduri.parseAlias(options, node.id);
- if (!/\.css$/.test(node.id)) {
- node.id += '.css';
+ return css.stringify(css.parse(data)[0].code, function(node) {
+ if (node.type === 'import' && node.id) {
+ if (node.id.charAt(0) === '.') {
+ editId(node);
+ return node;
+ }
+ if (/^https?:\/\//.test(node.id)) {
+ return node;
+ }
+ if (!iduri.isAlias(options, node.id)) {
+ grunt.log.warn('alias ' + node.id + ' not defined.');
+ } else {
+ node.id = iduri.parseAlias(options, node.id);
+ if (!/\.css$/.test(node.id)) {
+ node.id += '.css';
+ }
+ editId(node);
+ return node;
}
- return node;
}
- }
- });
-
- var id = unixy(options.idleading + fileObj.name.replace(/\.js$/, ''));
- var banner = format('/*! define %s */', id);
- grunt.file.write(fileObj.dest, [banner, ret].join('\n'));
-
- // create -debug.css file
- if (options.debug === false) {
- return;
+ });
}
- var dest = fileObj.dest.replace(/\.css$/, '-debug.css');
-
- ret = css.stringify(data[0].code, function(node) {
- if (node.type === 'import' && node.id) {
- var alias = node.id;
- if (alias.charAt(0) === '.') {
- node.id = alias.replace(/(\.css)?$/, '-debug.css');
- return node;
- }
- if (/^https?:\/\//.test(node.id)) {
- return node;
- }
- alias = iduri.parseAlias(options, alias);
- if (/\.css$/.test(alias)) {
- node.id = alias.replace(/\.css$/, '-debug.css');
- } else {
- node.id = alias + '-debug.css';
- }
- return node;
- }
- });
- id = id.replace(/(\.css)?$/, '-debug.css');
- banner = format('/*! define %s */', id);
- grunt.file.write(dest, [banner, ret].join('\n'));
};
return exports;
-};
+ function writeFile(data, dest) {
+ grunt.log.writeln('transport ' + dest + ' created');
+ grunt.file.write(dest, data + '\n');
+ }
+};
// helpers
function unixy(uri) {
return uri.replace(/\\/g, '/');
}
-
-function parseRules(rules, prefix) {
- return rules.map(function(o) {
- if (o.selectors) {
- o.selectors = o.selectors.map(function(selector) {
- // handle :root selector {}
- if (selector.indexOf(':root') === 0) {
- return ':root ' + prefix + selector.replace(':root', ' ');
- }
- return prefix + selector;
- });
- }
- if (o.rules) {
- o.rules = parseRules(o.rules, prefix);
- }
- return o;
- });
-}
-
-function css2js(code, id, options, fileObj) {
- // ex. arale/widget/1.0.0/ => arale-widget-1_0_0
- var styleId = unixy((options || {}).idleading || '')
- .replace(/\/$/, '')
- .replace(/\//g, '-')
- .replace(/\./g, '_');
- var prefix = ['.', styleId, ' '].join('');
-
- var addStyleBox = false;
- if (options.styleBox === true) {
- addStyleBox = true;
- } else if (options.styleBox && options.styleBox.length) {
- options.styleBox.forEach(function(file) {
- if (file === fileObj.name) {
- addStyleBox = true;
- }
- });
- }
-
- // if outside css modules, fileObj would be undefined
- // then dont add styleBox
- if (addStyleBox && styleId && fileObj) {
- var data = cssParse(code);
- data.stylesheet.rules = parseRules(data.stylesheet.rules, prefix);
- code = cssStringify(data);
- }
-
- // remove comment and format
- var cleancss = require('clean-css');
- code = cleancss.process(code, {
- keepSpecialComments: 0,
- removeEmpty: true
- });
-
- // transform css to js
- // spmjs/spm#581
- var tpl = [
- 'define("%s", [], function() {',
- "seajs.importStyle('%s')",
- '});'
- ].join('\n');
-
- // spmjs/spm#651
- code = code.split(/\r\n|\r|\n/).map(function(line) {
- return line.replace(/\\/g, '\\\\');
- }).join('\n');
-
- code = format(tpl, id, code.replace(/\'/g, '\\\''));
- return code;
-}
-
-exports.css2js = css2js;
diff --git a/tasks/lib/template.js b/tasks/lib/template.js
index d688126..b1cc038 100644
--- a/tasks/lib/template.js
+++ b/tasks/lib/template.js
@@ -1,131 +1,10 @@
-exports.init = function(grunt) {
-
- var path = require('path');
- var format = require('util').format;
- var iduri = require('cmd-util').iduri;
- var ast = require('cmd-util').ast;
-
- var exports = {};
-
- exports.tplParser = function(fileObj, options) {
- var dest = fileObj.dest + '.js';
- grunt.log.verbose.writeln('Transport ' + fileObj.src + ' -> ' + dest);
-
- var id = unixy(options.idleading + fileObj.name.replace(/\.js$/, ''));
- var data = fileObj.srcData || grunt.file.read(fileObj.src);
- var code = format('define("%s", [], "%s")', id, data.replace(/\"/g, '\\\"'));
- var astCache = ast.getAst(code);
-
- data = astCache.print_to_string(options.uglify);
- grunt.file.write(dest, data);
-
- // create debug file
- if (!options.debug) {
- return;
- }
- dest = dest.replace(/\.tpl\.js$/, '-debug.tpl.js');
-
- astCache = ast.modify(astCache, function(v) {
- var ext = path.extname(v);
- if (ext && options.parsers[ext]) {
- return v.replace(new RegExp('\\' + ext + '$'), '-debug' + ext);
- } else {
- return v + '-debug';
- }
- });
- data = astCache.print_to_string(options.uglify);
- grunt.file.write(dest, data);
- };
-
- exports.handlebarsParser = function(fileObj, options) {
- var dest = fileObj.dest + '.js';
- grunt.log.verbose.writeln('Transport ' + fileObj.src + ' -> ' + dest);
-
- var handlebars = require('handlebars');
-
- // id for template
- var id = unixy(options.idleading + fileObj.name.replace(/\.js$/, ''));
-
- // handlebars alias
- var alias = options.handlebars.id;
-
- var template = [
- 'define("%s", ["%s"], function(require, exports, module) {',
- 'var Handlebars = require("%s");',
- 'var template = Handlebars.template;',
- 'module.exports = template(',
- '%s',
- ');',
- '})'
- ].join('\n');
-
- var data = fileObj.srcData || grunt.file.read(fileObj.src);
+var commonParser = require('./common');
- patchHandlebars(handlebars);
- var code = handlebars.precompile(data, options.handlebars);
-
- var ret = format(template, id, alias, alias, code);
- var astCache = ast.getAst(ret);
-
- data = astCache.print_to_string(options.uglify);
- grunt.file.write(dest, data);
-
- // create debug file
- if (!options.debug) {
- return;
+exports.init = function(grunt) {
+ return commonParser.init(grunt, {
+ type: 'tpl',
+ factoryParser: function(data) {
+ return '"' + data.replace(/\"/g, '\\\"') + '"';
}
- dest = dest.replace(/\.handlebars\.js$/, '-debug.handlebars.js');
-
- astCache = ast.modify(astCache, function(v) {
- var ext = path.extname(v);
- if (ext && options.parsers[ext]) {
- return v.replace(new RegExp('\\' + ext + '$'), '-debug' + ext);
- } else {
- return v + '-debug';
- }
- });
- data = astCache.print_to_string(options.uglify);
- grunt.file.write(dest, data);
- };
-
- return exports;
+ });
};
-
-
-// patch for handlebars
-function patchHandlebars(Handlebars) {
- Handlebars.JavaScriptCompiler.prototype.preamble = function() {
- var out = [];
-
- if (!this.isChild) {
- var namespace = this.namespace;
- // patch for handlebars
- var copies = [
- "helpers = helpers || {};",
- "for (var key in " + namespace + ".helpers) {",
- " helpers[key] = helpers[key] || " + namespace + ".helpers[key];",
- "}"
- ].join('\n');
- if (this.environment.usePartial) { copies = copies + " partials = partials || " + namespace + ".partials;"; }
- if (this.options.data) { copies = copies + " data = data || {};"; }
- out.push(copies);
- } else {
- out.push('');
- }
-
- if (!this.environment.isSimple) {
- out.push(", buffer = " + this.initializeBuffer());
- } else {
- out.push("");
- }
-
- // track the last context pushed into place to allow skipping the
- // getContext opcode when it would be a noop
- this.lastContext = 0;
- this.source = out;
- };
-}
-
-function unixy(uri) {
- return uri.replace(/\\/g, '/');
-}
diff --git a/tasks/lib/text.js b/tasks/lib/text.js
index faa94d7..ab6b9ac 100644
--- a/tasks/lib/text.js
+++ b/tasks/lib/text.js
@@ -1,61 +1,14 @@
-var path = require('path');
-var format = require('util').format;
+var htmlclean = require('htmlclean');
+var commonParser = require('./common');
exports.init = function(grunt) {
- var ast = require('cmd-util').ast;
- var iduri = require('cmd-util').iduri;
-
- var exports = {};
-
- exports.html2jsParser = function(fileObj, options) {
- // don't transport debug html files
- if (/\-debug\.html/.test(fileObj.src)) return;
-
- grunt.log.verbose.writeln('Transport ' + fileObj.src + ' -> ' + fileObj.dest);
- // transport html to js
- var data = fileObj.srcData || grunt.file.read(fileObj.src);
- var id = unixy(options.idleading + fileObj.name.replace(/\.js$/, ''));
-
- data = html2js(data, id);
- data = ast.getAst(data).print_to_string(options.uglify);
- var dest = fileObj.dest + '.js';
- grunt.file.write(dest, data);
-
- if (!options.debug) {
- return;
- }
- dest = dest.replace(/\.html\.js$/, '-debug.html.js');
-
- data = ast.modify(data, function(v) {
- var ext = path.extname(v);
- if (ext && options.parsers[ext]) {
- return v.replace(new RegExp('\\' + ext + '$'), '-debug' + ext);
- } else {
- return v + '-debug';
- }
- });
- data = data.print_to_string(options.uglify);
- grunt.file.write(dest, data);
- };
-
- return exports;
+ return commonParser.init(grunt, {
+ type: 'html',
+ factoryParser: getCode
+ });
};
-
-// helpers
-function html2js(code, id) {
- var tpl = 'define("%s", [], "%s");';
-
- code = code.split(/\r\n|\r|\n/).map(function(line) {
- return line.replace(/\\/g, '\\\\');
- }).join('\n');
-
- code = format(tpl, id, code.replace(/\"/g, '\\\"'));
- return code;
+function getCode(data) {
+ data = htmlclean(data).replace(/(\"|\'|\\)/g, '\\$1');
+ return '"' + data + '"';
}
-
-function unixy(uri) {
- return uri.replace(/\\/g, '/');
-}
-
-exports.html2js = html2js;
diff --git a/tasks/lib/util.js b/tasks/lib/util.js
new file mode 100644
index 0000000..e4420ec
--- /dev/null
+++ b/tasks/lib/util.js
@@ -0,0 +1,13 @@
+var crypto = require('crypto');
+
+exports.md5 = function md5(contents, deps) {
+ if (!deps) deps = [];
+ contents = deps.map(function(depFile) {
+ return depFile.contents || '';
+ }).join('') + contents;
+ return crypto
+ .createHash('md5')
+ .update(contents, 'utf8')
+ .digest('hex')
+ .slice(0, 8);
+};
diff --git a/tasks/transport.js b/tasks/transport.js
index a0abf1f..d3543f9 100644
--- a/tasks/transport.js
+++ b/tasks/transport.js
@@ -8,12 +8,12 @@
module.exports = function(grunt) {
var path = require('path');
- var cmd = require('cmd-util');
var text = require('./lib/text').init(grunt);
var script = require('./lib/script').init(grunt);
var style = require('./lib/style').init(grunt);
var template = require('./lib/template').init(grunt);
+ var handlebars = require('./lib/handlebars').init(grunt);
var json = require('./lib/json').init(grunt);
grunt.registerMultiTask('transport', 'Transport everything into cmd.', function() {
@@ -27,6 +27,9 @@ module.exports = function(grunt) {
// create a debug file or not
debug: true,
+ // create a file with hash
+ hash: false,
+
// process a template or not
process: false,
@@ -34,10 +37,10 @@ module.exports = function(grunt) {
parsers: {
'.js': [script.jsParser],
'.css': [style.cssParser],
- '.html': [text.html2jsParser],
+ '.html': [text.htmlParser],
'.json': [json.jsonParser],
'.tpl': [template.tplParser],
- '.handlebars': [template.handlebarsParser]
+ '.handlebars': [handlebars.handlebarsParser]
},
// for handlebars
@@ -93,6 +96,5 @@ module.exports = function(grunt) {
count++;
});
- grunt.log.writeln('transport ' + count.toString().cyan + ' files');
});
};
diff --git a/test/cases/alias/baz-debug.js.expect b/test/cases/alias/baz-debug.js.expect
index e505e55..9373308 100644
--- a/test/cases/alias/baz-debug.js.expect
+++ b/test/cases/alias/baz-debug.js.expect
@@ -2,4 +2,4 @@ define("baz-debug", [ "arale/class/foo-debug", "$-debug" ], function(require, ex
require("arale/class/foo-debug");
require("$-debug");
module.exports = "baz";
-});
\ No newline at end of file
+});
diff --git a/test/cases/alias/baz.js.expect b/test/cases/alias/baz.js.expect
index bada148..30065eb 100644
--- a/test/cases/alias/baz.js.expect
+++ b/test/cases/alias/baz.js.expect
@@ -2,4 +2,4 @@ define("baz", [ "arale/class/foo", "$" ], function(require, exports, module) {
require("arale/class/foo");
require("$");
module.exports = "baz";
-});
\ No newline at end of file
+});
diff --git a/test/cases/assets/arale.js b/test/cases/assets/arale.js
index 10b820a..7f79dc1 100644
--- a/test/cases/assets/arale.js
+++ b/test/cases/assets/arale.js
@@ -1,3 +1,3 @@
-define('arale', ['foo'], function(require, exports, module) {
+define('arale', ['arale/class/foo'], function(require, exports, module) {
module.exports = 'arale';
})
diff --git a/test/cases/assets/family/bar/bar.js b/test/cases/assets/family/bar/bar.js
new file mode 100644
index 0000000..a7eb7e9
--- /dev/null
+++ b/test/cases/assets/family/bar/bar.js
@@ -0,0 +1,3 @@
+define('family/name/bar', [], function(require, exports, module) {
+ module.exports = 'family/name/bar';
+});
diff --git a/test/cases/cmdid/foo-debug.js.expect b/test/cases/cmdid/foo-debug.js.expect
index 976a58a..cbc9c69 100644
--- a/test/cases/cmdid/foo-debug.js.expect
+++ b/test/cases/cmdid/foo-debug.js.expect
@@ -1,3 +1,3 @@
define("family/name/1.0.0/foo-debug", [], function(require, exports, module) {
module.exports = "foo";
-});
\ No newline at end of file
+});
diff --git a/test/cases/cmdid/foo.js.expect b/test/cases/cmdid/foo.js.expect
index b6d22f4..42cd3d7 100644
--- a/test/cases/cmdid/foo.js.expect
+++ b/test/cases/cmdid/foo.js.expect
@@ -1,3 +1,3 @@
define("family/name/1.0.0/foo", [], function(require, exports, module) {
module.exports = "foo";
-});
\ No newline at end of file
+});
diff --git a/test/cases/css/alias-debug.css.expect b/test/cases/css/alias-debug.css.expect
index 81811c0..8651ad5 100644
--- a/test/cases/css/alias-debug.css.expect
+++ b/test/cases/css/alias-debug.css.expect
@@ -3,4 +3,4 @@
/*! import ./ie8-debug.css */
.alias {
color: red;
-}
\ No newline at end of file
+}
diff --git a/test/cases/css/alias.css.expect b/test/cases/css/alias.css.expect
index 1f34227..669dd09 100644
--- a/test/cases/css/alias.css.expect
+++ b/test/cases/css/alias.css.expect
@@ -3,4 +3,4 @@
/*! import ./ie8.css */
.alias {
color: red;
-}
\ No newline at end of file
+}
diff --git a/test/cases/css/ie8-debug.css.expect b/test/cases/css/ie8-debug.css.expect
index 851b9a1..ddb647c 100644
--- a/test/cases/css/ie8-debug.css.expect
+++ b/test/cases/css/ie8-debug.css.expect
@@ -2,4 +2,4 @@
body {
font-size: 14px\0;
font-weight: bold;
-}
\ No newline at end of file
+}
diff --git a/test/cases/css/ie8.css.expect b/test/cases/css/ie8.css.expect
index f9c9605..79372d8 100644
--- a/test/cases/css/ie8.css.expect
+++ b/test/cases/css/ie8.css.expect
@@ -2,4 +2,4 @@
body {
font-size: 14px\0;
font-weight: bold;
-}
\ No newline at end of file
+}
diff --git a/test/cases/css/simple-debug.css.expect b/test/cases/css/simple-debug.css.expect
index 5dce05d..0e00e05 100644
--- a/test/cases/css/simple-debug.css.expect
+++ b/test/cases/css/simple-debug.css.expect
@@ -1,4 +1,4 @@
/*! define simple-debug.css */
body {
color: '#fff';
-}
\ No newline at end of file
+}
diff --git a/test/cases/css/simple.css.expect b/test/cases/css/simple.css.expect
index 807ed3d..cb0ee6d 100644
--- a/test/cases/css/simple.css.expect
+++ b/test/cases/css/simple.css.expect
@@ -1,4 +1,4 @@
/*! define simple.css */
body {
color: '#fff';
-}
\ No newline at end of file
+}
diff --git a/test/cases/css2js/a-debug.css.js.expect b/test/cases/css2js/a-debug.css.js.expect
index 2d7f3e1..120c726 100644
--- a/test/cases/css2js/a-debug.css.js.expect
+++ b/test/cases/css2js/a-debug.css.js.expect
@@ -1,3 +1,3 @@
define("a-debug.css", [], function() {
seajs.importStyle("body{color:#fff}");
-});
\ No newline at end of file
+});
diff --git a/test/cases/css2js/a.css.js.expect b/test/cases/css2js/a.css.js.expect
index 972e288..74984fc 100644
--- a/test/cases/css2js/a.css.js.expect
+++ b/test/cases/css2js/a.css.js.expect
@@ -1,3 +1,3 @@
define("a.css", [], function() {
seajs.importStyle("body{color:#fff}");
-});
\ No newline at end of file
+});
diff --git a/test/cases/directory/v.js.expect b/test/cases/directory/v.js.expect
index 961b33d..57e91c5 100644
--- a/test/cases/directory/v.js.expect
+++ b/test/cases/directory/v.js.expect
@@ -1 +1 @@
-define("v", [], function(require, exports, module) {});
\ No newline at end of file
+define("v", [], function(require, exports, module) {});
diff --git a/test/cases/directory/v/a.js.expect b/test/cases/directory/v/a.js.expect
index 17cc4ea..5d51c13 100644
--- a/test/cases/directory/v/a.js.expect
+++ b/test/cases/directory/v/a.js.expect
@@ -1,4 +1,4 @@
define("v/a", [ "../v", "./b" ], function(require, exports, module) {
require("../v");
require("./b");
-});
\ No newline at end of file
+});
diff --git a/test/cases/directory/v/b.js.expect b/test/cases/directory/v/b.js.expect
index 6a90f90..1c8c478 100644
--- a/test/cases/directory/v/b.js.expect
+++ b/test/cases/directory/v/b.js.expect
@@ -1,3 +1,3 @@
define("v/b", [ "../v" ], function(require, exports, module) {
require("../v");
-});
\ No newline at end of file
+});
diff --git a/test/cases/duplicate/a.js.expect b/test/cases/duplicate/a.js.expect
index 82951db..d9f4b30 100644
--- a/test/cases/duplicate/a.js.expect
+++ b/test/cases/duplicate/a.js.expect
@@ -1,4 +1,4 @@
define("a", [ "./b", "./d", "./c" ], function(require, exports, module) {
require("./b");
require("./c");
-});
\ No newline at end of file
+});
diff --git a/test/cases/expand/expand-debug.js.expect b/test/cases/expand/expand-debug.js.expect
index 9f73da3..d5413d8 100644
--- a/test/cases/expand/expand-debug.js.expect
+++ b/test/cases/expand/expand-debug.js.expect
@@ -1 +1 @@
-define("expand-debug", [], function() {});
\ No newline at end of file
+define("expand-debug", [], function() {});
diff --git a/test/cases/expand/expand-debug.js.expect.expect b/test/cases/expand/expand-debug.js.expect.expect
index 9f73da3..d5413d8 100644
--- a/test/cases/expand/expand-debug.js.expect.expect
+++ b/test/cases/expand/expand-debug.js.expect.expect
@@ -1 +1 @@
-define("expand-debug", [], function() {});
\ No newline at end of file
+define("expand-debug", [], function() {});
diff --git a/test/cases/expand/expand.js.expect b/test/cases/expand/expand.js.expect
index 78e009b..2f0f5d8 100644
--- a/test/cases/expand/expand.js.expect
+++ b/test/cases/expand/expand.js.expect
@@ -1 +1 @@
-define("expand", [], function() {});
\ No newline at end of file
+define("expand", [], function() {});
diff --git a/test/cases/expand/expand.js.expect.expect b/test/cases/expand/expand.js.expect.expect
index 78e009b..2f0f5d8 100644
--- a/test/cases/expand/expand.js.expect.expect
+++ b/test/cases/expand/expand.js.expect.expect
@@ -1 +1 @@
-define("expand", [], function() {});
\ No newline at end of file
+define("expand", [], function() {});
diff --git a/test/cases/handlebars/month-debug.handlebars.js.expect b/test/cases/handlebars/month-debug.handlebars.js.expect
index ade6979..beb5c12 100644
--- a/test/cases/handlebars/month-debug.handlebars.js.expect
+++ b/test/cases/handlebars/month-debug.handlebars.js.expect
@@ -70,4 +70,4 @@ define("month-debug.handlebars", [ "gallery/handlebars/1.0.2/runtime-debug" ], f
buffer += "\n\n";
return buffer;
});
-});
\ No newline at end of file
+});
diff --git a/test/cases/handlebars/month.handlebars.js.expect b/test/cases/handlebars/month.handlebars.js.expect
index 95fec75..26f9b5a 100644
--- a/test/cases/handlebars/month.handlebars.js.expect
+++ b/test/cases/handlebars/month.handlebars.js.expect
@@ -70,4 +70,4 @@ define("month.handlebars", [ "gallery/handlebars/1.0.2/runtime" ], function(requ
buffer += "\n\n";
return buffer;
});
-});
\ No newline at end of file
+});
diff --git a/test/cases/handlebars/simple-debug.handlebars.js.expect b/test/cases/handlebars/simple-debug.handlebars.js.expect
index c79a193..c51270f 100644
--- a/test/cases/handlebars/simple-debug.handlebars.js.expect
+++ b/test/cases/handlebars/simple-debug.handlebars.js.expect
@@ -31,4 +31,4 @@ define("simple-debug.handlebars", [ "gallery/handlebars/1.0.2/runtime-debug" ],
buffer += "\n";
return buffer;
});
-});
\ No newline at end of file
+});
diff --git a/test/cases/handlebars/simple.handlebars.js.expect b/test/cases/handlebars/simple.handlebars.js.expect
index 82e8b8f..8885c5b 100644
--- a/test/cases/handlebars/simple.handlebars.js.expect
+++ b/test/cases/handlebars/simple.handlebars.js.expect
@@ -31,4 +31,4 @@ define("simple.handlebars", [ "gallery/handlebars/1.0.2/runtime" ], function(req
buffer += "\n";
return buffer;
});
-});
\ No newline at end of file
+});
diff --git a/test/cases/hash/a-da24e8b4.js.expect b/test/cases/hash/a-da24e8b4.js.expect
new file mode 100644
index 0000000..b37066b
--- /dev/null
+++ b/test/cases/hash/a-da24e8b4.js.expect
@@ -0,0 +1,4 @@
+define("family/name/a-da24e8b4", [ "./b-5821d03c", "./c/c-5bffe50a", "$", "family/bar/bar", "arale/class/foo" ], function(require, exports, module) {
+ require("./b-5821d03c.js");
+ require("arale/class/foo");
+});
diff --git a/test/cases/hash/a.js b/test/cases/hash/a.js
new file mode 100644
index 0000000..d0353ba
--- /dev/null
+++ b/test/cases/hash/a.js
@@ -0,0 +1,4 @@
+define(function(require, exports, module) {
+ require('./b');
+ require('foo');
+});
diff --git a/test/cases/hash/b-5821d03c.js.expect b/test/cases/hash/b-5821d03c.js.expect
new file mode 100644
index 0000000..d4eaf73
--- /dev/null
+++ b/test/cases/hash/b-5821d03c.js.expect
@@ -0,0 +1,4 @@
+define("family/name/b-5821d03c", [ "./c/c-5bffe50a", "$", "family/bar/bar" ], function(require, exports, module) {
+ require("./c/c-5bffe50a.js");
+ require("family/bar/bar");
+});
diff --git a/test/cases/hash/b.js b/test/cases/hash/b.js
new file mode 100644
index 0000000..312f769
--- /dev/null
+++ b/test/cases/hash/b.js
@@ -0,0 +1,4 @@
+define(function(require, exports, module) {
+ require('./c/c');
+ require('bar');
+});
diff --git a/test/cases/hash/c/c.js b/test/cases/hash/c/c.js
new file mode 100644
index 0000000..3d14f0b
--- /dev/null
+++ b/test/cases/hash/c/c.js
@@ -0,0 +1,3 @@
+define(function(require, exports, module) {
+ require('$');
+});
diff --git a/test/cases/id-deps-exist/a.js.expect b/test/cases/id-deps-exist/a.js.expect
index 1fbc12c..6aee1fe 100644
--- a/test/cases/id-deps-exist/a.js.expect
+++ b/test/cases/id-deps-exist/a.js.expect
@@ -1,3 +1,3 @@
define("a", [ "arale/class/foo" ], function(require, exports, module) {
require("arale/class/foo");
-});
\ No newline at end of file
+});
diff --git a/test/cases/id-deps-exist/b.js.expect b/test/cases/id-deps-exist/b.js.expect
index 0804a86..24bcb45 100644
--- a/test/cases/id-deps-exist/b.js.expect
+++ b/test/cases/id-deps-exist/b.js.expect
@@ -1,3 +1,3 @@
define("b", [ "./a" ], function(require, exports, module) {
require("foo");
-});
\ No newline at end of file
+});
diff --git a/test/cases/id-deps-exist/c.js.expect b/test/cases/id-deps-exist/c.js.expect
index 434bc06..d0deb6c 100644
--- a/test/cases/id-deps-exist/c.js.expect
+++ b/test/cases/id-deps-exist/c.js.expect
@@ -1,3 +1,3 @@
define("c", [ "./a" ], function(require, exports, module) {
require("foo");
-});
\ No newline at end of file
+});
diff --git a/test/cases/json/simple-debug.json.js.expect b/test/cases/json/simple-debug.json.js.expect
index fb91672..bc6f541 100644
--- a/test/cases/json/simple-debug.json.js.expect
+++ b/test/cases/json/simple-debug.json.js.expect
@@ -2,4 +2,4 @@ define("simple-debug.json", [], {
foo: "bar",
baz: "foobar",
test: 255
-});
\ No newline at end of file
+});
diff --git a/test/cases/json/simple.json.js.expect b/test/cases/json/simple.json.js.expect
index 5645f24..415f088 100644
--- a/test/cases/json/simple.json.js.expect
+++ b/test/cases/json/simple.json.js.expect
@@ -2,4 +2,4 @@ define("simple.json", [], {
foo: "bar",
baz: "foobar",
test: 255
-});
\ No newline at end of file
+});
diff --git a/test/cases/nested/bar/a.js.expect b/test/cases/nested/bar/a.js.expect
index 92c23f5..81ef367 100644
--- a/test/cases/nested/bar/a.js.expect
+++ b/test/cases/nested/bar/a.js.expect
@@ -1,4 +1,4 @@
define("bar/a", [ "./b", "./c", "../foo/a", "../foo/b", "../foo/c" ], function(require, exports, module) {
exports.a = require("./b");
exports.c = require("./c");
-});
\ No newline at end of file
+});
diff --git a/test/cases/nested/bar/b.js.expect b/test/cases/nested/bar/b.js.expect
index 8bfcf77..c538bcd 100644
--- a/test/cases/nested/bar/b.js.expect
+++ b/test/cases/nested/bar/b.js.expect
@@ -1,3 +1,3 @@
define("bar/b", [ "./c", "../foo/a", "../foo/b", "../foo/c" ], function(require, exports, module) {
exports.b = require("./c");
-});
\ No newline at end of file
+});
diff --git a/test/cases/nested/bar/c.js.expect b/test/cases/nested/bar/c.js.expect
index 7042707..99c92ca 100644
--- a/test/cases/nested/bar/c.js.expect
+++ b/test/cases/nested/bar/c.js.expect
@@ -1,3 +1,3 @@
define("bar/c", [ "../foo/a", "../foo/b", "../foo/c" ], function(require, exports, module) {
exports.c = require("../foo/a");
-});
\ No newline at end of file
+});
diff --git a/test/cases/nested/foo/a.js.expect b/test/cases/nested/foo/a.js.expect
index 723965e..66cb656 100644
--- a/test/cases/nested/foo/a.js.expect
+++ b/test/cases/nested/foo/a.js.expect
@@ -1,3 +1,3 @@
define("foo/a", [ "./b", "./c" ], function(require, exports, module) {
exports.a = require("./b");
-});
\ No newline at end of file
+});
diff --git a/test/cases/nested/foo/b.js.expect b/test/cases/nested/foo/b.js.expect
index cdd9c1e..229f243 100644
--- a/test/cases/nested/foo/b.js.expect
+++ b/test/cases/nested/foo/b.js.expect
@@ -1,3 +1,3 @@
define("foo/b", [ "./c" ], function(require, exports, module) {
exports.b = require("./c");
-});
\ No newline at end of file
+});
diff --git a/test/cases/nested/foo/c.js.expect b/test/cases/nested/foo/c.js.expect
index d3572f5..85f55a8 100644
--- a/test/cases/nested/foo/c.js.expect
+++ b/test/cases/nested/foo/c.js.expect
@@ -1,3 +1,3 @@
define("foo/c", [], function(require, exports, module) {
exports.c = "c";
-});
\ No newline at end of file
+});
diff --git a/test/cases/project/a-2cee5097.html.js.expect b/test/cases/project/a-2cee5097.html.js.expect
new file mode 100644
index 0000000..d8f7029
--- /dev/null
+++ b/test/cases/project/a-2cee5097.html.js.expect
@@ -0,0 +1 @@
+define("family/name/a-2cee5097.html", [], "
");
diff --git a/test/cases/project/a-33cd4a1a-debug.js.expect b/test/cases/project/a-33cd4a1a-debug.js.expect
new file mode 100644
index 0000000..439987c
--- /dev/null
+++ b/test/cases/project/a-33cd4a1a-debug.js.expect
@@ -0,0 +1,9 @@
+define("family/name/a-33cd4a1a-debug", [ "./a-670b8177-debug.handlebars", "./a-d41d8cd9-debug.json", "./a-2cee5097-debug.html", "./a-d41d8cd9-debug.tpl", "./a-e1f4111c-debug.css", "./b-0ec4c8ca-debug", "arale/base/1.1.1/base-debug", "arale/class/1.1.0/class-debug", "arale/events/1.1.0/events-debug", "alice/loading/1.0.0/loading-debug.css", "arale/dialog/1.3.1/confirmbox-debug", "arale/overlay/1.1.4/overlay-debug", "arale/position/1.0.1/position-debug", "arale/iframe-shim/1.0.2/iframe-shim-debug", "arale/widget/1.1.1/widget-debug", "arale/overlay/1.1.4/mask-debug", "arale/templatable/0.9.2/templatable-debug", "gallery/handlebars/1.0.2/handlebars-debug", "gallery/handlebars/1.0.2/runtime-debug" ], function(require) {
+ require("./a-670b8177-debug.handlebars");
+ require("./a-d41d8cd9-debug.json");
+ require("./a-2cee5097-debug.html");
+ require("./a-d41d8cd9-debug.tpl");
+ require("./a-e1f4111c-debug.css");
+ require("./b-0ec4c8ca-debug.js");
+ require("arale/dialog/1.3.1/confirmbox-debug");
+});
diff --git a/test/cases/project/a-33cd4a1a.js.expect b/test/cases/project/a-33cd4a1a.js.expect
new file mode 100644
index 0000000..87abecd
--- /dev/null
+++ b/test/cases/project/a-33cd4a1a.js.expect
@@ -0,0 +1,9 @@
+define("family/name/a-33cd4a1a", [ "./a-670b8177.handlebars", "./a-d41d8cd9.json", "./a-2cee5097.html", "./a-d41d8cd9.tpl", "./a-e1f4111c.css", "./b-0ec4c8ca", "arale/base/1.1.1/base", "arale/class/1.1.0/class", "arale/events/1.1.0/events", "alice/loading/1.0.0/loading.css", "arale/dialog/1.3.1/confirmbox", "arale/overlay/1.1.4/overlay", "arale/position/1.0.1/position", "arale/iframe-shim/1.0.2/iframe-shim", "arale/widget/1.1.1/widget", "arale/overlay/1.1.4/mask", "arale/templatable/0.9.2/templatable", "gallery/handlebars/1.0.2/handlebars", "gallery/handlebars/1.0.2/runtime" ], function(require) {
+ require("./a-670b8177.handlebars");
+ require("./a-d41d8cd9.json");
+ require("./a-2cee5097.html");
+ require("./a-d41d8cd9.tpl");
+ require("./a-e1f4111c.css");
+ require("./b-0ec4c8ca.js");
+ require("arale/dialog/1.3.1/confirmbox");
+});
diff --git a/test/cases/project/a-670b8177.handlebars.js.expect b/test/cases/project/a-670b8177.handlebars.js.expect
new file mode 100644
index 0000000..747d3ee
--- /dev/null
+++ b/test/cases/project/a-670b8177.handlebars.js.expect
@@ -0,0 +1,28 @@
+define("family/name/a-670b8177.handlebars", [ "gallery/handlebars/1.0.2/runtime" ], function(require, exports, module) {
+ var Handlebars = require("gallery/handlebars/1.0.2/runtime");
+ var template = Handlebars.template;
+ module.exports = template(function(Handlebars, depth0, helpers, partials, data) {
+ this.compilerInfo = [ 3, ">= 1.0.0-rc.4" ];
+ helpers = helpers || {};
+ for (var key in Handlebars.helpers) {
+ helpers[key] = helpers[key] || Handlebars.helpers[key];
+ }
+ data = data || {};
+ var buffer = "", stack1, functionType = "function";
+ buffer += "";
+ if (stack1 = helpers.data) {
+ stack1 = stack1.call(depth0, {
+ hash: {},
+ data: data
+ });
+ } else {
+ stack1 = depth0.data;
+ stack1 = typeof stack1 === functionType ? stack1.apply(depth0) : stack1;
+ }
+ if (stack1 || stack1 === 0) {
+ buffer += stack1;
+ }
+ buffer += "
\n";
+ return buffer;
+ });
+});
diff --git a/test/cases/project/a-d41d8cd9-debug.json.js.expect b/test/cases/project/a-d41d8cd9-debug.json.js.expect
new file mode 100644
index 0000000..89906c6
--- /dev/null
+++ b/test/cases/project/a-d41d8cd9-debug.json.js.expect
@@ -0,0 +1 @@
+define("family/name/a-d41d8cd9-debug.json", [], {});
diff --git a/test/cases/project/a-d41d8cd9.json.js.expect b/test/cases/project/a-d41d8cd9.json.js.expect
new file mode 100644
index 0000000..96c1ec4
--- /dev/null
+++ b/test/cases/project/a-d41d8cd9.json.js.expect
@@ -0,0 +1 @@
+define("family/name/a-d41d8cd9.json", [], {});
diff --git a/test/cases/project/a-d41d8cd9.tpl.js.expect b/test/cases/project/a-d41d8cd9.tpl.js.expect
new file mode 100644
index 0000000..ee43aac
--- /dev/null
+++ b/test/cases/project/a-d41d8cd9.tpl.js.expect
@@ -0,0 +1 @@
+define("family/name/a-d41d8cd9.tpl", [], "");
diff --git a/test/cases/project/a-e1f4111c-debug.css.expect b/test/cases/project/a-e1f4111c-debug.css.expect
new file mode 100644
index 0000000..1067234
--- /dev/null
+++ b/test/cases/project/a-e1f4111c-debug.css.expect
@@ -0,0 +1,5 @@
+/*! define family/name/a-e1f4111c-debug.css */
+/*! import ./b-debug.css */
+ul {
+ margin: 0;
+}
diff --git a/test/cases/project/a-e1f4111c.css.expect b/test/cases/project/a-e1f4111c.css.expect
new file mode 100644
index 0000000..fd14e9d
--- /dev/null
+++ b/test/cases/project/a-e1f4111c.css.expect
@@ -0,0 +1,5 @@
+/*! define family/name/a-e1f4111c.css */
+/*! import ./b-aa896723.css */
+ul {
+ margin: 0;
+}
diff --git a/test/cases/project/a.css b/test/cases/project/a.css
new file mode 100644
index 0000000..5e5e336
--- /dev/null
+++ b/test/cases/project/a.css
@@ -0,0 +1,5 @@
+@import "./b.css";
+
+ul {
+ margin: 0;
+}
diff --git a/test/cases/project/a.handlebars b/test/cases/project/a.handlebars
new file mode 100644
index 0000000..52e672a
--- /dev/null
+++ b/test/cases/project/a.handlebars
@@ -0,0 +1 @@
+{{{data}}}
diff --git a/test/cases/project/a.html b/test/cases/project/a.html
new file mode 100644
index 0000000..e69de94
--- /dev/null
+++ b/test/cases/project/a.html
@@ -0,0 +1,5 @@
+
diff --git a/test/cases/project/a.js b/test/cases/project/a.js
new file mode 100644
index 0000000..05d5952
--- /dev/null
+++ b/test/cases/project/a.js
@@ -0,0 +1,9 @@
+define(function(require) {
+ require('./a.handlebars');
+ require('./a.json');
+ require('./a.html');
+ require('./a.tpl');
+ require('./a.css');
+ require('./b');
+ require('confirmbox');
+});
diff --git a/test/cases/project/a.json b/test/cases/project/a.json
new file mode 100644
index 0000000..e69de29
diff --git a/test/cases/project/a.tpl b/test/cases/project/a.tpl
new file mode 100644
index 0000000..e69de29
diff --git a/test/cases/project/b-0ec4c8ca.js.expect b/test/cases/project/b-0ec4c8ca.js.expect
new file mode 100644
index 0000000..bc952c6
--- /dev/null
+++ b/test/cases/project/b-0ec4c8ca.js.expect
@@ -0,0 +1,4 @@
+define("family/name/b-0ec4c8ca", [ "arale/base/1.1.1/base", "arale/class/1.1.0/class", "arale/events/1.1.0/events", "alice/loading/1.0.0/loading.css" ], function(require) {
+ require("arale/base/1.1.1/base");
+ require("alice/loading/1.0.0/loading.css");
+});
diff --git a/test/cases/project/b-aa896723-debug.css.expect b/test/cases/project/b-aa896723-debug.css.expect
new file mode 100644
index 0000000..525169b
--- /dev/null
+++ b/test/cases/project/b-aa896723-debug.css.expect
@@ -0,0 +1,5 @@
+/*! define family/name/b-aa896723-debug.css */
+/*! import alice/list/1.0.1/list-debug.css */
+html {
+ margin: 0;
+}
diff --git a/test/cases/project/b-aa896723.css.expect b/test/cases/project/b-aa896723.css.expect
new file mode 100644
index 0000000..bfeaa53
--- /dev/null
+++ b/test/cases/project/b-aa896723.css.expect
@@ -0,0 +1,5 @@
+/*! define family/name/b-aa896723.css */
+/*! import alice/list/1.0.1/list.css */
+html {
+ margin: 0;
+}
diff --git a/test/cases/project/b.css b/test/cases/project/b.css
new file mode 100644
index 0000000..1c544ed
--- /dev/null
+++ b/test/cases/project/b.css
@@ -0,0 +1,5 @@
+@import "list"
+
+html {
+ margin: 0;
+}
diff --git a/test/cases/project/b.js b/test/cases/project/b.js
new file mode 100644
index 0000000..61869ca
--- /dev/null
+++ b/test/cases/project/b.js
@@ -0,0 +1,4 @@
+define(function(require) {
+ require('base');
+ require('loading');
+});
diff --git a/test/cases/project/sea-modules/alice/list/1.0.1/list-debug.css b/test/cases/project/sea-modules/alice/list/1.0.1/list-debug.css
new file mode 100644
index 0000000..b76f2c0
--- /dev/null
+++ b/test/cases/project/sea-modules/alice/list/1.0.1/list-debug.css
@@ -0,0 +1,70 @@
+/*! define alice/list/1.0.1/list-debug.css */
+/* alice.list 样式模块 */
+
+.ui-list {
+ margin: 0;
+ padding: 10px;
+ list-style: square inside;
+}
+/* 默认有方角 */
+.ui-list-item {
+ font-size: 9px;
+ line-height: 20px;
+}
+.ui-list-item a {
+ line-height: 20px;
+ text-decoration: none;
+ color: #08c;
+}
+
+.ui-list-item a,
+.ui-list-item span.ui-list-item-text {
+ font-size: 12px;
+ vertical-align: middle;
+}
+
+.ui-list-item a:hover {
+ text-decoration: underline;
+}
+
+/* 灰色小方角 */
+.ui-list-gray .ui-list-item {
+ color: #808080;
+}
+
+/* 灰色小方角+灰色链接 */
+.ui-list-graylink .ui-list-item {
+ color: #808080;
+}
+
+.ui-list-graylink .ui-list-item a {
+ color: #666;
+}
+
+/* 没有小图标的 */
+.ui-list-nosquare {
+ list-style: none;
+}
+
+/* ui-dlist */
+
+.ui-dlist {
+ display: inline-block;
+ color: #808080;
+ font-size: 12px;
+ line-height: 2.2;
+}
+
+.ui-dlist-tit {
+ float: left;
+ width: 20%;/* 默认值, 具体根据视觉可改 */
+ text-align: right;
+ margin: 0;
+}
+
+.ui-dlist-det {
+ float: left;
+ width: 80%;/* 默认值,具体根据视觉可改 */
+ text-align: left;
+ margin: 0;
+}
diff --git a/test/cases/project/sea-modules/alice/list/1.0.1/list.css b/test/cases/project/sea-modules/alice/list/1.0.1/list.css
new file mode 100644
index 0000000..cad8f96
--- /dev/null
+++ b/test/cases/project/sea-modules/alice/list/1.0.1/list.css
@@ -0,0 +1 @@
+.ui-list{margin:0;padding:10px;list-style:square inside}.ui-list-item{font-size:9px;line-height:20px}.ui-list-item a{line-height:20px;text-decoration:none;color:#08c}.ui-list-item a,.ui-list-item span.ui-list-item-text{font-size:12px;vertical-align:middle}.ui-list-item a:hover{text-decoration:underline}.ui-list-gray .ui-list-item{color:gray}.ui-list-graylink .ui-list-item{color:gray}.ui-list-graylink .ui-list-item a{color:#666}.ui-list-nosquare{list-style:none}.ui-dlist{display:inline-block;color:gray;font-size:12px;line-height:2.2}.ui-dlist-tit{float:left;width:20%;text-align:right;margin:0}.ui-dlist-det{float:left;width:80%;text-align:left;margin:0}
diff --git a/test/cases/project/sea-modules/alice/list/1.0.1/package.json b/test/cases/project/sea-modules/alice/list/1.0.1/package.json
new file mode 100644
index 0000000..8924252
--- /dev/null
+++ b/test/cases/project/sea-modules/alice/list/1.0.1/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "list",
+ "version": "1.0.1",
+ "family": "alice",
+ "description": "通用列表样式。",
+ "keywords": [
+ "列表"
+ ],
+ "homepage": "http://aliceui.org/list",
+ "author": "",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/aliceui/list"
+ },
+ "bugs": {
+ "url": "https://github.com/aliceui/list/issues"
+ },
+ "license": "MIT",
+ "spm": {
+ "alias": {
+ },
+ "output": ["list.css"]
+ }
+}
diff --git a/test/cases/project/sea-modules/alice/loading/1.0.0/loading-debug.css b/test/cases/project/sea-modules/alice/loading/1.0.0/loading-debug.css
new file mode 100644
index 0000000..240291d
--- /dev/null
+++ b/test/cases/project/sea-modules/alice/loading/1.0.0/loading-debug.css
@@ -0,0 +1,14 @@
+/*! define alice/loading/1.0.0/loading-debug.css */
+/* alice.loading 样式模块 */
+
+.ui-loading {
+ width: 50px;
+ height: 50px;
+ background-repeat: no-repeat;
+ background-image: url('data:image/gif;base64,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');
+ *background-image: url(https://i.alipayobjects.com/e/201305/M9UQl3TuH.gif);
+ text-align:center;
+ line-height: 50px;
+ font-size: 11px;
+ color: #777;
+}
diff --git a/test/cases/project/sea-modules/alice/loading/1.0.0/loading.css b/test/cases/project/sea-modules/alice/loading/1.0.0/loading.css
new file mode 100644
index 0000000..8ca19e7
--- /dev/null
+++ b/test/cases/project/sea-modules/alice/loading/1.0.0/loading.css
@@ -0,0 +1 @@
+.ui-loading{width:50px;height:50px;background-repeat:no-repeat;background-image:url('data:image/gif;base64,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');*background-image:url(https://i.alipayobjects.com/e/201305/M9UQl3TuH.gif);text-align:center;line-height:50px;font-size:11px;color:#777}
diff --git a/test/cases/project/sea-modules/alice/loading/1.0.0/package.json b/test/cases/project/sea-modules/alice/loading/1.0.0/package.json
new file mode 100644
index 0000000..a3499ea
--- /dev/null
+++ b/test/cases/project/sea-modules/alice/loading/1.0.0/package.json
@@ -0,0 +1,23 @@
+{
+ "family": "alice",
+ "name": "loading",
+ "description": "alice loading style.",
+ "version": "1.0.0",
+ "homepage": "http://aliceui.org/loading/",
+ "keywords": [
+ "加载中"
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/aliceui/loading/"
+ },
+ "bugs": {
+ "url": "https://github.com/aliceui/loading/issues"
+ },
+ "spm": {
+ "alias": {},
+ "output": [
+ "loading.css"
+ ]
+ }
+}
diff --git a/test/cases/project/sea-modules/arale/base/1.1.1/base-debug.js b/test/cases/project/sea-modules/arale/base/1.1.1/base-debug.js
new file mode 100644
index 0000000..04ef152
--- /dev/null
+++ b/test/cases/project/sea-modules/arale/base/1.1.1/base-debug.js
@@ -0,0 +1,462 @@
+define("arale/base/1.1.1/base-debug", [ "arale/class/1.1.0/class-debug", "arale/events/1.1.0/events-debug", "./aspect-debug", "./attribute-debug" ], function(require, exports, module) {
+ // Base
+ // ---------
+ // Base 是一个基础类,提供 Class、Events、Attrs 和 Aspect 支持。
+ var Class = require("arale/class/1.1.0/class-debug");
+ var Events = require("arale/events/1.1.0/events-debug");
+ var Aspect = require("./aspect-debug");
+ var Attribute = require("./attribute-debug");
+ module.exports = Class.create({
+ Implements: [ Events, Aspect, Attribute ],
+ initialize: function(config) {
+ this.initAttrs(config);
+ // Automatically register `this._onChangeAttr` method as
+ // a `change:attr` event handler.
+ parseEventsFromInstance(this, this.attrs);
+ },
+ destroy: function() {
+ this.off();
+ for (var p in this) {
+ if (this.hasOwnProperty(p)) {
+ delete this[p];
+ }
+ }
+ // Destroy should be called only once, generate a fake destroy after called
+ // https://github.com/aralejs/widget/issues/50
+ this.destroy = function() {};
+ }
+ });
+ function parseEventsFromInstance(host, attrs) {
+ for (var attr in attrs) {
+ if (attrs.hasOwnProperty(attr)) {
+ var m = "_onChange" + ucfirst(attr);
+ if (host[m]) {
+ host.on("change:" + attr, host[m]);
+ }
+ }
+ }
+ }
+ function ucfirst(str) {
+ return str.charAt(0).toUpperCase() + str.substring(1);
+ }
+});
+
+define("arale/base/1.1.1/aspect-debug", [], function(require, exports) {
+ // Aspect
+ // ---------------------
+ // Thanks to:
+ // - http://yuilibrary.com/yui/docs/api/classes/Do.html
+ // - http://code.google.com/p/jquery-aop/
+ // - http://lazutkin.com/blog/2008/may/18/aop-aspect-javascript-dojo/
+ // 在指定方法执行前,先执行 callback
+ exports.before = function(methodName, callback, context) {
+ return weave.call(this, "before", methodName, callback, context);
+ };
+ // 在指定方法执行后,再执行 callback
+ exports.after = function(methodName, callback, context) {
+ return weave.call(this, "after", methodName, callback, context);
+ };
+ // Helpers
+ // -------
+ var eventSplitter = /\s+/;
+ function weave(when, methodName, callback, context) {
+ var names = methodName.split(eventSplitter);
+ var name, method;
+ while (name = names.shift()) {
+ method = getMethod(this, name);
+ if (!method.__isAspected) {
+ wrap.call(this, name);
+ }
+ this.on(when + ":" + name, callback, context);
+ }
+ return this;
+ }
+ function getMethod(host, methodName) {
+ var method = host[methodName];
+ if (!method) {
+ throw new Error("Invalid method name: " + methodName);
+ }
+ return method;
+ }
+ function wrap(methodName) {
+ var old = this[methodName];
+ this[methodName] = function() {
+ var args = Array.prototype.slice.call(arguments);
+ var beforeArgs = [ "before:" + methodName ].concat(args);
+ // prevent if trigger return false
+ if (this.trigger.apply(this, beforeArgs) === false) return;
+ var ret = old.apply(this, arguments);
+ var afterArgs = [ "after:" + methodName, ret ].concat(args);
+ this.trigger.apply(this, afterArgs);
+ return ret;
+ };
+ this[methodName].__isAspected = true;
+ }
+});
+
+define("arale/base/1.1.1/attribute-debug", [], function(require, exports) {
+ // Attribute
+ // -----------------
+ // Thanks to:
+ // - http://documentcloud.github.com/backbone/#Model
+ // - http://yuilibrary.com/yui/docs/api/classes/AttributeCore.html
+ // - https://github.com/berzniz/backbone.getters.setters
+ // 负责 attributes 的初始化
+ // attributes 是与实例相关的状态信息,可读可写,发生变化时,会自动触发相关事件
+ exports.initAttrs = function(config) {
+ // initAttrs 是在初始化时调用的,默认情况下实例上肯定没有 attrs,不存在覆盖问题
+ var attrs = this.attrs = {};
+ // Get all inherited attributes.
+ var specialProps = this.propsInAttrs || [];
+ mergeInheritedAttrs(attrs, this, specialProps);
+ // Merge user-specific attributes from config.
+ if (config) {
+ mergeUserValue(attrs, config);
+ }
+ // 对于有 setter 的属性,要用初始值 set 一下,以保证关联属性也一同初始化
+ setSetterAttrs(this, attrs, config);
+ // Convert `on/before/afterXxx` config to event handler.
+ parseEventsFromAttrs(this, attrs);
+ // 将 this.attrs 上的 special properties 放回 this 上
+ copySpecialProps(specialProps, this, attrs, true);
+ };
+ // Get the value of an attribute.
+ exports.get = function(key) {
+ var attr = this.attrs[key] || {};
+ var val = attr.value;
+ return attr.getter ? attr.getter.call(this, val, key) : val;
+ };
+ // Set a hash of model attributes on the object, firing `"change"` unless
+ // you choose to silence it.
+ exports.set = function(key, val, options) {
+ var attrs = {};
+ // set("key", val, options)
+ if (isString(key)) {
+ attrs[key] = val;
+ } else {
+ attrs = key;
+ options = val;
+ }
+ options || (options = {});
+ var silent = options.silent;
+ var override = options.override;
+ var now = this.attrs;
+ var changed = this.__changedAttrs || (this.__changedAttrs = {});
+ for (key in attrs) {
+ if (!attrs.hasOwnProperty(key)) continue;
+ var attr = now[key] || (now[key] = {});
+ val = attrs[key];
+ if (attr.readOnly) {
+ throw new Error("This attribute is readOnly: " + key);
+ }
+ // invoke setter
+ if (attr.setter) {
+ val = attr.setter.call(this, val, key);
+ }
+ // 获取设置前的 prev 值
+ var prev = this.get(key);
+ // 获取需要设置的 val 值
+ // 如果设置了 override 为 true,表示要强制覆盖,就不去 merge 了
+ // 都为对象时,做 merge 操作,以保留 prev 上没有覆盖的值
+ if (!override && isPlainObject(prev) && isPlainObject(val)) {
+ val = merge(merge({}, prev), val);
+ }
+ // set finally
+ now[key].value = val;
+ // invoke change event
+ // 初始化时对 set 的调用,不触发任何事件
+ if (!this.__initializingAttrs && !isEqual(prev, val)) {
+ if (silent) {
+ changed[key] = [ val, prev ];
+ } else {
+ this.trigger("change:" + key, val, prev, key);
+ }
+ }
+ }
+ return this;
+ };
+ // Call this method to manually fire a `"change"` event for triggering
+ // a `"change:attribute"` event for each changed attribute.
+ exports.change = function() {
+ var changed = this.__changedAttrs;
+ if (changed) {
+ for (var key in changed) {
+ if (changed.hasOwnProperty(key)) {
+ var args = changed[key];
+ this.trigger("change:" + key, args[0], args[1], key);
+ }
+ }
+ delete this.__changedAttrs;
+ }
+ return this;
+ };
+ // for test
+ exports._isPlainObject = isPlainObject;
+ // Helpers
+ // -------
+ var toString = Object.prototype.toString;
+ var hasOwn = Object.prototype.hasOwnProperty;
+ /**
+ * Detect the JScript [[DontEnum]] bug:
+ * In IE < 9 an objects own properties, shadowing non-enumerable ones, are
+ * made non-enumerable as well.
+ * https://github.com/bestiejs/lodash/blob/7520066fc916e205ef84cb97fbfe630d7c154158/lodash.js#L134-L144
+ */
+ /** Detect if own properties are iterated after inherited properties (IE < 9) */
+ var iteratesOwnLast;
+ (function() {
+ var props = [];
+ function Ctor() {
+ this.x = 1;
+ }
+ Ctor.prototype = {
+ valueOf: 1,
+ y: 1
+ };
+ for (var prop in new Ctor()) {
+ props.push(prop);
+ }
+ iteratesOwnLast = props[0] !== "x";
+ })();
+ var isArray = Array.isArray || function(val) {
+ return toString.call(val) === "[object Array]";
+ };
+ function isString(val) {
+ return toString.call(val) === "[object String]";
+ }
+ function isFunction(val) {
+ return toString.call(val) === "[object Function]";
+ }
+ function isWindow(o) {
+ return o != null && o == o.window;
+ }
+ function isPlainObject(o) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor
+ // property. Make sure that DOM nodes and window objects don't
+ // pass through, as well
+ if (!o || toString.call(o) !== "[object Object]" || o.nodeType || isWindow(o)) {
+ return false;
+ }
+ try {
+ // Not own constructor property must be Object
+ if (o.constructor && !hasOwn.call(o, "constructor") && !hasOwn.call(o.constructor.prototype, "isPrototypeOf")) {
+ return false;
+ }
+ } catch (e) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+ var key;
+ // Support: IE<9
+ // Handle iteration over inherited properties before own properties.
+ // http://bugs.jquery.com/ticket/12199
+ if (iteratesOwnLast) {
+ for (key in o) {
+ return hasOwn.call(o, key);
+ }
+ }
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+ for (key in o) {}
+ return key === undefined || hasOwn.call(o, key);
+ }
+ function isEmptyObject(o) {
+ if (!o || toString.call(o) !== "[object Object]" || o.nodeType || isWindow(o) || !o.hasOwnProperty) {
+ return false;
+ }
+ for (var p in o) {
+ if (o.hasOwnProperty(p)) return false;
+ }
+ return true;
+ }
+ function merge(receiver, supplier) {
+ var key, value;
+ for (key in supplier) {
+ if (supplier.hasOwnProperty(key)) {
+ value = supplier[key];
+ // 只 clone 数组和 plain object,其他的保持不变
+ if (isArray(value)) {
+ value = value.slice();
+ } else if (isPlainObject(value)) {
+ var prev = receiver[key];
+ isPlainObject(prev) || (prev = {});
+ value = merge(prev, value);
+ }
+ receiver[key] = value;
+ }
+ }
+ return receiver;
+ }
+ var keys = Object.keys;
+ if (!keys) {
+ keys = function(o) {
+ var result = [];
+ for (var name in o) {
+ if (o.hasOwnProperty(name)) {
+ result.push(name);
+ }
+ }
+ return result;
+ };
+ }
+ function mergeInheritedAttrs(attrs, instance, specialProps) {
+ var inherited = [];
+ var proto = instance.constructor.prototype;
+ while (proto) {
+ // 不要拿到 prototype 上的
+ if (!proto.hasOwnProperty("attrs")) {
+ proto.attrs = {};
+ }
+ // 将 proto 上的特殊 properties 放到 proto.attrs 上,以便合并
+ copySpecialProps(specialProps, proto.attrs, proto);
+ // 为空时不添加
+ if (!isEmptyObject(proto.attrs)) {
+ inherited.unshift(proto.attrs);
+ }
+ // 向上回溯一级
+ proto = proto.constructor.superclass;
+ }
+ // Merge and clone default values to instance.
+ for (var i = 0, len = inherited.length; i < len; i++) {
+ merge(attrs, normalize(inherited[i]));
+ }
+ }
+ function mergeUserValue(attrs, config) {
+ merge(attrs, normalize(config, true));
+ }
+ function copySpecialProps(specialProps, receiver, supplier, isAttr2Prop) {
+ for (var i = 0, len = specialProps.length; i < len; i++) {
+ var key = specialProps[i];
+ if (supplier.hasOwnProperty(key)) {
+ receiver[key] = isAttr2Prop ? receiver.get(key) : supplier[key];
+ }
+ }
+ }
+ var EVENT_PATTERN = /^(on|before|after)([A-Z].*)$/;
+ var EVENT_NAME_PATTERN = /^(Change)?([A-Z])(.*)/;
+ function parseEventsFromAttrs(host, attrs) {
+ for (var key in attrs) {
+ if (attrs.hasOwnProperty(key)) {
+ var value = attrs[key].value, m;
+ if (isFunction(value) && (m = key.match(EVENT_PATTERN))) {
+ host[m[1]](getEventName(m[2]), value);
+ delete attrs[key];
+ }
+ }
+ }
+ }
+ // Converts `Show` to `show` and `ChangeTitle` to `change:title`
+ function getEventName(name) {
+ var m = name.match(EVENT_NAME_PATTERN);
+ var ret = m[1] ? "change:" : "";
+ ret += m[2].toLowerCase() + m[3];
+ return ret;
+ }
+ function setSetterAttrs(host, attrs, config) {
+ var options = {
+ silent: true
+ };
+ host.__initializingAttrs = true;
+ for (var key in config) {
+ if (config.hasOwnProperty(key)) {
+ if (attrs[key].setter) {
+ host.set(key, config[key], options);
+ }
+ }
+ }
+ delete host.__initializingAttrs;
+ }
+ var ATTR_SPECIAL_KEYS = [ "value", "getter", "setter", "readOnly" ];
+ // normalize `attrs` to
+ //
+ // {
+ // value: 'xx',
+ // getter: fn,
+ // setter: fn,
+ // readOnly: boolean
+ // }
+ //
+ function normalize(attrs, isUserValue) {
+ var newAttrs = {};
+ for (var key in attrs) {
+ var attr = attrs[key];
+ if (!isUserValue && isPlainObject(attr) && hasOwnProperties(attr, ATTR_SPECIAL_KEYS)) {
+ newAttrs[key] = attr;
+ continue;
+ }
+ newAttrs[key] = {
+ value: attr
+ };
+ }
+ return newAttrs;
+ }
+ function hasOwnProperties(object, properties) {
+ for (var i = 0, len = properties.length; i < len; i++) {
+ if (object.hasOwnProperty(properties[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+ // 对于 attrs 的 value 来说,以下值都认为是空值: null, undefined, '', [], {}
+ function isEmptyAttrValue(o) {
+ return o == null || // null, undefined
+ (isString(o) || isArray(o)) && o.length === 0 || // '', []
+ isEmptyObject(o);
+ }
+ // 判断属性值 a 和 b 是否相等,注意仅适用于属性值的判断,非普适的 === 或 == 判断。
+ function isEqual(a, b) {
+ if (a === b) return true;
+ if (isEmptyAttrValue(a) && isEmptyAttrValue(b)) return true;
+ // Compare `[[Class]]` names.
+ var className = toString.call(a);
+ if (className != toString.call(b)) return false;
+ switch (className) {
+ // Strings, numbers, dates, and booleans are compared by value.
+ case "[object String]":
+ // Primitives and their corresponding object wrappers are
+ // equivalent; thus, `"5"` is equivalent to `new String("5")`.
+ return a == String(b);
+
+ case "[object Number]":
+ // `NaN`s are equivalent, but non-reflexive. An `equal`
+ // comparison is performed for other numeric values.
+ return a != +a ? b != +b : a == 0 ? 1 / a == 1 / b : a == +b;
+
+ case "[object Date]":
+ case "[object Boolean]":
+ // Coerce dates and booleans to numeric primitive values.
+ // Dates are compared by their millisecond representations.
+ // Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ return +a == +b;
+
+ // RegExps are compared by their source patterns and flags.
+ case "[object RegExp]":
+ return a.source == b.source && a.global == b.global && a.multiline == b.multiline && a.ignoreCase == b.ignoreCase;
+
+ // 简单判断数组包含的 primitive 值是否相等
+ case "[object Array]":
+ var aString = a.toString();
+ var bString = b.toString();
+ // 只要包含非 primitive 值,为了稳妥起见,都返回 false
+ return aString.indexOf("[object") === -1 && bString.indexOf("[object") === -1 && aString === bString;
+ }
+ if (typeof a != "object" || typeof b != "object") return false;
+ // 简单判断两个对象是否相等,只判断第一层
+ if (isPlainObject(a) && isPlainObject(b)) {
+ // 键值不相等,立刻返回 false
+ if (!isEqual(keys(a), keys(b))) {
+ return false;
+ }
+ // 键相同,但有值不等,立刻返回 false
+ for (var p in a) {
+ if (a[p] !== b[p]) return false;
+ }
+ return true;
+ }
+ // 其他情况返回 false, 以避免误判导致 change 事件没发生
+ return false;
+ }
+});
diff --git a/test/cases/project/sea-modules/arale/base/1.1.1/base.js b/test/cases/project/sea-modules/arale/base/1.1.1/base.js
new file mode 100644
index 0000000..1e2ef17
--- /dev/null
+++ b/test/cases/project/sea-modules/arale/base/1.1.1/base.js
@@ -0,0 +1 @@
+define("arale/base/1.1.1/base",["arale/class/1.1.0/class","arale/events/1.1.0/events","./aspect","./attribute"],function(a,b,c){function d(a,b){for(var c in b)if(b.hasOwnProperty(c)){var d="_onChange"+e(c);a[d]&&a.on("change:"+c,a[d])}}function e(a){return a.charAt(0).toUpperCase()+a.substring(1)}var f=a("arale/class/1.1.0/class"),g=a("arale/events/1.1.0/events"),h=a("./aspect"),i=a("./attribute");c.exports=f.create({Implements:[g,h,i],initialize:function(a){this.initAttrs(a),d(this,this.attrs)},destroy:function(){this.off();for(var a in this)this.hasOwnProperty(a)&&delete this[a];this.destroy=function(){}}})}),define("arale/base/1.1.1/aspect",[],function(a,b){function c(a,b,c,g){for(var h,i,j=b.split(f);h=j.shift();)i=d(this,h),i.__isAspected||e.call(this,h),this.on(a+":"+h,c,g);return this}function d(a,b){var c=a[b];if(!c)throw new Error("Invalid method name: "+b);return c}function e(a){var b=this[a];this[a]=function(){var c=Array.prototype.slice.call(arguments),d=["before:"+a].concat(c);if(this.trigger.apply(this,d)!==!1){var e=b.apply(this,arguments),f=["after:"+a,e].concat(c);return this.trigger.apply(this,f),e}},this[a].__isAspected=!0}b.before=function(a,b,d){return c.call(this,"before",a,b,d)},b.after=function(a,b,d){return c.call(this,"after",a,b,d)};var f=/\s+/}),define("arale/base/1.1.1/attribute",[],function(a,b){function c(a){return"[object String]"===t.call(a)}function d(a){return"[object Function]"===t.call(a)}function e(a){return null!=a&&a==a.window}function f(a){if(!a||"[object Object]"!==t.call(a)||a.nodeType||e(a))return!1;try{if(a.constructor&&!u.call(a,"constructor")&&!u.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(b){return!1}var c;if(s)for(c in a)return u.call(a,c);for(c in a);return void 0===c||u.call(a,c)}function g(a){if(!a||"[object Object]"!==t.call(a)||a.nodeType||e(a)||!a.hasOwnProperty)return!1;for(var b in a)if(a.hasOwnProperty(b))return!1;return!0}function h(a,b){var c,d;for(c in b)if(b.hasOwnProperty(c)){if(d=b[c],v(d))d=d.slice();else if(f(d)){var e=a[c];f(e)||(e={}),d=h(e,d)}a[c]=d}return a}function i(a,b,c){for(var d=[],e=b.constructor.prototype;e;)e.hasOwnProperty("attrs")||(e.attrs={}),k(c,e.attrs,e),g(e.attrs)||d.unshift(e.attrs),e=e.constructor.superclass;for(var f=0,i=d.length;i>f;f++)h(a,o(d[f]))}function j(a,b){h(a,o(b,!0))}function k(a,b,c,d){for(var e=0,f=a.length;f>e;e++){var g=a[e];c.hasOwnProperty(g)&&(b[g]=d?b.get(g):c[g])}}function l(a,b){for(var c in b)if(b.hasOwnProperty(c)){var e,f=b[c].value;d(f)&&(e=c.match(x))&&(a[e[1]](m(e[2]),f),delete b[c])}}function m(a){var b=a.match(y),c=b[1]?"change:":"";return c+=b[2].toLowerCase()+b[3]}function n(a,b,c){var d={silent:!0};a.__initializingAttrs=!0;for(var e in c)c.hasOwnProperty(e)&&b[e].setter&&a.set(e,c[e],d);delete a.__initializingAttrs}function o(a,b){var c={};for(var d in a){var e=a[d];c[d]=!b&&f(e)&&p(e,z)?e:{value:e}}return c}function p(a,b){for(var c=0,d=b.length;d>c;c++)if(a.hasOwnProperty(b[c]))return!0;return!1}function q(a){return null==a||(c(a)||v(a))&&0===a.length||g(a)}function r(a,b){if(a===b)return!0;if(q(a)&&q(b))return!0;var c=t.call(a);if(c!=t.call(b))return!1;switch(c){case"[object String]":return a==String(b);case"[object Number]":return a!=+a?b!=+b:0==a?1/a==1/b:a==+b;case"[object Date]":case"[object Boolean]":return+a==+b;case"[object RegExp]":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase;case"[object Array]":var d=a.toString(),e=b.toString();return-1===d.indexOf("[object")&&-1===e.indexOf("[object")&&d===e}if("object"!=typeof a||"object"!=typeof b)return!1;if(f(a)&&f(b)){if(!r(w(a),w(b)))return!1;for(var g in a)if(a[g]!==b[g])return!1;return!0}return!1}b.initAttrs=function(a){var b=this.attrs={},c=this.propsInAttrs||[];i(b,this,c),a&&j(b,a),n(this,b,a),l(this,b),k(c,this,b,!0)},b.get=function(a){var b=this.attrs[a]||{},c=b.value;return b.getter?b.getter.call(this,c,a):c},b.set=function(a,b,d){var e={};c(a)?e[a]=b:(e=a,d=b),d||(d={});var g=d.silent,i=d.override,j=this.attrs,k=this.__changedAttrs||(this.__changedAttrs={});for(a in e)if(e.hasOwnProperty(a)){var l=j[a]||(j[a]={});if(b=e[a],l.readOnly)throw new Error("This attribute is readOnly: "+a);l.setter&&(b=l.setter.call(this,b,a));var m=this.get(a);!i&&f(m)&&f(b)&&(b=h(h({},m),b)),j[a].value=b,this.__initializingAttrs||r(m,b)||(g?k[a]=[b,m]:this.trigger("change:"+a,b,m,a))}return this},b.change=function(){var a=this.__changedAttrs;if(a){for(var b in a)if(a.hasOwnProperty(b)){var c=a[b];this.trigger("change:"+b,c[0],c[1],b)}delete this.__changedAttrs}return this},b._isPlainObject=f;var s,t=Object.prototype.toString,u=Object.prototype.hasOwnProperty;!function(){function a(){this.x=1}var b=[];a.prototype={valueOf:1,y:1};for(var c in new a)b.push(c);s="x"!==b[0]}();var v=Array.isArray||function(a){return"[object Array]"===t.call(a)},w=Object.keys;w||(w=function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b});var x=/^(on|before|after)([A-Z].*)$/,y=/^(Change)?([A-Z])(.*)/,z=["value","getter","setter","readOnly"]});
diff --git a/test/cases/project/sea-modules/arale/base/1.1.1/package.json b/test/cases/project/sea-modules/arale/base/1.1.1/package.json
new file mode 100644
index 0000000..7c7bfe8
--- /dev/null
+++ b/test/cases/project/sea-modules/arale/base/1.1.1/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "base",
+ "family": "arale",
+ "version": "1.1.1",
+ "description": "Base 是一个基础类,提供 Class、Events、Attribute 和 Aspect 支持。",
+ "keywords": ["infrastructure"],
+ "homepage": "http://aralejs.org/base",
+ "author": "贯高 ",
+ "maintainers": [
+ "玉伯 ",
+ "贯高 "
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/aralejs/base.git"
+ },
+ "bugs": {
+ "url": "https://github.com/aralejs/base/issues"
+ },
+ "spm": {
+ "alias": {
+ "class": "arale/class/1.1.0/class",
+ "events": "arale/events/1.1.0/events"
+ },
+ "output": ["base.js"]
+ }
+}
diff --git a/test/cases/project/sea-modules/arale/class/1.1.0/class-debug.js b/test/cases/project/sea-modules/arale/class/1.1.0/class-debug.js
new file mode 100644
index 0000000..df60888
--- /dev/null
+++ b/test/cases/project/sea-modules/arale/class/1.1.0/class-debug.js
@@ -0,0 +1,148 @@
+define("arale/class/1.1.0/class-debug", [], function(require, exports, module) {
+ // Class
+ // -----------------
+ // Thanks to:
+ // - http://mootools.net/docs/core/Class/Class
+ // - http://ejohn.org/blog/simple-javascript-inheritance/
+ // - https://github.com/ded/klass
+ // - http://documentcloud.github.com/backbone/#Model-extend
+ // - https://github.com/joyent/node/blob/master/lib/util.js
+ // - https://github.com/kissyteam/kissy/blob/master/src/seed/src/kissy.js
+ // The base Class implementation.
+ function Class(o) {
+ // Convert existed function to Class.
+ if (!(this instanceof Class) && isFunction(o)) {
+ return classify(o);
+ }
+ }
+ module.exports = Class;
+ // Create a new Class.
+ //
+ // var SuperPig = Class.create({
+ // Extends: Animal,
+ // Implements: Flyable,
+ // initialize: function() {
+ // SuperPig.superclass.initialize.apply(this, arguments)
+ // },
+ // Statics: {
+ // COLOR: 'red'
+ // }
+ // })
+ //
+ Class.create = function(parent, properties) {
+ if (!isFunction(parent)) {
+ properties = parent;
+ parent = null;
+ }
+ properties || (properties = {});
+ parent || (parent = properties.Extends || Class);
+ properties.Extends = parent;
+ // The created class constructor
+ function SubClass() {
+ // Call the parent constructor.
+ parent.apply(this, arguments);
+ // Only call initialize in self constructor.
+ if (this.constructor === SubClass && this.initialize) {
+ this.initialize.apply(this, arguments);
+ }
+ }
+ // Inherit class (static) properties from parent.
+ if (parent !== Class) {
+ mix(SubClass, parent, parent.StaticsWhiteList);
+ }
+ // Add instance properties to the subclass.
+ implement.call(SubClass, properties);
+ // Make subclass extendable.
+ return classify(SubClass);
+ };
+ function implement(properties) {
+ var key, value;
+ for (key in properties) {
+ value = properties[key];
+ if (Class.Mutators.hasOwnProperty(key)) {
+ Class.Mutators[key].call(this, value);
+ } else {
+ this.prototype[key] = value;
+ }
+ }
+ }
+ // Create a sub Class based on `Class`.
+ Class.extend = function(properties) {
+ properties || (properties = {});
+ properties.Extends = this;
+ return Class.create(properties);
+ };
+ function classify(cls) {
+ cls.extend = Class.extend;
+ cls.implement = implement;
+ return cls;
+ }
+ // Mutators define special properties.
+ Class.Mutators = {
+ Extends: function(parent) {
+ var existed = this.prototype;
+ var proto = createProto(parent.prototype);
+ // Keep existed properties.
+ mix(proto, existed);
+ // Enforce the constructor to be what we expect.
+ proto.constructor = this;
+ // Set the prototype chain to inherit from `parent`.
+ this.prototype = proto;
+ // Set a convenience property in case the parent's prototype is
+ // needed later.
+ this.superclass = parent.prototype;
+ },
+ Implements: function(items) {
+ isArray(items) || (items = [ items ]);
+ var proto = this.prototype, item;
+ while (item = items.shift()) {
+ mix(proto, item.prototype || item);
+ }
+ },
+ Statics: function(staticProperties) {
+ mix(this, staticProperties);
+ }
+ };
+ // Shared empty constructor function to aid in prototype-chain creation.
+ function Ctor() {}
+ // See: http://jsperf.com/object-create-vs-new-ctor
+ var createProto = Object.__proto__ ? function(proto) {
+ return {
+ __proto__: proto
+ };
+ } : function(proto) {
+ Ctor.prototype = proto;
+ return new Ctor();
+ };
+ // Helpers
+ // ------------
+ function mix(r, s, wl) {
+ // Copy "all" properties including inherited ones.
+ for (var p in s) {
+ if (s.hasOwnProperty(p)) {
+ if (wl && indexOf(wl, p) === -1) continue;
+ // 在 iPhone 1 代等设备的 Safari 中,prototype 也会被枚举出来,需排除
+ if (p !== "prototype") {
+ r[p] = s[p];
+ }
+ }
+ }
+ }
+ var toString = Object.prototype.toString;
+ var isArray = Array.isArray || function(val) {
+ return toString.call(val) === "[object Array]";
+ };
+ var isFunction = function(val) {
+ return toString.call(val) === "[object Function]";
+ };
+ var indexOf = Array.prototype.indexOf ? function(arr, item) {
+ return arr.indexOf(item);
+ } : function(arr, item) {
+ for (var i = 0, len = arr.length; i < len; i++) {
+ if (arr[i] === item) {
+ return i;
+ }
+ }
+ return -1;
+ };
+});
diff --git a/test/cases/project/sea-modules/arale/class/1.1.0/class.js b/test/cases/project/sea-modules/arale/class/1.1.0/class.js
new file mode 100644
index 0000000..9f6062e
--- /dev/null
+++ b/test/cases/project/sea-modules/arale/class/1.1.0/class.js
@@ -0,0 +1 @@
+define("arale/class/1.1.0/class",[],function(a,b,c){function d(a){return this instanceof d||!l(a)?void 0:f(a)}function e(a){var b,c;for(b in a)c=a[b],d.Mutators.hasOwnProperty(b)?d.Mutators[b].call(this,c):this.prototype[b]=c}function f(a){return a.extend=d.extend,a.implement=e,a}function g(){}function h(a,b,c){for(var d in b)if(b.hasOwnProperty(d)){if(c&&-1===m(c,d))continue;"prototype"!==d&&(a[d]=b[d])}}c.exports=d,d.create=function(a,b){function c(){a.apply(this,arguments),this.constructor===c&&this.initialize&&this.initialize.apply(this,arguments)}return l(a)||(b=a,a=null),b||(b={}),a||(a=b.Extends||d),b.Extends=a,a!==d&&h(c,a,a.StaticsWhiteList),e.call(c,b),f(c)},d.extend=function(a){return a||(a={}),a.Extends=this,d.create(a)},d.Mutators={Extends:function(a){var b=this.prototype,c=i(a.prototype);h(c,b),c.constructor=this,this.prototype=c,this.superclass=a.prototype},Implements:function(a){k(a)||(a=[a]);for(var b,c=this.prototype;b=a.shift();)h(c,b.prototype||b)},Statics:function(a){h(this,a)}};var i=Object.__proto__?function(a){return{__proto__:a}}:function(a){return g.prototype=a,new g},j=Object.prototype.toString,k=Array.isArray||function(a){return"[object Array]"===j.call(a)},l=function(a){return"[object Function]"===j.call(a)},m=Array.prototype.indexOf?function(a,b){return a.indexOf(b)}:function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1}});
diff --git a/test/cases/project/sea-modules/arale/class/1.1.0/package.json b/test/cases/project/sea-modules/arale/class/1.1.0/package.json
new file mode 100644
index 0000000..10d4a97
--- /dev/null
+++ b/test/cases/project/sea-modules/arale/class/1.1.0/package.json
@@ -0,0 +1,23 @@
+{
+ "name": "class",
+ "family": "arale",
+ "version": "1.1.0",
+ "keywords": ["infrastructure"],
+ "description": "提供简洁的 OO 实现。",
+ "homepage": "http://aralejs.org/class/",
+ "author": "玉伯 ",
+ "maintainers": [
+ "玉伯 ",
+ "贯高 "
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/aralejs/class.git"
+ },
+ "bugs": {
+ "url": "https://github.com/aralejs/class/issues"
+ },
+ "spm": {
+ "output": ["class.js"]
+ }
+}
diff --git a/test/cases/project/sea-modules/arale/dialog/1.3.1/confirmbox-debug.js b/test/cases/project/sea-modules/arale/dialog/1.3.1/confirmbox-debug.js
new file mode 100644
index 0000000..336e6cb
--- /dev/null
+++ b/test/cases/project/sea-modules/arale/dialog/1.3.1/confirmbox-debug.js
@@ -0,0 +1,713 @@
+define("arale/dialog/1.3.1/confirmbox-debug", [ "$-debug", "./dialog-debug", "arale/overlay/1.1.4/overlay-debug", "arale/position/1.0.1/position-debug", "arale/iframe-shim/1.0.2/iframe-shim-debug", "arale/widget/1.1.1/widget-debug", "arale/base/1.1.1/base-debug", "arale/class/1.1.0/class-debug", "arale/events/1.1.0/events-debug", "arale/overlay/1.1.4/mask-debug", "arale/templatable/0.9.2/templatable-debug", "gallery/handlebars/1.0.2/handlebars-debug", "./dialog-debug.handlebars", "./confirmbox-debug.handlebars", "./dialog-debug.css" ], function(require, exports, module) {
+ var $ = require("$-debug"), Dialog = require("./dialog-debug");
+ var template = require("./confirmbox-debug.handlebars");
+ require("./dialog-debug.css");
+ // ConfirmBox
+ // -------
+ // ConfirmBox 是一个有基础模板和样式的对话框组件。
+ var ConfirmBox = Dialog.extend({
+ attrs: {
+ title: "默认标题",
+ confirmTpl: '确定',
+ cancelTpl: '取消',
+ message: "默认内容"
+ },
+ setup: function() {
+ ConfirmBox.superclass.setup.call(this);
+ var model = {
+ classPrefix: this.get("classPrefix"),
+ message: this.get("message"),
+ title: this.get("title"),
+ confirmTpl: this.get("confirmTpl"),
+ cancelTpl: this.get("cancelTpl"),
+ hasFoot: this.get("confirmTpl") || this.get("cancelTpl")
+ };
+ this.set("content", template(model));
+ },
+ events: {
+ "click [data-role=confirm]": function(e) {
+ e.preventDefault();
+ this.trigger("confirm");
+ },
+ "click [data-role=cancel]": function(e) {
+ e.preventDefault();
+ this.trigger("cancel");
+ this.hide();
+ }
+ },
+ _onChangeMessage: function(val) {
+ this.$("[data-role=message]").html(val);
+ },
+ _onChangeTitle: function(val) {
+ this.$("[data-role=title]").html(val);
+ },
+ _onChangeConfirmTpl: function(val) {
+ this.$("[data-role=confirm]").html(val);
+ },
+ _onChangeCancelTpl: function(val) {
+ this.$("[data-role=cancel]").html(val);
+ }
+ });
+ ConfirmBox.alert = function(message, callback, options) {
+ var defaults = {
+ message: message,
+ title: "",
+ cancelTpl: "",
+ closeTpl: "",
+ onConfirm: function() {
+ callback && callback();
+ this.hide();
+ }
+ };
+ new ConfirmBox($.extend(null, defaults, options)).show().after("hide", function() {
+ this.destroy();
+ });
+ };
+ ConfirmBox.confirm = function(message, title, onConfirm, onCancel, options) {
+ // support confirm(message, title, onConfirm, options)
+ if (typeof onCancel === "object" && !options) {
+ options = onCancel;
+ }
+ var defaults = {
+ message: message,
+ title: title || "确认框",
+ closeTpl: "",
+ onConfirm: function() {
+ onConfirm && onConfirm();
+ this.hide();
+ },
+ onCancel: function() {
+ onCancel && onCancel();
+ this.hide();
+ }
+ };
+ new ConfirmBox($.extend(null, defaults, options)).show().after("hide", function() {
+ this.destroy();
+ });
+ };
+ ConfirmBox.show = function(message, callback, options) {
+ var defaults = {
+ message: message,
+ title: "",
+ confirmTpl: false,
+ cancelTpl: false
+ };
+ new ConfirmBox($.extend(null, defaults, options)).show().before("hide", function() {
+ callback && callback();
+ }).after("hide", function() {
+ this.destroy();
+ });
+ };
+ module.exports = ConfirmBox;
+ module.exports.outerBoxClass = "arale-dialog-1_3_1";
+});
+
+define("arale/dialog/1.3.1/dialog-debug", [ "$-debug", "arale/overlay/1.1.4/overlay-debug", "arale/position/1.0.1/position-debug", "arale/iframe-shim/1.0.2/iframe-shim-debug", "arale/widget/1.1.1/widget-debug", "arale/base/1.1.1/base-debug", "arale/class/1.1.0/class-debug", "arale/events/1.1.0/events-debug", "arale/overlay/1.1.4/mask-debug", "arale/templatable/0.9.2/templatable-debug", "gallery/handlebars/1.0.2/handlebars-debug" ], function(require, exports, module) {
+ var $ = require("$-debug"), Overlay = require("arale/overlay/1.1.4/overlay-debug"), mask = require("arale/overlay/1.1.4/mask-debug"), Events = require("arale/events/1.1.0/events-debug"), Templatable = require("arale/templatable/0.9.2/templatable-debug");
+ // Dialog
+ // ---
+ // Dialog 是通用对话框组件,提供显隐关闭、遮罩层、内嵌iframe、内容区域自定义功能。
+ // 是所有对话框类型组件的基类。
+ var Dialog = Overlay.extend({
+ Implements: Templatable,
+ attrs: {
+ // 模板
+ template: require("arale/dialog/1.3.1/dialog-debug.handlebars"),
+ // 对话框触发点
+ trigger: {
+ value: null,
+ getter: function(val) {
+ return $(val);
+ }
+ },
+ // 统一样式前缀
+ classPrefix: "ui-dialog",
+ // 指定内容元素,可以是 url 地址
+ content: {
+ value: null,
+ setter: function(val) {
+ // 判断是否是 url 地址
+ if (/^(https?:\/\/|\/|\.\/|\.\.\/)/.test(val)) {
+ this._type = "iframe";
+ // 用 ajax 的方式而不是 iframe 进行载入
+ if (val.indexOf("?ajax") > 0 || val.indexOf("&ajax") > 0) {
+ this._ajax = true;
+ }
+ }
+ return val;
+ }
+ },
+ // 是否有背景遮罩层
+ hasMask: true,
+ // 关闭按钮可以自定义
+ closeTpl: "×",
+ // 默认宽度
+ width: 500,
+ // 默认高度
+ height: null,
+ // iframe 类型时,dialog 的最初高度
+ initialHeight: 300,
+ // 简单的动画效果 none | fade
+ effect: "none",
+ // 不用解释了吧
+ zIndex: 999,
+ // 是否自适应高度
+ autoFit: true,
+ // 默认定位居中
+ align: {
+ value: {
+ selfXY: [ "50%", "50%" ],
+ baseXY: [ "50%", "42%" ]
+ },
+ getter: function(val) {
+ // 高度超过窗口的 42/50 浮层头部顶住窗口
+ // https://github.com/aralejs/dialog/issues/41
+ if (this.element.height() > $(window).height() * .84) {
+ return {
+ selfXY: [ "50%", "0" ],
+ baseXY: [ "50%", "0" ]
+ };
+ }
+ return val;
+ }
+ }
+ },
+ parseElement: function() {
+ this.set("model", {
+ classPrefix: this.get("classPrefix")
+ });
+ Dialog.superclass.parseElement.call(this);
+ this.contentElement = this.$("[data-role=content]");
+ // 必要的样式
+ this.contentElement.css({
+ height: "100%",
+ zoom: 1
+ });
+ // 关闭按钮先隐藏
+ // 后面当 onRenderCloseTpl 时,如果 closeTpl 不为空,会显示出来
+ // 这样写是为了回避 arale.base 的一个问题:
+ // 当属性初始值为''时,不会进入 onRender 方法
+ // https://github.com/aralejs/base/issues/7
+ this.$("[data-role=close]").hide();
+ },
+ events: {
+ "click [data-role=close]": function(e) {
+ e.preventDefault();
+ this.hide();
+ }
+ },
+ show: function() {
+ // iframe 要在载入完成才显示
+ if (this._type === "iframe") {
+ // ajax 读入内容并 append 到容器中
+ if (this._ajax) {
+ this._ajaxHtml();
+ } else {
+ // iframe 还未请求完,先设置一个固定高度
+ !this.get("height") && this.contentElement.css("height", this.get("initialHeight"));
+ this._showIframe();
+ }
+ }
+ Dialog.superclass.show.call(this);
+ return this;
+ },
+ hide: function() {
+ // 把 iframe 状态复原
+ if (this._type === "iframe" && this.iframe) {
+ this.iframe.attr({
+ src: "javascript:'';"
+ });
+ // 原来只是将 iframe 的状态复原
+ // 但是发现在 IE6 下,将 src 置为 javascript:''; 会出现 404 页面
+ this.iframe.remove();
+ this.iframe = null;
+ }
+ Dialog.superclass.hide.call(this);
+ clearInterval(this._interval);
+ delete this._interval;
+ return this;
+ },
+ destroy: function() {
+ this.element.remove();
+ this._hideMask();
+ clearInterval(this._interval);
+ return Dialog.superclass.destroy.call(this);
+ },
+ setup: function() {
+ Dialog.superclass.setup.call(this);
+ this._setupTrigger();
+ this._setupMask();
+ this._setupKeyEvents();
+ this._setupFocus();
+ toTabed(this.element);
+ toTabed(this.get("trigger"));
+ // 默认当前触发器
+ this.activeTrigger = this.get("trigger").eq(0);
+ },
+ // onRender
+ // ---
+ _onRenderContent: function(val) {
+ if (this._type !== "iframe") {
+ var value;
+ // 有些情况会报错
+ try {
+ value = $(val);
+ } catch (e) {
+ value = [];
+ }
+ if (value[0]) {
+ this.contentElement.empty().append(value);
+ } else {
+ this.contentElement.empty().html(val);
+ }
+ // #38 #44
+ this._setPosition();
+ }
+ },
+ _onRenderCloseTpl: function(val) {
+ if (val === "") {
+ this.$("[data-role=close]").html(val).hide();
+ } else {
+ this.$("[data-role=close]").html(val).show();
+ }
+ },
+ // 覆盖 overlay,提供动画
+ _onRenderVisible: function(val) {
+ if (val) {
+ if (this.get("effect") === "fade") {
+ // 固定 300 的动画时长,暂不可定制
+ this.element.fadeIn(300);
+ } else {
+ this.element.show();
+ }
+ } else {
+ this.element.hide();
+ }
+ },
+ // 私有方法
+ // ---
+ // 绑定触发对话框出现的事件
+ _setupTrigger: function() {
+ this.delegateEvents(this.get("trigger"), "click", function(e) {
+ e.preventDefault();
+ // 标识当前点击的元素
+ this.activeTrigger = $(e.currentTarget);
+ this.show();
+ });
+ },
+ // 绑定遮罩层事件
+ _setupMask: function() {
+ var that = this;
+ // 存放 mask 对应的对话框
+ mask._dialogs = mask._dialogs || [];
+ this.after("show", function() {
+ if (!this.get("hasMask")) {
+ return;
+ }
+ // not using the z-index
+ // because multiable dialogs may share same mask
+ mask.set("zIndex", that.get("zIndex")).show();
+ mask.element.insertBefore(that.element);
+ // 避免重复存放
+ var existed = false;
+ for (var i = 0; i < mask._dialogs.length; i++) {
+ if (mask._dialogs[i] === that) {
+ existed = true;
+ }
+ }
+ // 依次存放对应的对话框
+ if (!existed) {
+ mask._dialogs.push(that);
+ }
+ });
+ this.after("hide", this._hideMask);
+ },
+ // 隐藏 mask
+ _hideMask: function() {
+ if (!this.get("hasMask")) {
+ return;
+ }
+ mask._dialogs && mask._dialogs.pop();
+ if (mask._dialogs && mask._dialogs.length > 0) {
+ var last = mask._dialogs[mask._dialogs.length - 1];
+ mask.set("zIndex", last.get("zIndex"));
+ mask.element.insertBefore(last.element);
+ } else {
+ mask.hide();
+ }
+ },
+ // 绑定元素聚焦状态
+ _setupFocus: function() {
+ this.after("show", function() {
+ this.element.focus();
+ });
+ this.after("hide", function() {
+ // 关于网页中浮层消失后的焦点处理
+ // http://www.qt06.com/post/280/
+ this.activeTrigger && this.activeTrigger.focus();
+ });
+ },
+ // 绑定键盘事件,ESC关闭窗口
+ _setupKeyEvents: function() {
+ this.delegateEvents($(document), "keyup.esc", function(e) {
+ if (e.keyCode === 27) {
+ this.get("visible") && this.hide();
+ }
+ });
+ },
+ _showIframe: function() {
+ var that = this;
+ // 若未创建则新建一个
+ if (!this.iframe) {
+ this._createIframe();
+ }
+ // 开始请求 iframe
+ this.iframe.attr({
+ src: this._fixUrl(),
+ name: "dialog-iframe" + new Date().getTime()
+ });
+ // 因为在 IE 下 onload 无法触发
+ // http://my.oschina.net/liangrockman/blog/24015
+ // 所以使用 jquery 的 one 函数来代替 onload
+ // one 比 on 好,因为它只执行一次,并在执行后自动销毁
+ this.iframe.one("load", function() {
+ // 如果 dialog 已经隐藏了,就不需要触发 onload
+ if (!that.get("visible")) {
+ return;
+ }
+ // 绑定自动处理高度的事件
+ if (that.get("autoFit")) {
+ clearInterval(that._interval);
+ that._interval = setInterval(function() {
+ that._syncHeight();
+ }, 300);
+ }
+ that._syncHeight();
+ that._setPosition();
+ that.trigger("complete:show");
+ });
+ },
+ _fixUrl: function() {
+ var s = this.get("content").match(/([^?#]*)(\?[^#]*)?(#.*)?/);
+ s.shift();
+ s[1] = (s[1] && s[1] !== "?" ? s[1] + "&" : "?") + "t=" + new Date().getTime();
+ return s.join("");
+ },
+ _createIframe: function() {
+ var that = this;
+ this.iframe = $("