]> git.za3k.com Git - flowy.git/commitdiff
Split js files apart
authorZachary Vance <vanceza@gmail.com>
Fri, 20 Mar 2015 01:59:48 +0000 (18:59 -0700)
committerZachary Vance <vanceza@gmail.com>
Fri, 20 Mar 2015 01:59:48 +0000 (18:59 -0700)
33 files changed:
Gruntfile.js
dist/flowy.js
dist/flowy.unwrapped.js [moved from src/models/test.js with 96% similarity]
node_modules/grunt-concat-in-order/.jshintrc [new file with mode: 0644]
node_modules/grunt-concat-in-order/.npmignore [new file with mode: 0644]
node_modules/grunt-concat-in-order/Gruntfile.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/LICENSE-MIT [new file with mode: 0644]
node_modules/grunt-concat-in-order/README.md [new file with mode: 0644]
node_modules/grunt-concat-in-order/package.json [new file with mode: 0644]
node_modules/grunt-concat-in-order/tasks/concat_in_order.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/concat_in_order_test.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/cycle/A.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/cycle/B.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/cycle/C.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/default/AUsingBaseA.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/default/AUsingBaseBAndBaseA.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/default/BaseA.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/default/BaseBUsingBaseA.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/expected/custom_options [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/expected/default_options.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/expected/filebased_options.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/filebased/AUsingBaseA.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/filebased/AUsingBaseBAndBaseA.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/filebased/BaseA.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/filebased/BaseBUsingBaseA.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/missing/lowercase.js [new file with mode: 0644]
node_modules/grunt-concat-in-order/test/missing/requiresUpperCase.js [new file with mode: 0644]
package.json
src/flowy.js [new file with mode: 0644]
src/models/flowyDoc.js [new file with mode: 0644]
src/models/todo.js [new file with mode: 0644]
src/views/app.js [new file with mode: 0644]
src/views/todo.js [new file with mode: 0644]

index da26c6de4534d4db41f85fa1928c1b53cca20371..b4cc9355dea78504797b558c12413db46a57b47c 100644 (file)
@@ -1,4 +1,6 @@
+
 module.exports = function(grunt) {
+  path = require("path");
 
   grunt.initConfig({
     pkg: grunt.file.readJSON('package.json'),
@@ -24,16 +26,41 @@ module.exports = function(grunt) {
       }
     },
     concat: {
-      options: {
-        separator: ';'
-      },
-      dist: {
-        src: ['src/**/*.js'],
-        dest: 'dist/<%= pkg.name %>.js'
-      },
       css: {
         src: ['src/**/*.css'],
         dest: 'dist/<%= pkg.name %>.css'
+      },
+      js: { // Wrap subtask
+        options: {
+          banner: "$(function(){\n",
+          footer: "\n});\n",
+        },
+        src: 'dist/<%= pkg.name %>.unwrapped.js',
+        dest: 'dist/<%= pkg.name %>.js',
+      }
+    },
+    concat_in_order: {
+      options: {
+        separator: ';',
+        extractRequired: function(filepath, filecontent) {
+            var workingdir = path.normalize(filepath).split(path.sep);
+            workingdir.pop();
+
+            var deps = this.getMatches(/\*\s*@depend\s(.*\.js)/g, filecontent);
+            deps.forEach(function(dep, i) {
+                var dependency = workingdir.concat([dep]);
+                deps[i] = path.join.apply(null, dependency);
+            });
+            return deps;
+        },
+        extractDeclared: function(filepath) {
+            return [filepath];
+        },
+        onlyConcatRequiredFiles: true
+      },
+      dist: {
+        src: ['src/flowy.js'],
+        dest: 'dist/<%= pkg.name %>.unwrapped.js'
       }
     },
     copy: {
@@ -48,8 +75,9 @@ module.exports = function(grunt) {
   grunt.loadNpmTasks('grunt-bower-concat');
   grunt.loadNpmTasks('grunt-contrib-copy');
   grunt.loadNpmTasks('grunt-contrib-concat');
+  grunt.loadNpmTasks('grunt-concat-in-order');
   grunt.loadNpmTasks('grunt-contrib-jshint');
   grunt.loadNpmTasks('grunt-contrib-watch');
 
-  grunt.registerTask('default', ['jshint', 'bower_concat', 'concat', 'copy']);
+  grunt.registerTask('default', ['jshint', 'bower_concat', 'concat_in_order', 'concat', 'copy']);
 };
index bf6b3a3663fd56abf421e7dd1dccaa1a2401e1cb..697df19a33095127f60871d6aab1d1fb2aa11e55 100644 (file)
@@ -1,4 +1,35 @@
-$(function() {
+$(function(){
+var TodoView = Backbone.View.extend({
+  tagName: 'div',
+  className: 'todo',
+  events: {
+    "click > .text": "toggleComplete",
+  },
+  initialize: function() {
+    this.listenTo(this.model, "change", this.render);
+    this.listenTo(this.model, 'destroy', this.remove);
+  },
+  toggleComplete: function(e) {
+    this.model.toggleComplete();
+    e.stopPropagation();
+  },
+  template: "<div class=\"text\">{{text}}</div><div class=\"bullets\"></div>",
+  addChild: function(el, position) {
+     if(typeof position === 'undefined') {
+        console.log("TodoView:addChild called without a position");
+     }
+     this.$el.find("> .bullets").append(el);
+     return this;
+  },
+  render: function() {
+    var oldChildren = this.$el.find("> .bullets > *").detach(); // detach keeps handlers
+    this.$el.html(Mustache.to_html(this.template, this.model.toJSON())); // Should hopefully be model.attributes
+    this.$el.toggleClass('completed', this.model.get('completed'));
+    this.$el.toggleClass('collapsed', this.model.get('collapsed'));
+    this.$el.find("> .bullets").append(oldChildren);
+    return this;
+  },
+});
 
 var TodoModel = Backbone.Model.extend({
     defaults: function() {
@@ -35,6 +66,10 @@ var TodoModel = Backbone.Model.extend({
     },
 });
 
+/**
+ * @depend ../models/todo.js
+ */
+
 flowyDocDefaults = { rootId: null };
 var FlowyDocModel = Backbone.Collection.extend({
     initialize: function(options) {
@@ -66,6 +101,12 @@ var FlowyDocModel = Backbone.Collection.extend({
     comparator: 'order',
 });
 
+/**
+ * @depend ../views/todo.js
+ * @depend ../models/flowyDoc.js
+ * @depend ../models/todo.js
+ */
+
 var todos = new FlowyDocModel({
     id: "master",
     default: [
@@ -113,38 +154,6 @@ var todos = new FlowyDocModel({
     ],
 });
 
-var TodoView = Backbone.View.extend({
-  tagName: 'div',
-  className: 'todo',
-  events: {
-    "click > .text": "toggleComplete",
-  },
-  initialize: function() {
-    this.listenTo(this.model, "change", this.render);
-    this.listenTo(this.model, 'destroy', this.remove);
-  },
-  toggleComplete: function(e) {
-    this.model.toggleComplete();
-    e.stopPropagation();
-  },
-  template: "<div class=\"text\">{{text}}</div><div class=\"bullets\"></div>",
-  addChild: function(el, position) {
-     if(typeof position === 'undefined') {
-        console.log("TodoView:addChild called without a position");
-     }
-     this.$el.find("> .bullets").append(el);
-     return this;
-  },
-  render: function() {
-    var oldChildren = this.$el.find("> .bullets > *").detach(); // detach keeps handlers
-    this.$el.html(Mustache.to_html(this.template, this.model.toJSON())); // Should hopefully be model.attributes
-    this.$el.toggleClass('completed', this.model.get('completed'));
-    this.$el.toggleClass('collapsed', this.model.get('collapsed'));
-    this.$el.find("> .bullets").append(oldChildren);
-    return this;
-  },
-});
-
 var appDefaults = { list: todos };
 var AppView = Backbone.View.extend({
     el: $("#todo-app"),
@@ -196,6 +205,12 @@ var AppView = Backbone.View.extend({
     },
 });
 
-var flowyView = new AppView();
+/**
+ * @depend views/app.js
+ */
+
+$(function() {
+    var flowyView = new AppView();
+});
 
 });
similarity index 96%
rename from src/models/test.js
rename to dist/flowy.unwrapped.js
index bf6b3a3663fd56abf421e7dd1dccaa1a2401e1cb..75d80022edea1a45c6177596844c338c059d0330 100644 (file)
@@ -1,4 +1,34 @@
-$(function() {
+var TodoView = Backbone.View.extend({
+  tagName: 'div',
+  className: 'todo',
+  events: {
+    "click > .text": "toggleComplete",
+  },
+  initialize: function() {
+    this.listenTo(this.model, "change", this.render);
+    this.listenTo(this.model, 'destroy', this.remove);
+  },
+  toggleComplete: function(e) {
+    this.model.toggleComplete();
+    e.stopPropagation();
+  },
+  template: "<div class=\"text\">{{text}}</div><div class=\"bullets\"></div>",
+  addChild: function(el, position) {
+     if(typeof position === 'undefined') {
+        console.log("TodoView:addChild called without a position");
+     }
+     this.$el.find("> .bullets").append(el);
+     return this;
+  },
+  render: function() {
+    var oldChildren = this.$el.find("> .bullets > *").detach(); // detach keeps handlers
+    this.$el.html(Mustache.to_html(this.template, this.model.toJSON())); // Should hopefully be model.attributes
+    this.$el.toggleClass('completed', this.model.get('completed'));
+    this.$el.toggleClass('collapsed', this.model.get('collapsed'));
+    this.$el.find("> .bullets").append(oldChildren);
+    return this;
+  },
+});
 
 var TodoModel = Backbone.Model.extend({
     defaults: function() {
@@ -35,6 +65,10 @@ var TodoModel = Backbone.Model.extend({
     },
 });
 
+/**
+ * @depend ../models/todo.js
+ */
+
 flowyDocDefaults = { rootId: null };
 var FlowyDocModel = Backbone.Collection.extend({
     initialize: function(options) {
@@ -66,6 +100,12 @@ var FlowyDocModel = Backbone.Collection.extend({
     comparator: 'order',
 });
 
+/**
+ * @depend ../views/todo.js
+ * @depend ../models/flowyDoc.js
+ * @depend ../models/todo.js
+ */
+
 var todos = new FlowyDocModel({
     id: "master",
     default: [
@@ -113,38 +153,6 @@ var todos = new FlowyDocModel({
     ],
 });
 
-var TodoView = Backbone.View.extend({
-  tagName: 'div',
-  className: 'todo',
-  events: {
-    "click > .text": "toggleComplete",
-  },
-  initialize: function() {
-    this.listenTo(this.model, "change", this.render);
-    this.listenTo(this.model, 'destroy', this.remove);
-  },
-  toggleComplete: function(e) {
-    this.model.toggleComplete();
-    e.stopPropagation();
-  },
-  template: "<div class=\"text\">{{text}}</div><div class=\"bullets\"></div>",
-  addChild: function(el, position) {
-     if(typeof position === 'undefined') {
-        console.log("TodoView:addChild called without a position");
-     }
-     this.$el.find("> .bullets").append(el);
-     return this;
-  },
-  render: function() {
-    var oldChildren = this.$el.find("> .bullets > *").detach(); // detach keeps handlers
-    this.$el.html(Mustache.to_html(this.template, this.model.toJSON())); // Should hopefully be model.attributes
-    this.$el.toggleClass('completed', this.model.get('completed'));
-    this.$el.toggleClass('collapsed', this.model.get('collapsed'));
-    this.$el.find("> .bullets").append(oldChildren);
-    return this;
-  },
-});
-
 var appDefaults = { list: todos };
 var AppView = Backbone.View.extend({
     el: $("#todo-app"),
@@ -196,6 +204,10 @@ var AppView = Backbone.View.extend({
     },
 });
 
-var flowyView = new AppView();
+/**
+ * @depend views/app.js
+ */
 
+$(function() {
+    var flowyView = new AppView();
 });
diff --git a/node_modules/grunt-concat-in-order/.jshintrc b/node_modules/grunt-concat-in-order/.jshintrc
new file mode 100644 (file)
index 0000000..948d55e
--- /dev/null
@@ -0,0 +1,13 @@
+{\r
+  "curly": true,\r
+  "eqeqeq": true,\r
+  "immed": true,\r
+  "latedef": true,\r
+  "newcap": true,\r
+  "noarg": true,\r
+  "sub": true,\r
+  "undef": true,\r
+  "boss": true,\r
+  "eqnull": true,\r
+  "node": true\r
+}\r
diff --git a/node_modules/grunt-concat-in-order/.npmignore b/node_modules/grunt-concat-in-order/.npmignore
new file mode 100644 (file)
index 0000000..7903979
--- /dev/null
@@ -0,0 +1,4 @@
+node_modules\r
+npm-debug.log\r
+tmp\r
+.idea\r
diff --git a/node_modules/grunt-concat-in-order/Gruntfile.js b/node_modules/grunt-concat-in-order/Gruntfile.js
new file mode 100644 (file)
index 0000000..af9693f
--- /dev/null
@@ -0,0 +1,95 @@
+/*\r
+ * grunt-concat-in-order\r
+ * https://github.com/miensol/grunt-concat-in-order\r
+ *\r
+ * Copyright (c) 2013 Piotr Mionskowski\r
+ * Licensed under the MIT license.\r
+ */\r
+\r
+'use strict';\r
+\r
+module.exports = function(grunt) {\r
+    var path = require('path');\r
+\r
+    // Project configuration.\r
+    grunt.initConfig({\r
+        jshint: {\r
+            all: [\r
+                'Gruntfile.js',\r
+                'tasks/*.js',\r
+                '<%= nodeunit.tests %>'\r
+            ],\r
+            options: {\r
+                jshintrc: '.jshintrc'\r
+            }\r
+        },\r
+\r
+        // Before generating any new files, remove any previously-created files.\r
+        clean: {\r
+            tests: ['tmp']\r
+        },\r
+\r
+        // Configuration to be run (and then tested).\r
+        concat_in_order: {\r
+            default_options: {\r
+                files: {\r
+                    'tmp/default_options.js': ['test/default/**/*.js']\r
+                }\r
+            },\r
+            cycle_options: {\r
+                files: {\r
+                    'tmp/cycle_options.js': ['test/cycle/**/*.js']\r
+                }\r
+            },\r
+            missing_options: {\r
+                files: {\r
+                    'tmp/missing_options.js': ['test/missing/**/*.js']\r
+                }\r
+            },\r
+            filebased_options: {\r
+                files: {\r
+                    'tmp/filebased_options.js': ['test/filebased/AUsingBaseBAndBaseA.js']\r
+                },\r
+                options: {\r
+                    extractRequired: function(filepath, filecontent) {\r
+                        var workingdir = path.normalize(filepath).split(path.sep);\r
+                        workingdir.pop();\r
+\r
+                        var deps = this.getMatches(/\*\s*@depend\s(.*\.js)/g, filecontent);\r
+                        deps.forEach(function(dep, i) {\r
+                            var dependency = workingdir.concat([dep]);\r
+                            deps[i] = path.join.apply(null, dependency);\r
+                        });\r
+                        return deps;\r
+                    },\r
+                    extractDeclared: function(filepath) {\r
+                        return [filepath];\r
+                    },\r
+                    onlyConcatRequiredFiles: true\r
+                }\r
+            }\r
+        },\r
+\r
+        // Unit tests.\r
+        nodeunit: {\r
+            tests: ['test/*_test.js']\r
+        }\r
+\r
+    });\r
+\r
+    // Actually load this plugin's task(s).\r
+    grunt.loadTasks('tasks');\r
+\r
+    // These plugins provide necessary tasks.\r
+    grunt.loadNpmTasks('grunt-contrib-jshint');\r
+    grunt.loadNpmTasks('grunt-contrib-clean');\r
+    grunt.loadNpmTasks('grunt-contrib-nodeunit');\r
+\r
+    // Whenever the "test" task is run, first clean the "tmp" dir, then run this\r
+    // plugin's task(s), then test the result.\r
+    grunt.registerTask('test', ['clean', 'concat_in_order:default_options', 'concat_in_order:filebased_options', 'nodeunit']);\r
+\r
+    // By default, lint and run all tests.\r
+    grunt.registerTask('default', ['jshint', 'test']);\r
+\r
+};\r
diff --git a/node_modules/grunt-concat-in-order/LICENSE-MIT b/node_modules/grunt-concat-in-order/LICENSE-MIT
new file mode 100644 (file)
index 0000000..42224a2
--- /dev/null
@@ -0,0 +1,22 @@
+Copyright (c) 2013 Piotr Mionskowski\r
+\r
+Permission is hereby granted, free of charge, to any person\r
+obtaining a copy of this software and associated documentation\r
+files (the "Software"), to deal in the Software without\r
+restriction, including without limitation the rights to use,\r
+copy, modify, merge, publish, distribute, sublicense, and/or sell\r
+copies of the Software, and to permit persons to whom the\r
+Software is furnished to do so, subject to the following\r
+conditions:\r
+\r
+The above copyright notice and this permission notice shall be\r
+included in all copies or substantial portions of the Software.\r
+\r
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\r
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT\r
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,\r
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\r
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR\r
+OTHER DEALINGS IN THE SOFTWARE.\r
diff --git a/node_modules/grunt-concat-in-order/README.md b/node_modules/grunt-concat-in-order/README.md
new file mode 100644 (file)
index 0000000..2917914
--- /dev/null
@@ -0,0 +1,168 @@
+# grunt-concat-in-order\r
+\r
+> Concatenates files respecting dependencies.\r
+\r
+## Getting Started\r
+This plugin requires Grunt `~0.4.1`\r
+\r
+If you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:\r
+\r
+```shell\r
+npm install grunt-concat-in-order --save-dev\r
+```\r
+\r
+Once the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:\r
+\r
+```js\r
+grunt.loadNpmTasks('grunt-concat-in-order');\r
+```\r
+\r
+## The "concat_in_order" task\r
+\r
+### Overview\r
+\r
+The `concat_in_order` task extracts declared required dependencies as well as provided modules/classes from your javascript (or any other text) files. Having this [dependency graph](http://en.wikipedia.org/wiki/Dependency_graph) the task will perform [topological sort](http://en.wikipedia.org/wiki/Topological_sorting) and concatenate file so that all modules will be put after their required dependencies.\r
+\r
+In your project's Gruntfile, add a section named `concat_in_order` to the data object passed into `grunt.initConfig()`.\r
+\r
+```js\r
+grunt.initConfig({\r
+  concat_in_order: {\r
+    your_target: {\r
+      options: {\r
+          /*\r
+          this is a default function that extracts required dependencies/module names from file content\r
+          (getMatches - function that pick groups from given regexp)\r
+          extractRequired: function (filepath, filecontent) {\r
+            return this.getMatches(/require\(['"]([^'"]+)['"]/g, filecontent);\r
+          },\r
+          this is a default function that extracts declared modules names from file content\r
+          extractDeclared: function (filepath, filecontent) {\r
+            return this.getMatches(/declare\(['"]([^'"]+)['"]/g, filecontent);\r
+          }\r
+          */\r
+      },\r
+      files: {\r
+        'build/concatenated.js': ['lib/**/*.js']\r
+      }\r
+    }\r
+  }\r
+})\r
+```\r
+### Sample\r
+Let's say you have 4 files in a `lib` directory\r
+\r
+- AUsingBaseBAndBaseA.js\r
+\r
+```js\r
+/*start AUsingBaseBAndBaseA*/\r
+framwork.require('module.BaseB');\r
+framwork.require('module.BaseA');\r
+framework.declare('module.UsingBaseBAndBaseA');\r
+var forth = function fourthFunction(){};\r
+/*end AUsingBaseBAnddBaseA*/\r
+```\r
+\r
+- AUsingBaseA.js\r
+\r
+```js\r
+/*start AUsingBaseA*/\r
+framwork.require('module.BaseA');\r
+var second = function secondFunction(){};\r
+/*end AUsingBaseA*/\r
+```\r
+\r
+\r
+- BaseA.js\r
+\r
+```js\r
+/*start BaseA*/\r
+framework.declare('module.BaseA');\r
+var first = function firstFunction(){};\r
+/*end BaseA*/\r
+```\r
+\r
+- BaseBUsingBaseA.js\r
+\r
+```js\r
+/*start BaseBUsingBaseA*/\r
+framwork.require('module.BaseA');\r
+framework.declare('module.BaseBUsingBaseA');\r
+framework.declare('module.BaseB');\r
+var third = function thirdFunction(){};\r
+/*end  BaseBUsingBaseA*/\r
+```\r
+Given the above configuration the task will produce `build/concatenated.js` file with following content:\r
+\r
+```js\r
+/*start BaseA*/\r
+framework.declare('module.BaseA');\r
+var first = function firstFunction(){};\r
+/*end BaseA*/\r
+/*start BaseBUsingBaseA*/\r
+framwork.require('module.BaseA');\r
+framework.declare('module.BaseBUsingBaseA');\r
+framework.declare('module.BaseB');\r
+var third = function thirdFunction(){};\r
+/*end  BaseBUsingBaseA*/\r
+/*start AUsingBaseA*/\r
+framwork.require('module.BaseA');\r
+var second = function secondFunction(){};\r
+/*end AUsingBaseA*/\r
+/*start AUsingBaseBAndBaseA*/\r
+framwork.require('module.BaseB');\r
+framwork.require('module.BaseA');\r
+framework.declare('module.UsingBaseBAndBaseA');\r
+var forth = function fourthFunction(){};\r
+/*end AUsingBaseBAnddBaseA*/\r
+```\r
+\r
+### File based\r
+You can enable automatic addition of files with the following example. (notice the onlyConcatRequiredFiles : true) This is the same way of declaring dependencies used by [juicer](https://github.com/cjohansen/juicer)\r
+\r
+    files: {\r
+        'dist/mybuild.js': ['js/src/main.js']\r
+    },\r
+    options: {\r
+        extractRequired: function(filepath, filecontent) {\r
+            var workingdir = path.normalize(filepath).split(path.sep);\r
+            workingdir.pop();\r
+\r
+            var deps = this.getMatches(/\*\s*@depend\s(.*\.js)/g, filecontent);\r
+            deps.forEach(function(dep, i) {\r
+                var dependency = workingdir.concat([dep]);\r
+                deps[i] = path.join.apply(null, dependency);\r
+            });\r
+            return deps;\r
+        },\r
+        extractDeclared: function(filepath) {\r
+            return [filepath];\r
+        },\r
+        onlyConcatRequiredFiles: true\r
+    }\r
+\r
+This will declare all files as modules using their filenames. In main.js you will typically have these depend statements:\r
+\r
+    /**\r
+     * @depend ../lib/jquery.js\r
+     * @depend otherfile.js\r
+     * @depend calculator/add.js\r
+     */\r
+\r
+You only need to specify the main.js and the other dependencies will be added automatically. As well as their dependencies etc.\r
+\r
+If you want to add a file that isn't referenced anywhere you need to add it manually.\r
+\r
+    files: {\r
+        'dist/mybuild.js': ['js/src/main.js', 'js/src/unReferencedButWanted.js']\r
+    },\r
+\r
+The option onlyConcatRequiredFiles will only work if modules are declared and required with their actual filenames.\r
+\r
+## Contributing\r
+In lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).\r
+\r
+## Release History\r
+\r
+- 0.1.6 - @mokkabonna updated documenation and fixed path splitting in sample\r
+- 0.1.4 - @mokkabonna added ability to concat only files that are required by some module\r
diff --git a/node_modules/grunt-concat-in-order/package.json b/node_modules/grunt-concat-in-order/package.json
new file mode 100644 (file)
index 0000000..6ee234c
--- /dev/null
@@ -0,0 +1,73 @@
+{
+  "name": "grunt-concat-in-order",
+  "description": "Concatenates files respecting declared, required dependencies order.",
+  "version": "0.1.6",
+  "homepage": "https://github.com/miensol/grunt-concat-in-order",
+  "author": {
+    "name": "Piotr Mionskowski",
+    "email": "piotr.mionskowski@gmail.com"
+  },
+  "repository": {
+    "type": "git",
+    "url": "git://github.com/miensol/grunt-concat-in-order.git"
+  },
+  "bugs": {
+    "url": "https://github.com/miensol/grunt-concat-in-order/issues"
+  },
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://github.com/miensol/grunt-concat-in-order/blob/master/LICENSE-MIT"
+    }
+  ],
+  "main": "Gruntfile.js",
+  "engines": {
+    "node": ">= 0.8.0"
+  },
+  "scripts": {
+    "test": "grunt test"
+  },
+  "dependencies": {},
+  "devDependencies": {
+    "grunt-contrib-jshint": "~0.6.0",
+    "grunt-contrib-clean": "~0.4.0",
+    "grunt-contrib-nodeunit": "~0.2.0",
+    "grunt": "~0.4.1"
+  },
+  "peerDependencies": {
+    "grunt": "~0.4.1"
+  },
+  "keywords": [
+    "gruntplugin",
+    "concat",
+    "concat-in-order",
+    "topological sort",
+    "dependency tree",
+    "concat in order",
+    "resolve file order",
+    "sort dependencies",
+    "dependency"
+  ],
+  "readme": "# grunt-concat-in-order\r\n\r\n> Concatenates files respecting dependencies.\r\n\r\n## Getting Started\r\nThis plugin requires Grunt `~0.4.1`\r\n\r\nIf you haven't used [Grunt](http://gruntjs.com/) before, be sure to check out the [Getting Started](http://gruntjs.com/getting-started) guide, as it explains how to create a [Gruntfile](http://gruntjs.com/sample-gruntfile) as well as install and use Grunt plugins. Once you're familiar with that process, you may install this plugin with this command:\r\n\r\n```shell\r\nnpm install grunt-concat-in-order --save-dev\r\n```\r\n\r\nOnce the plugin has been installed, it may be enabled inside your Gruntfile with this line of JavaScript:\r\n\r\n```js\r\ngrunt.loadNpmTasks('grunt-concat-in-order');\r\n```\r\n\r\n## The \"concat_in_order\" task\r\n\r\n### Overview\r\n\r\nThe `concat_in_order` task extracts declared required dependencies as well as provided modules/classes from your javascript (or any other text) files. Having this [dependency graph](http://en.wikipedia.org/wiki/Dependency_graph) the task will perform [topological sort](http://en.wikipedia.org/wiki/Topological_sorting) and concatenate file so that all modules will be put after their required dependencies.\r\n\r\nIn your project's Gruntfile, add a section named `concat_in_order` to the data object passed into `grunt.initConfig()`.\r\n\r\n```js\r\ngrunt.initConfig({\r\n  concat_in_order: {\r\n    your_target: {\r\n      options: {\r\n          /*\r\n          this is a default function that extracts required dependencies/module names from file content\r\n          (getMatches - function that pick groups from given regexp)\r\n          extractRequired: function (filepath, filecontent) {\r\n            return this.getMatches(/require\\(['\"]([^'\"]+)['\"]/g, filecontent);\r\n          },\r\n          this is a default function that extracts declared modules names from file content\r\n          extractDeclared: function (filepath, filecontent) {\r\n            return this.getMatches(/declare\\(['\"]([^'\"]+)['\"]/g, filecontent);\r\n          }\r\n          */\r\n      },\r\n      files: {\r\n        'build/concatenated.js': ['lib/**/*.js']\r\n      }\r\n    }\r\n  }\r\n})\r\n```\r\n### Sample\r\nLet's say you have 4 files in a `lib` directory\r\n\r\n- AUsingBaseBAndBaseA.js\r\n\r\n```js\r\n/*start AUsingBaseBAndBaseA*/\r\nframwork.require('module.BaseB');\r\nframwork.require('module.BaseA');\r\nframework.declare('module.UsingBaseBAndBaseA');\r\nvar forth = function fourthFunction(){};\r\n/*end AUsingBaseBAnddBaseA*/\r\n```\r\n\r\n- AUsingBaseA.js\r\n\r\n```js\r\n/*start AUsingBaseA*/\r\nframwork.require('module.BaseA');\r\nvar second = function secondFunction(){};\r\n/*end AUsingBaseA*/\r\n```\r\n\r\n\r\n- BaseA.js\r\n\r\n```js\r\n/*start BaseA*/\r\nframework.declare('module.BaseA');\r\nvar first = function firstFunction(){};\r\n/*end BaseA*/\r\n```\r\n\r\n- BaseBUsingBaseA.js\r\n\r\n```js\r\n/*start BaseBUsingBaseA*/\r\nframwork.require('module.BaseA');\r\nframework.declare('module.BaseBUsingBaseA');\r\nframework.declare('module.BaseB');\r\nvar third = function thirdFunction(){};\r\n/*end  BaseBUsingBaseA*/\r\n```\r\nGiven the above configuration the task will produce `build/concatenated.js` file with following content:\r\n\r\n```js\r\n/*start BaseA*/\r\nframework.declare('module.BaseA');\r\nvar first = function firstFunction(){};\r\n/*end BaseA*/\r\n/*start BaseBUsingBaseA*/\r\nframwork.require('module.BaseA');\r\nframework.declare('module.BaseBUsingBaseA');\r\nframework.declare('module.BaseB');\r\nvar third = function thirdFunction(){};\r\n/*end  BaseBUsingBaseA*/\r\n/*start AUsingBaseA*/\r\nframwork.require('module.BaseA');\r\nvar second = function secondFunction(){};\r\n/*end AUsingBaseA*/\r\n/*start AUsingBaseBAndBaseA*/\r\nframwork.require('module.BaseB');\r\nframwork.require('module.BaseA');\r\nframework.declare('module.UsingBaseBAndBaseA');\r\nvar forth = function fourthFunction(){};\r\n/*end AUsingBaseBAnddBaseA*/\r\n```\r\n\r\n### File based\r\nYou can enable automatic addition of files with the following example. (notice the onlyConcatRequiredFiles : true) This is the same way of declaring dependencies used by [juicer](https://github.com/cjohansen/juicer)\r\n\r\n    files: {\r\n        'dist/mybuild.js': ['js/src/main.js']\r\n    },\r\n    options: {\r\n        extractRequired: function(filepath, filecontent) {\r\n            var workingdir = path.normalize(filepath).split(path.sep);\r\n            workingdir.pop();\r\n\r\n            var deps = this.getMatches(/\\*\\s*@depend\\s(.*\\.js)/g, filecontent);\r\n            deps.forEach(function(dep, i) {\r\n                var dependency = workingdir.concat([dep]);\r\n                deps[i] = path.join.apply(null, dependency);\r\n            });\r\n            return deps;\r\n        },\r\n        extractDeclared: function(filepath) {\r\n            return [filepath];\r\n        },\r\n        onlyConcatRequiredFiles: true\r\n    }\r\n\r\nThis will declare all files as modules using their filenames. In main.js you will typically have these depend statements:\r\n\r\n    /**\r\n     * @depend ../lib/jquery.js\r\n     * @depend otherfile.js\r\n     * @depend calculator/add.js\r\n     */\r\n\r\nYou only need to specify the main.js and the other dependencies will be added automatically. As well as their dependencies etc.\r\n\r\nIf you want to add a file that isn't referenced anywhere you need to add it manually.\r\n\r\n    files: {\r\n        'dist/mybuild.js': ['js/src/main.js', 'js/src/unReferencedButWanted.js']\r\n    },\r\n\r\nThe option onlyConcatRequiredFiles will only work if modules are declared and required with their actual filenames.\r\n\r\n## Contributing\r\nIn lieu of a formal styleguide, take care to maintain the existing coding style. Add unit tests for any new or changed functionality. Lint and test your code using [Grunt](http://gruntjs.com/).\r\n\r\n## Release History\r\n\r\n- 0.1.6 - @mokkabonna updated documenation and fixed path splitting in sample\r\n- 0.1.4 - @mokkabonna added ability to concat only files that are required by some module\r\n",
+  "readmeFilename": "README.md",
+  "_id": "grunt-concat-in-order@0.1.6",
+  "dist": {
+    "shasum": "26e0d2b6ac9fdcbd6611b05937df78d68c5a8b0e",
+    "tarball": "http://registry.npmjs.org/grunt-concat-in-order/-/grunt-concat-in-order-0.1.6.tgz"
+  },
+  "_from": "grunt-concat-in-order@*",
+  "_npmVersion": "1.2.21",
+  "_npmUser": {
+    "name": "miensol",
+    "email": "piotr.mionskowski@gmail.com"
+  },
+  "maintainers": [
+    {
+      "name": "miensol",
+      "email": "piotr.mionskowski@gmail.com"
+    }
+  ],
+  "directories": {},
+  "_shasum": "26e0d2b6ac9fdcbd6611b05937df78d68c5a8b0e",
+  "_resolved": "https://registry.npmjs.org/grunt-concat-in-order/-/grunt-concat-in-order-0.1.6.tgz"
+}
diff --git a/node_modules/grunt-concat-in-order/tasks/concat_in_order.js b/node_modules/grunt-concat-in-order/tasks/concat_in_order.js
new file mode 100644 (file)
index 0000000..b542a5c
--- /dev/null
@@ -0,0 +1,168 @@
+/*\r
+ * grunt-concat-in-order\r
+ * https://github.com/miensol/grunt-concat-in-order\r
+ *\r
+ * Copyright (c) 2013 Piotr Mionskowski\r
+ * Licensed under the MIT license.\r
+ */\r
+\r
+'use strict';\r
+\r
+var path = require('path'),\r
+    EOL = require('os').EOL;\r
+\r
+module.exports = function (grunt) {\r
+\r
+    var defaultOptions = {\r
+        getMatches: function (regex, string, index) {\r
+            var matches = [], match;\r
+            if(arguments.length < 3){\r
+                index = 1;\r
+            }\r
+            while (match = regex.exec(string)) {\r
+                matches.push(match[index]);\r
+            }\r
+            return matches;\r
+        },\r
+        extractRequired: function (filepath, filecontent) {\r
+            return this.getMatches(/require\(['"]([^'"]+)['"]/g, filecontent);\r
+        },\r
+        extractDeclared: function (filepath, filecontent) {\r
+            return this.getMatches(/declare\(['"]([^'"]+)['"]/g, filecontent);\r
+        }\r
+    }, getExistingFiles = function (files) {\r
+        return files.src.filter(function (filepath) {\r
+            // Warn on and remove invalid source files (if nonull was set).\r
+            if (!grunt.file.exists(filepath)) {\r
+                grunt.log.warn('Source file "' + filepath + '" not found.');\r
+                return false;\r
+            } else {\r
+                return true;\r
+            }\r
+        });\r
+    }, ensureNoCycleExists = function (depsTree) {\r
+        var graph = {};\r
+        depsTree.forEach(function (item) {\r
+            graph[item.file] = item;\r
+        });\r
+        depsTree.forEach(function (item) {\r
+            var visited = {};\r
+            ensureNoCycleExistsDfs(item, visited, graph);\r
+        });\r
+    }, ensureNoCycleExistsDfs = function (item, visited, graph) {\r
+        var graphKeys = Object.keys(graph),\r
+            graphArray = graphKeys.map(function (key) {\r
+                return graph[key];\r
+            }),\r
+            findItemsDeclaringIgnoreCase = function(declaration){\r
+                return graphArray.filter(function (anyItem) {\r
+                    return anyItem.declared.map(function(declared){\r
+                        return declared.toLowerCase();\r
+                    }).indexOf(declaration.toLowerCase()) !== -1;\r
+                });\r
+            },\r
+            findItemsDeclaring = function (declaration) {\r
+                return graphArray.filter(function (anyItem) {\r
+                    return anyItem.declared.indexOf(declaration) !== -1;\r
+                });\r
+            },\r
+            findItemDeclaring = function (declaration) {\r
+                return findItemsDeclaring(declaration)[0];\r
+            };\r
+        visited[item.file] = item;\r
+        item.required.forEach(function (requiredItem) {\r
+            var message,\r
+                declaringItem = findItemDeclaring(requiredItem);\r
+            if (!declaringItem) {\r
+                message = findItemsDeclaringIgnoreCase(requiredItem).map(function(matching){\r
+                    return matching.declared.join(', ') + ' in ' + matching.file;\r
+                }).join(', ');\r
+                if(message.length){\r
+                    message = "\nMaybe " + message;\r
+                }\r
+                grunt.fail.fatal("Dependency required in " + item.file + " does not exist: " + requiredItem + message);\r
+            }\r
+\r
+            if (!visited[declaringItem.file]) {\r
+                ensureNoCycleExistsDfs(declaringItem, visited, graph);\r
+            } else {\r
+                message = Object.keys(visited).join(', ');\r
+                grunt.fail.fatal("Cycle found! Current item is " + item.file + '\nVisited nodes are ' + message);\r
+            }\r
+        });\r
+        delete visited[item.file];\r
+    }, writeArray = function(array, prefix){\r
+        grunt.verbose.writeln(prefix +':');\r
+        if(array.length){\r
+            array.forEach(function(item){\r
+               grunt.verbose.writeln('\t' + item);\r
+            });\r
+        } else {\r
+            grunt.verbose.writeln('\tNo items.');\r
+        }\r
+    };\r
+\r
+    grunt.registerMultiTask('concat_in_order', 'Concatenates files in order', function () {\r
+\r
+        var options = this.options(defaultOptions);\r
+\r
+        this.files.forEach(function (fileSet) {\r
+            grunt.verbose.writeln('Extracting dependencies from "' + fileSet.src + '".');\r
+\r
+            var depsTree = [], ordered = [], current, countOfPreviouslySatisfiedDependencies,\r
+                previouslyDeclared = [],\r
+                previouslyDeclaredFilter = function (dep) {\r
+                    return previouslyDeclared.filter(function (previous) {\r
+                        return previous === dep;\r
+                    }).length > 0;\r
+                };\r
+\r
+            getExistingFiles(fileSet).map(function extractAndAddDependencies(filepath) {\r
+                //do not process this file again if already added\r
+                if (depsTree.some(function(item) {\r
+                    return item.file === path.normalize(filepath);\r
+                })) {\r
+                    return;\r
+                }\r
+\r
+                var content = grunt.file.read(filepath),\r
+                    required = options.extractRequired(filepath, content),\r
+                    declared = options.extractDeclared(filepath, content);\r
+\r
+                grunt.verbose.writeln('File %s', filepath);\r
+                writeArray(required, 'required');\r
+                writeArray(declared, 'declared');\r
+                if (options.onlyConcatRequiredFiles) {\r
+                    required.map(extractAndAddDependencies);\r
+                }\r
+\r
+                depsTree.push({\r
+                    content: content,\r
+                    file: path.normalize(filepath),\r
+                    required: required,\r
+                    declared: declared\r
+                });\r
+            });\r
+\r
+            ensureNoCycleExists(depsTree);\r
+\r
+            while (depsTree.length) {\r
+                current = depsTree.shift();\r
+                countOfPreviouslySatisfiedDependencies = current.required.filter(previouslyDeclaredFilter).length;\r
+                if (countOfPreviouslySatisfiedDependencies === current.required.length) {\r
+                    previouslyDeclared.push.apply(previouslyDeclared, current.declared);\r
+                    ordered.push(current);\r
+                } else {\r
+                    depsTree.push(current);\r
+                }\r
+            }\r
+\r
+            grunt.file.write(fileSet.dest, ordered.map(function (item) {\r
+                return item.content;\r
+            }).join(EOL));\r
+\r
+            grunt.log.writeln('File "' + fileSet.dest + '" created.');\r
+        });\r
+    });\r
+\r
+};\r
diff --git a/node_modules/grunt-concat-in-order/test/concat_in_order_test.js b/node_modules/grunt-concat-in-order/test/concat_in_order_test.js
new file mode 100644 (file)
index 0000000..088d1ae
--- /dev/null
@@ -0,0 +1,48 @@
+'use strict';\r
+\r
+var grunt = require('grunt');\r
+\r
+/*\r
+  ======== A Handy Little Nodeunit Reference ========\r
+  https://github.com/caolan/nodeunit\r
+\r
+  Test methods:\r
+    test.expect(numAssertions)\r
+    test.done()\r
+  Test assertions:\r
+    test.ok(value, [message])\r
+    test.equal(actual, expected, [message])\r
+    test.notEqual(actual, expected, [message])\r
+    test.deepEqual(actual, expected, [message])\r
+    test.notDeepEqual(actual, expected, [message])\r
+    test.strictEqual(actual, expected, [message])\r
+    test.notStrictEqual(actual, expected, [message])\r
+    test.throws(block, [error], [message])\r
+    test.doesNotThrow(block, [error], [message])\r
+    test.ifError(value)\r
+*/\r
+\r
+exports.concat_in_order = {\r
+  setUp: function(done) {\r
+    // setup here if necessary\r
+    done();\r
+  },\r
+  default_options: function(test) {\r
+    test.expect(1);\r
+\r
+    var actual = grunt.file.read('tmp/default_options.js');\r
+    var expected = grunt.file.read('test/expected/default_options.js');\r
+    test.equal(actual, expected, 'should concatenate files respecting file order');\r
+\r
+    test.done();\r
+  },\r
+  filebased_options : function(test) {\r
+    test.expect(1);\r
+\r
+    var actual = grunt.file.read('tmp/filebased_options.js');\r
+    var expected = grunt.file.read('test/expected/filebased_options.js');\r
+    test.equal(actual, expected, 'should include files automatically when filebased');\r
+\r
+    test.done();\r
+  }\r
+};\r
diff --git a/node_modules/grunt-concat-in-order/test/cycle/A.js b/node_modules/grunt-concat-in-order/test/cycle/A.js
new file mode 100644 (file)
index 0000000..6a29fc9
--- /dev/null
@@ -0,0 +1,2 @@
+declare('A');\r
+require('B');
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/cycle/B.js b/node_modules/grunt-concat-in-order/test/cycle/B.js
new file mode 100644 (file)
index 0000000..6bc7c39
--- /dev/null
@@ -0,0 +1,2 @@
+declare('B');\r
+require('C');
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/cycle/C.js b/node_modules/grunt-concat-in-order/test/cycle/C.js
new file mode 100644 (file)
index 0000000..acd01ea
--- /dev/null
@@ -0,0 +1,2 @@
+declare('C');\r
+require('A');
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/default/AUsingBaseA.js b/node_modules/grunt-concat-in-order/test/default/AUsingBaseA.js
new file mode 100644 (file)
index 0000000..1fa2898
--- /dev/null
@@ -0,0 +1,4 @@
+/*start AUsingBaseA*/\r
+framwork.require('module.BaseA');\r
+var second = function secondFunction(){};\r
+/*end AUsingBaseA*/
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/default/AUsingBaseBAndBaseA.js b/node_modules/grunt-concat-in-order/test/default/AUsingBaseBAndBaseA.js
new file mode 100644 (file)
index 0000000..45d41b5
--- /dev/null
@@ -0,0 +1,6 @@
+/*start AUsingBaseBAndBaseA*/\r
+framwork.require('module.BaseB');\r
+framwork.require('module.BaseA');\r
+framework.declare('module.UsingBaseBAndBaseA');\r
+var forth = function fourthFunction(){};\r
+/*end AUsingBaseBAnddBaseA*/
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/default/BaseA.js b/node_modules/grunt-concat-in-order/test/default/BaseA.js
new file mode 100644 (file)
index 0000000..72a7e85
--- /dev/null
@@ -0,0 +1,4 @@
+/*start BaseA*/\r
+framework.declare('module.BaseA');\r
+var first = function firstFunction(){};\r
+/*end BaseA*/
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/default/BaseBUsingBaseA.js b/node_modules/grunt-concat-in-order/test/default/BaseBUsingBaseA.js
new file mode 100644 (file)
index 0000000..9f58588
--- /dev/null
@@ -0,0 +1,6 @@
+/*start BaseBUsingBaseA*/\r
+framwork.require('module.BaseA');\r
+framework.declare('module.BaseBUsingBaseA');\r
+framework.declare('module.BaseB');\r
+var third = function thirdFunction(){};\r
+/*end  BaseBUsingBaseA*/
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/expected/custom_options b/node_modules/grunt-concat-in-order/test/expected/custom_options
new file mode 100644 (file)
index 0000000..e597128
--- /dev/null
@@ -0,0 +1 @@
+Testing: 1 2 3 !!!
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/expected/default_options.js b/node_modules/grunt-concat-in-order/test/expected/default_options.js
new file mode 100644 (file)
index 0000000..a667d5e
--- /dev/null
@@ -0,0 +1,20 @@
+/*start BaseA*/\r
+framework.declare('module.BaseA');\r
+var first = function firstFunction(){};\r
+/*end BaseA*/\r
+/*start BaseBUsingBaseA*/\r
+framwork.require('module.BaseA');\r
+framework.declare('module.BaseBUsingBaseA');\r
+framework.declare('module.BaseB');\r
+var third = function thirdFunction(){};\r
+/*end  BaseBUsingBaseA*/\r
+/*start AUsingBaseA*/\r
+framwork.require('module.BaseA');\r
+var second = function secondFunction(){};\r
+/*end AUsingBaseA*/\r
+/*start AUsingBaseBAndBaseA*/\r
+framwork.require('module.BaseB');\r
+framwork.require('module.BaseA');\r
+framework.declare('module.UsingBaseBAndBaseA');\r
+var forth = function fourthFunction(){};\r
+/*end AUsingBaseBAnddBaseA*/
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/expected/filebased_options.js b/node_modules/grunt-concat-in-order/test/expected/filebased_options.js
new file mode 100644 (file)
index 0000000..6b35550
--- /dev/null
@@ -0,0 +1,26 @@
+/*start BaseA*/\r
+var first = function firstFunction(){};\r
+/*end BaseA*/\r
+\r
+/*start AUsingBaseA*/\r
+/**\r
+ * @depend BaseA.js\r
+ */\r
+var second = function secondFunction(){};\r
+/*end AUsingBaseA*/\r
+\r
+/*start BaseBUsingBaseA*/\r
+/**\r
+ * @depend AUsingBaseA.js\r
+ */\r
+var third = function thirdFunction(){};\r
+/*end  BaseBUsingBaseA*/\r
+\r
+/*start AUsingBaseBAndBaseA*/\r
+/**\r
+ * @depend BaseBUsingBaseA.js\r
+ * @depend BaseA.js\r
+ *\r
+ */\r
+var forth = function fourthFunction(){};\r
+/*end AUsingBaseBAnddBaseA*/\r
diff --git a/node_modules/grunt-concat-in-order/test/filebased/AUsingBaseA.js b/node_modules/grunt-concat-in-order/test/filebased/AUsingBaseA.js
new file mode 100644 (file)
index 0000000..1928938
--- /dev/null
@@ -0,0 +1,6 @@
+/*start AUsingBaseA*/\r
+/**\r
+ * @depend BaseA.js\r
+ */\r
+var second = function secondFunction(){};\r
+/*end AUsingBaseA*/\r
diff --git a/node_modules/grunt-concat-in-order/test/filebased/AUsingBaseBAndBaseA.js b/node_modules/grunt-concat-in-order/test/filebased/AUsingBaseBAndBaseA.js
new file mode 100644 (file)
index 0000000..c82323b
--- /dev/null
@@ -0,0 +1,8 @@
+/*start AUsingBaseBAndBaseA*/\r
+/**\r
+ * @depend BaseBUsingBaseA.js\r
+ * @depend BaseA.js\r
+ *\r
+ */\r
+var forth = function fourthFunction(){};\r
+/*end AUsingBaseBAnddBaseA*/\r
diff --git a/node_modules/grunt-concat-in-order/test/filebased/BaseA.js b/node_modules/grunt-concat-in-order/test/filebased/BaseA.js
new file mode 100644 (file)
index 0000000..12e6fa6
--- /dev/null
@@ -0,0 +1,3 @@
+/*start BaseA*/\r
+var first = function firstFunction(){};\r
+/*end BaseA*/\r
diff --git a/node_modules/grunt-concat-in-order/test/filebased/BaseBUsingBaseA.js b/node_modules/grunt-concat-in-order/test/filebased/BaseBUsingBaseA.js
new file mode 100644 (file)
index 0000000..a30bd2a
--- /dev/null
@@ -0,0 +1,6 @@
+/*start BaseBUsingBaseA*/\r
+/**\r
+ * @depend AUsingBaseA.js\r
+ */\r
+var third = function thirdFunction(){};\r
+/*end  BaseBUsingBaseA*/\r
diff --git a/node_modules/grunt-concat-in-order/test/missing/lowercase.js b/node_modules/grunt-concat-in-order/test/missing/lowercase.js
new file mode 100644 (file)
index 0000000..6a05c6d
--- /dev/null
@@ -0,0 +1 @@
+declare('lowercase');
\ No newline at end of file
diff --git a/node_modules/grunt-concat-in-order/test/missing/requiresUpperCase.js b/node_modules/grunt-concat-in-order/test/missing/requiresUpperCase.js
new file mode 100644 (file)
index 0000000..0cb0d9d
--- /dev/null
@@ -0,0 +1 @@
+require('LOWERCASE');
\ No newline at end of file
index d8d5bc0fe0d2639ce3ec0e9cf5bc23a280bad31c..7437ab11ff1cf728324a40d574c99efde4fce9ed 100644 (file)
@@ -11,7 +11,9 @@
     "grunt-contrib-jshint": "^0.11.0",
     "grunt-contrib-watch": "^0.6.1"
   },
-  "devDependencies": {},
+  "devDependencies": {
+    "grunt-concat-in-order": "^0.1.6"
+  },
   "scripts": {
     "test": "echo \"Error: no test specified\" && exit 1"
   },
diff --git a/src/flowy.js b/src/flowy.js
new file mode 100644 (file)
index 0000000..8aacf11
--- /dev/null
@@ -0,0 +1,7 @@
+/**
+ * @depend views/app.js
+ */
+
+$(function() {
+    var flowyView = new AppView();
+});
diff --git a/src/models/flowyDoc.js b/src/models/flowyDoc.js
new file mode 100644 (file)
index 0000000..893f5a9
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * @depend ../models/todo.js
+ */
+
+flowyDocDefaults = { rootId: null };
+var FlowyDocModel = Backbone.Collection.extend({
+    initialize: function(options) {
+        options = _.defaults({}, options, flowyDocDefaults);
+        this.id = options.id;
+        this.default = options.default;
+        this.rootId = options.rootId;
+    },
+    model: TodoModel,
+    localStorage: new Backbone.LocalStorage("todos-backbone"),
+    fetch: function(options) { // TODO: Move to base class as default behavior for localStorage
+        // If during the initial fetch, the collection is not present, instead populate it using this.default
+        options = options ? _.clone(options) : {};
+        var error = options.error;
+        var collection = this;
+        options.error = function(_, resp, __) {
+            if (resp ==="Record Not Found" && collection.default) {
+                var method = options.reset ? 'reset' : 'set';
+                collection[method](collection.default);
+            }
+            if (error) error(collection, resp, options);
+        };
+        Backbone.Collection.prototype.fetch.call(this, options);
+    },
+    nextOrder: function() {
+        if (!this.length) return 1;
+        return this.last().get('order') + 1;
+    },
+    comparator: 'order',
+});
diff --git a/src/models/todo.js b/src/models/todo.js
new file mode 100644 (file)
index 0000000..1b5f230
--- /dev/null
@@ -0,0 +1,34 @@
+var TodoModel = Backbone.Model.extend({
+    defaults: function() {
+        return {
+            completed: false,
+            collapsed: false,
+            text: "Should never be visible",
+            bullets: [],
+            parent: null,
+        };
+    },
+    toggleComplete: function() {
+        this.save({completed: !this.get("completed")});
+    },
+    toggleCollapsed: function() {
+        this.save({collapsed: !this.get("collapsed")});
+    },
+    setText: function(text) {
+        this.save({text: text});
+    },
+    getChildren: function(collection) {
+        return _.map(this.get("bullets"), function(id) {
+            return collection.get(id);
+        }, this);
+    },
+    getParent: function(collection) {
+        return collection.get(this.get("parent"));
+    },
+    isTopLevel: function(collection) {
+        return this.get("parent") === collection.rootId;
+    },
+    isParentLoaded: function(collection, collectionView) {
+        return this.isTopLevel(collection) ? true : (this.getParent(collection) && collectionView.getView(this.getParent(collection)));
+    },
+});
diff --git a/src/views/app.js b/src/views/app.js
new file mode 100644 (file)
index 0000000..920b454
--- /dev/null
@@ -0,0 +1,103 @@
+/**
+ * @depend ../views/todo.js
+ * @depend ../models/flowyDoc.js
+ * @depend ../models/todo.js
+ */
+
+var todos = new FlowyDocModel({
+    id: "master",
+    default: [
+        new TodoModel({
+            parent: null,
+            id: 1,
+            text: "Daily todos",
+            bullets: [2,3,4],
+        }),
+        new TodoModel({
+            parent: 1,
+            id: 2,
+            text: "Shave",
+            completed: true
+        }),
+        new TodoModel({
+            parent: 1,
+            id: 3,
+            text: "Check t-mail",
+            completed: true
+        }),
+        new TodoModel({
+            parent: 1,
+            id: 4,
+            text: "Eat green eggs and ham",
+        }),
+        new TodoModel({
+            parent: null,
+            id: 5,
+            text: "To do this year",
+            collapsed: true,
+            bullets: [6],
+        }),
+        new TodoModel({
+            parent: 5,
+            id: 6,
+            text: "Save the world",
+            bullets: [7],
+        }),
+        new TodoModel({
+            parent: 6,
+            id: 7,
+            text: "Save California",
+        }),
+    ],
+});
+
+var appDefaults = { list: todos };
+var AppView = Backbone.View.extend({
+    el: $("#todo-app"),
+    initialize: function(options) {
+        options = _.defaults({}, options, appDefaults);
+        this.listenTo(todos, 'add', this.addOne);
+        this.listenTo(todos, 'reset', this.addAll);
+        this.list = options.list;
+        this.views = {}; // A list of views for each element in the collection
+        this.list.fetch();
+    },
+    render: function() {
+        return this;
+    },
+    addOne: function(todo) {
+        this.renderTodo(todo);
+    },
+    renderTodo: function(todo) {
+        if (this.getView(todo)) {
+            console.log("View rendered more than once for todo #" + todo.id);
+            return;
+        }
+        var view = new TodoView({model: todo});
+        this.setView(todo, view);
+        if (todo.isTopLevel(this.list)) {
+            this.$("#todo-list").append(view.render().el);
+        } else if (todo.isParentLoaded(this.list, this)) {
+            var parent = todo.getParent(this.list);
+            var parentView = this.getView(parent);
+            parentView.addChild(view.render().el);
+        }
+
+        // Find unrendered descendents and render them, too.
+        _.each(todo.getChildren(this.list), function(child) {
+            if (child && !this.getView(child)) {
+               this.renderTodo(child); 
+            }
+        }, this);
+        return this;
+    },
+    setView: function(model, view) {
+        this.views[model.id] = view;
+    },
+    getView: function(model) {
+        return this.views[model.id];
+    },
+    addAll: function() {
+        this.list.each(this.addOne, this);
+    },
+});
diff --git a/src/views/todo.js b/src/views/todo.js
new file mode 100644 (file)
index 0000000..abba964
--- /dev/null
@@ -0,0 +1,31 @@
+var TodoView = Backbone.View.extend({
+  tagName: 'div',
+  className: 'todo',
+  events: {
+    "click > .text": "toggleComplete",
+  },
+  initialize: function() {
+    this.listenTo(this.model, "change", this.render);
+    this.listenTo(this.model, 'destroy', this.remove);
+  },
+  toggleComplete: function(e) {
+    this.model.toggleComplete();
+    e.stopPropagation();
+  },
+  template: "<div class=\"text\">{{text}}</div><div class=\"bullets\"></div>",
+  addChild: function(el, position) {
+     if(typeof position === 'undefined') {
+        console.log("TodoView:addChild called without a position");
+     }
+     this.$el.find("> .bullets").append(el);
+     return this;
+  },
+  render: function() {
+    var oldChildren = this.$el.find("> .bullets > *").detach(); // detach keeps handlers
+    this.$el.html(Mustache.to_html(this.template, this.model.toJSON())); // Should hopefully be model.attributes
+    this.$el.toggleClass('completed', this.model.get('completed'));
+    this.$el.toggleClass('collapsed', this.model.get('collapsed'));
+    this.$el.find("> .bullets").append(oldChildren);
+    return this;
+  },
+});