Commit e5adfb0c authored by Arthur Verschaeve's avatar Arthur Verschaeve

Migrate `somajs_require` app to `todomvc-app-css`

Ref #1110
parent 91fff57b
node_modules/todomvc-app-css/*
!node_modules/todomvc-app-css/index.css
node_modules/todomvc-common/*
!node_modules/todomvc-common/base.js
!node_modules/todomvc-common/base.css
node_modules/director/**
!node_modules/director/build/director.js
node_modules/soma.js/**
!node_modules/soma.js/build/soma.js
node_modules/soma-template/**
!node_modules/soma-template/build/soma-template.js
node_modules/requirejs/**
!node_modules/requirejs/require.js
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>soma.js • TodoMVC</title> <title>soma.js • TodoMVC</title>
<link rel="stylesheet" href="bower_components/todomvc-common/base.css"> <link rel="stylesheet" href="node_modules/todomvc-common/base.css">
<link rel="stylesheet" href="node_modules/todomvc-app-css/index.css">
<link rel="stylesheet" href="css/app.css"> <link rel="stylesheet" href="css/app.css">
</head> </head>
<body> <body>
...@@ -57,7 +58,7 @@ ...@@ -57,7 +58,7 @@
</footer> </footer>
<!-- TODO APP --> <!-- TODO APP -->
<script src="bower_components/todomvc-common/base.js"></script> <script src="node_modules/todomvc-common/base.js"></script>
<script data-main="js/app" src="bower_components/requirejs/require.js"></script> <script data-main="js/app" src="node_modules/requirejs/require.js"></script>
</body> </body>
</html> </html>
...@@ -7,9 +7,9 @@ ...@@ -7,9 +7,9 @@
baseUrl: './js', baseUrl: './js',
paths: { paths: {
// libs // libs
soma: '../bower_components/soma.js/build/soma', soma: '../node_modules/soma.js/build/soma',
template: '../bower_components/soma-template/build/soma-template', template: '../node_modules/soma-template/build/soma-template',
director: '../bower_components/director/build/director', director: '../node_modules/director/build/director',
// app paths // app paths
views: './views', views: './views',
models: './models' models: './models'
......
// //
// Generated on Fri Dec 27 2013 12:02:11 GMT-0500 (EST) by Nodejitsu, Inc (Using Codesurgeon). // Generated on Tue Dec 16 2014 12:13:47 GMT+0100 (CET) by Charlie Robbins, Paolo Fragomeni & the Contributors (Using Codesurgeon).
// Version 1.2.2 // Version 1.2.6
// //
(function (exports) { (function (exports) {
...@@ -10,29 +10,11 @@ ...@@ -10,29 +10,11 @@
/* /*
* browser.js: Browser specific functionality for director. * browser.js: Browser specific functionality for director.
* *
* (C) 2011, Nodejitsu Inc. * (C) 2011, Charlie Robbins, Paolo Fragomeni, & the Contributors.
* MIT LICENSE * MIT LICENSE
* *
*/ */
if (!Array.prototype.filter) {
Array.prototype.filter = function(filter, that) {
var other = [], v;
for (var i = 0, n = this.length; i < n; i++) {
if (i in this && filter.call(that, v = this[i], i, this)) {
other.push(v);
}
}
return other;
};
}
if (!Array.isArray){
Array.isArray = function(obj) {
return Object.prototype.toString.call(obj) === '[object Array]';
};
}
var dloc = document.location; var dloc = document.location;
function dlocHashEmpty() { function dlocHashEmpty() {
...@@ -196,7 +178,8 @@ var Router = exports.Router = function (routes) { ...@@ -196,7 +178,8 @@ var Router = exports.Router = function (routes) {
}; };
Router.prototype.init = function (r) { Router.prototype.init = function (r) {
var self = this; var self = this
, routeTo;
this.handler = function(onChangeEvent) { this.handler = function(onChangeEvent) {
var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash; var newURL = onChangeEvent && onChangeEvent.newURL || window.location.hash;
var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, ''); var url = self.history === true ? self.getPath() : newURL.replace(/.*#/, '');
...@@ -213,9 +196,16 @@ Router.prototype.init = function (r) { ...@@ -213,9 +196,16 @@ Router.prototype.init = function (r) {
} }
} }
else { else {
var routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null; if (this.convert_hash_in_init) {
if (routeTo) { // Use hash as route
window.history.replaceState({}, document.title, routeTo); routeTo = dlocHashEmpty() && r ? r : !dlocHashEmpty() ? dloc.hash.replace(/^#/, '') : null;
if (routeTo) {
window.history.replaceState({}, document.title, routeTo);
}
}
else {
// Use canonical url
routeTo = this.getPath();
} }
// Router has been initialized, but due to the chrome bug it will not // Router has been initialized, but due to the chrome bug it will not
...@@ -351,7 +341,7 @@ function paramifyString(str, params, mod) { ...@@ -351,7 +341,7 @@ function paramifyString(str, params, mod) {
} }
} }
} }
return mod === str ? "([._a-zA-Z0-9-]+)" : mod; return mod === str ? "([._a-zA-Z0-9-%()]+)" : mod;
} }
function regifyString(str, params) { function regifyString(str, params) {
...@@ -397,6 +387,8 @@ function terminator(routes, delimiter, start, stop) { ...@@ -397,6 +387,8 @@ function terminator(routes, delimiter, start, stop) {
return routes; return routes;
} }
var QUERY_SEPARATOR = /\?.*/;
Router.prototype.configure = function(options) { Router.prototype.configure = function(options) {
options = options || {}; options = options || {};
for (var i = 0; i < this.methods.length; i++) { for (var i = 0; i < this.methods.length; i++) {
...@@ -410,6 +402,7 @@ Router.prototype.configure = function(options) { ...@@ -410,6 +402,7 @@ Router.prototype.configure = function(options) {
this.resource = options.resource; this.resource = options.resource;
this.history = options.html5history && this.historySupport || false; this.history = options.html5history && this.historySupport || false;
this.run_in_init = this.history === true && options.run_handler_in_init !== false; this.run_in_init = this.history === true && options.run_handler_in_init !== false;
this.convert_hash_in_init = this.history === true && options.convert_hash_in_init !== false;
this.every = { this.every = {
after: options.after || null, after: options.after || null,
before: options.before || null, before: options.before || null,
...@@ -426,6 +419,7 @@ Router.prototype.param = function(token, matcher) { ...@@ -426,6 +419,7 @@ Router.prototype.param = function(token, matcher) {
this.params[token] = function(str) { this.params[token] = function(str) {
return str.replace(compiled, matcher.source || matcher); return str.replace(compiled, matcher.source || matcher);
}; };
return this;
}; };
Router.prototype.on = Router.prototype.route = function(method, path, route) { Router.prototype.on = Router.prototype.route = function(method, path, route) {
...@@ -453,8 +447,20 @@ Router.prototype.on = Router.prototype.route = function(method, path, route) { ...@@ -453,8 +447,20 @@ Router.prototype.on = Router.prototype.route = function(method, path, route) {
this.insert(method, this.scope.concat(path), route); this.insert(method, this.scope.concat(path), route);
}; };
Router.prototype.path = function(path, routesFn) {
var self = this, length = this.scope.length;
if (path.source) {
path = path.source.replace(/\\\//ig, "/");
}
path = path.split(new RegExp(this.delimiter));
path = terminator(path, this.delimiter);
this.scope = this.scope.concat(path);
routesFn.call(this, this);
this.scope.splice(length, path.length);
};
Router.prototype.dispatch = function(method, path, callback) { Router.prototype.dispatch = function(method, path, callback) {
var self = this, fns = this.traverse(method, path, this.routes, ""), invoked = this._invoked, after; var self = this, fns = this.traverse(method, path.replace(QUERY_SEPARATOR, ""), this.routes, ""), invoked = this._invoked, after;
this._invoked = true; this._invoked = true;
if (!fns || fns.length === 0) { if (!fns || fns.length === 0) {
this.last = []; this.last = [];
...@@ -495,7 +501,7 @@ Router.prototype.invoke = function(fns, thisArg, callback) { ...@@ -495,7 +501,7 @@ Router.prototype.invoke = function(fns, thisArg, callback) {
if (Array.isArray(fn)) { if (Array.isArray(fn)) {
return _asyncEverySeries(fn, apply, next); return _asyncEverySeries(fn, apply, next);
} else if (typeof fn == "function") { } else if (typeof fn == "function") {
fn.apply(thisArg, fns.captures.concat(next)); fn.apply(thisArg, (fns.captures || []).concat(next));
} }
}; };
_asyncEverySeries(fns, apply, function() { _asyncEverySeries(fns, apply, function() {
......
/** vim: et:ts=4:sw=4:sts=4 /** vim: et:ts=4:sw=4:sts=4
* @license RequireJS 2.1.11 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. * @license RequireJS 2.1.16 Copyright (c) 2010-2015, The Dojo Foundation All Rights Reserved.
* Available via the MIT or new BSD license. * Available via the MIT or new BSD license.
* see: http://github.com/jrburke/requirejs for details * see: http://github.com/jrburke/requirejs for details
*/ */
...@@ -12,7 +12,7 @@ var requirejs, require, define; ...@@ -12,7 +12,7 @@ var requirejs, require, define;
(function (global) { (function (global) {
var req, s, head, baseElement, dataMain, src, var req, s, head, baseElement, dataMain, src,
interactiveScript, currentlyAddingScript, mainScript, subPath, interactiveScript, currentlyAddingScript, mainScript, subPath,
version = '2.1.11', version = '2.1.16',
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg, commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g, cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/, jsSuffixRegExp = /\.js$/,
...@@ -180,7 +180,7 @@ var requirejs, require, define; ...@@ -180,7 +180,7 @@ var requirejs, require, define;
if (typeof requirejs !== 'undefined') { if (typeof requirejs !== 'undefined') {
if (isFunction(requirejs)) { if (isFunction(requirejs)) {
//Do not overwrite and existing requirejs instance. //Do not overwrite an existing requirejs instance.
return; return;
} }
cfg = requirejs; cfg = requirejs;
...@@ -232,21 +232,20 @@ var requirejs, require, define; ...@@ -232,21 +232,20 @@ var requirejs, require, define;
* @param {Array} ary the array of path segments. * @param {Array} ary the array of path segments.
*/ */
function trimDots(ary) { function trimDots(ary) {
var i, part, length = ary.length; var i, part;
for (i = 0; i < length; i++) { for (i = 0; i < ary.length; i++) {
part = ary[i]; part = ary[i];
if (part === '.') { if (part === '.') {
ary.splice(i, 1); ary.splice(i, 1);
i -= 1; i -= 1;
} else if (part === '..') { } else if (part === '..') {
if (i === 1 && (ary[2] === '..' || ary[0] === '..')) { // If at the start, or previous value is still ..,
//End of the line. Keep at least one non-dot // keep them so that when converted to a path it may
//path segment at the front so it can be mapped // still work when converted to a path, even though
//correctly to disk. Otherwise, there is likely // as an ID it is less than ideal. In larger point
//no path mapping for a path starting with '..'. // releases, may be better to just kick out an error.
//This can still fail, but catches the most reasonable if (i === 0 || (i == 1 && ary[2] === '..') || ary[i - 1] === '..') {
//uses of .. continue;
break;
} else if (i > 0) { } else if (i > 0) {
ary.splice(i - 1, 2); ary.splice(i - 1, 2);
i -= 2; i -= 2;
...@@ -267,43 +266,37 @@ var requirejs, require, define; ...@@ -267,43 +266,37 @@ var requirejs, require, define;
*/ */
function normalize(name, baseName, applyMap) { function normalize(name, baseName, applyMap) {
var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex,
foundMap, foundI, foundStarMap, starI, foundMap, foundI, foundStarMap, starI, normalizedBaseParts,
baseParts = baseName && baseName.split('/'), baseParts = (baseName && baseName.split('/')),
normalizedBaseParts = baseParts,
map = config.map, map = config.map,
starMap = map && map['*']; starMap = map && map['*'];
//Adjust any relative paths. //Adjust any relative paths.
if (name && name.charAt(0) === '.') { if (name) {
//If have a base name, try to normalize against it, name = name.split('/');
//otherwise, assume it is a top-level require that will lastIndex = name.length - 1;
//be relative to baseUrl in the end.
if (baseName) { // If wanting node ID compatibility, strip .js from end
// of IDs. Have to do this here, and not in nameToUrl
// because node allows either .js or non .js to map
// to same file.
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
// Starts with a '.' so need the baseName
if (name[0].charAt(0) === '.' && baseParts) {
//Convert baseName to array, and lop off the last part, //Convert baseName to array, and lop off the last part,
//so that . matches that 'directory' and not name of the baseName's //so that . matches that 'directory' and not name of the baseName's
//module. For instance, baseName of 'one/two/three', maps to //module. For instance, baseName of 'one/two/three', maps to
//'one/two/three.js', but we want the directory, 'one/two' for //'one/two/three.js', but we want the directory, 'one/two' for
//this normalization. //this normalization.
normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); normalizedBaseParts = baseParts.slice(0, baseParts.length - 1);
name = name.split('/');
lastIndex = name.length - 1;
// If wanting node ID compatibility, strip .js from end
// of IDs. Have to do this here, and not in nameToUrl
// because node allows either .js or non .js to map
// to same file.
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
}
name = normalizedBaseParts.concat(name); name = normalizedBaseParts.concat(name);
trimDots(name);
name = name.join('/');
} else if (name.indexOf('./') === 0) {
// No baseName, so this is ID is resolved relative
// to baseUrl, pull off the leading dot.
name = name.substring(2);
} }
trimDots(name);
name = name.join('/');
} }
//Apply map config if available. //Apply map config if available.
...@@ -379,7 +372,13 @@ var requirejs, require, define; ...@@ -379,7 +372,13 @@ var requirejs, require, define;
//retry //retry
pathConfig.shift(); pathConfig.shift();
context.require.undef(id); context.require.undef(id);
context.require([id]);
//Custom require that does not do map translation, since
//ID is "absolute", already mapped/resolved.
context.makeRequire(null, {
skipMap: true
})([id]);
return true; return true;
} }
} }
...@@ -445,7 +444,16 @@ var requirejs, require, define; ...@@ -445,7 +444,16 @@ var requirejs, require, define;
return normalize(name, parentName, applyMap); return normalize(name, parentName, applyMap);
}); });
} else { } else {
normalizedName = normalize(name, parentName, applyMap); // If nested plugin references, then do not try to
// normalize, as it will not normalize correctly. This
// places a restriction on resourceIds, and the longer
// term solution is not to normalize until plugins are
// loaded and all normalizations to allow for async
// loading of a loader plugin. But for now, fixes the
// common uses. Details in #1131
normalizedName = name.indexOf('!') === -1 ?
normalize(name, parentName, applyMap) :
name;
} }
} else { } else {
//A regular module. //A regular module.
...@@ -1115,6 +1123,13 @@ var requirejs, require, define; ...@@ -1115,6 +1123,13 @@ var requirejs, require, define;
if (this.errback) { if (this.errback) {
on(depMap, 'error', bind(this, this.errback)); on(depMap, 'error', bind(this, this.errback));
} else if (this.events.error) {
// No direct errback on this module, but something
// else is listening for errors, so be sure to
// propagate the error correctly.
on(depMap, 'error', bind(this, function(err) {
this.emit('error', err);
}));
} }
} }
......
...@@ -351,6 +351,9 @@ ...@@ -351,6 +351,9 @@
if (regex.string.test(pattern)) { if (regex.string.test(pattern)) {
return trimQuotes(pattern); return trimQuotes(pattern);
} }
else if(!isNaN(pattern)) {
return +pattern;
}
// find params // find params
var paramsValues = []; var paramsValues = [];
if (!paramsFound && params) { if (!paramsFound && params) {
......
...@@ -643,7 +643,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -643,7 +643,7 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
'use strict'; 'use strict';
soma.version = '2.1.0'; soma.version = '2.1.1';
soma.applyProperties = function(target, extension, bindToExtension, list) { soma.applyProperties = function(target, extension, bindToExtension, list) {
if (Object.prototype.toString.apply(list) === '[object Array]') { if (Object.prototype.toString.apply(list) === '[object Array]') {
...@@ -776,13 +776,28 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ...@@ -776,13 +776,28 @@ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
throw new Error('Error creating a plugin, plugin class is missing.'); throw new Error('Error creating a plugin, plugin class is missing.');
} }
var params = infuse.getConstructorParams(arguments[0]); var params = infuse.getConstructorParams(arguments[0]);
// add plugin function
var args = [arguments[0]]; var args = [arguments[0]];
for (var i=0; i<params.length; i++) { // add injection mappings
for (var i=0, l=params.length; i < l; i++) {
if (this.injector.hasMapping(params[i]) || this.injector.hasInheritedMapping(params[i])) { if (this.injector.hasMapping(params[i]) || this.injector.hasInheritedMapping(params[i])) {
args.push(this.injector.getValue(params[i])); args.push(this.injector.getValue(params[i]));
} }
else {
args.push(undefined);
}
}
// trim array
for (var a = args.length-1, b = args.length; a >= 0; a--) {
if (typeof args[a] === 'undefined') {
args.splice(a, 1);
}
else {
break;
}
} }
for (var j=1; j<arguments.length; j++) { // add arguments
for (var j=1, k=arguments.length; j < k; j++) {
args.push(arguments[j]); args.push(arguments[j]);
} }
return this.injector.createInstance.apply(this.injector, args); return this.injector.createInstance.apply(this.injector, args);
......
hr {
margin: 20px 0;
border: 0;
border-top: 1px dashed #c5c5c5;
border-bottom: 1px dashed #f7f7f7;
}
.learn a {
font-weight: normal;
text-decoration: none;
color: #b83f45;
}
.learn a:hover {
text-decoration: underline;
color: #787e7e;
}
.learn h3,
.learn h4,
.learn h5 {
margin: 10px 0;
font-weight: 500;
line-height: 1.2;
color: #000;
}
.learn h3 {
font-size: 24px;
}
.learn h4 {
font-size: 18px;
}
.learn h5 {
margin-bottom: 0;
font-size: 14px;
}
.learn ul {
padding: 0;
margin: 0 0 30px 25px;
}
.learn li {
line-height: 20px;
}
.learn p {
font-size: 15px;
font-weight: 300;
line-height: 1.3;
margin-top: 0;
margin-bottom: 0;
}
#issue-count {
display: none;
}
.quote {
border: none;
margin: 20px 0 60px 0;
}
.quote p {
font-style: italic;
}
.quote p:before {
content: '“';
font-size: 50px;
opacity: .15;
position: absolute;
top: -20px;
left: 3px;
}
.quote p:after {
content: '”';
font-size: 50px;
opacity: .15;
position: absolute;
bottom: -42px;
right: 3px;
}
.quote footer {
position: absolute;
bottom: -40px;
right: 0;
}
.quote footer img {
border-radius: 3px;
}
.quote footer a {
margin-left: 5px;
vertical-align: middle;
}
.speech-bubble {
position: relative;
padding: 10px;
background: rgba(0, 0, 0, .04);
border-radius: 5px;
}
.speech-bubble:after {
content: '';
position: absolute;
top: 100%;
right: 30px;
border: 13px solid transparent;
border-top-color: rgba(0, 0, 0, .04);
}
.learn-bar > .learn {
position: absolute;
width: 272px;
top: 8px;
left: -300px;
padding: 10px;
border-radius: 5px;
background-color: rgba(255, 255, 255, .6);
transition-property: left;
transition-duration: 500ms;
}
@media (min-width: 899px) {
.learn-bar {
width: auto;
padding-left: 300px;
}
.learn-bar > .learn {
left: 8px;
}
}
/* global _ */
(function () { (function () {
'use strict'; 'use strict';
/* jshint ignore:start */
// Underscore's Template Module // Underscore's Template Module
// Courtesy of underscorejs.org // Courtesy of underscorejs.org
var _ = (function (_) { var _ = (function (_) {
...@@ -114,6 +116,7 @@ ...@@ -114,6 +116,7 @@
if (location.hostname === 'todomvc.com') { if (location.hostname === 'todomvc.com') {
window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script')); window._gaq = [['_setAccount','UA-31081062-1'],['_trackPageview']];(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.src='//www.google-analytics.com/ga.js';s.parentNode.insertBefore(g,s)}(document,'script'));
} }
/* jshint ignore:end */
function redirect() { function redirect() {
if (location.hostname === 'tastejs.github.io') { if (location.hostname === 'tastejs.github.io') {
...@@ -175,13 +178,17 @@ ...@@ -175,13 +178,17 @@
if (learnJSON.backend) { if (learnJSON.backend) {
this.frameworkJSON = learnJSON.backend; this.frameworkJSON = learnJSON.backend;
this.frameworkJSON.issueLabel = framework;
this.append({ this.append({
backend: true backend: true
}); });
} else if (learnJSON[framework]) { } else if (learnJSON[framework]) {
this.frameworkJSON = learnJSON[framework]; this.frameworkJSON = learnJSON[framework];
this.frameworkJSON.issueLabel = framework;
this.append(); this.append();
} }
this.fetchIssueCount();
} }
Learn.prototype.append = function (opts) { Learn.prototype.append = function (opts) {
...@@ -212,6 +219,26 @@ ...@@ -212,6 +219,26 @@
document.body.insertAdjacentHTML('afterBegin', aside.outerHTML); document.body.insertAdjacentHTML('afterBegin', aside.outerHTML);
}; };
Learn.prototype.fetchIssueCount = function () {
var issueLink = document.getElementById('issue-count-link');
if (issueLink) {
var url = issueLink.href.replace('https://github.com', 'https://api.github.com/repos');
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function (e) {
var parsedResponse = JSON.parse(e.target.responseText);
if (parsedResponse instanceof Array) {
var count = parsedResponse.length
if (count !== 0) {
issueLink.innerHTML = 'This app has ' + count + ' open issues';
document.getElementById('issue-count').style.display = 'inline';
}
}
};
xhr.send();
}
};
redirect(); redirect();
getFile('learn.json', Learn); getFile('learn.json', Learn);
})(); })();
{ {
"name": "todomvc-somajs", "private": true,
"version": "0.0.0",
"dependencies": { "dependencies": {
"todomvc-common": "~0.3.0", "todomvc-common": "^1.0.1",
"todomvc-app-css": "^1.0.1",
"director": "~1.2.0", "director": "~1.2.0",
"requirejs": "~2.1.5", "requirejs": "~2.1.5",
"soma.js": "~2.1.0", "soma.js": "~2.1.0",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment