<commit_before>module.exports = function(grunt) {
    grunt.util.linefeed = '\n';

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readYAML('../config/config.yml'),

        watch: {
            options: {
                spawn: false,
                livereload: true
            },
            scripts: {
                files: [
                    'sass/*.scss',
                    'sass/nav/*.scss',
                    'sass/modules/*.scss'
                ],
                tasks: [
                    'sass'
                ]
            },
            js: {
                files: [
                    'lib/bolt/*.js'
                ],
                tasks: [
                    'uglify:bolt'
                ]
            }
        },

        sass: {
            dist: {
                options: {
                    outputStyle: 'compressed',
                    includePaths: [
                        'node_modules/bootstrap-sass/assets/stylesheets/',
                        'node_modules/font-awesome/scss/'
                    ],
                    lineNumbers: false,
                    unixNewlines: true,
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED CSS files!\n" +
                            " * Do not edit these files, because all changes will be lost.\n" +
                            " * You can edit ../scss/app.scss & ../scss/app-old-ie.scss, and run 'grunt' to generate this file.\n" +
                            " */\n",
                    precision: 5
                },
                files: {
                    'css/bolt-old-ie.css': 'sass/app-old-ie.scss',
                    'css/bolt.css': 'sass/app.scss'
                }
            }

        },

        copy: {
            main: {
                // Includes files within path
                files: [{
                    expand: true,
                    flatten: true,
                    src: [
                        'node_modules/font-awesome/fonts/*'
                    ],
                    filter: 'isFile',
                    dest: 'fonts/'
                }]
            },
            momentlocales: {
                files: [{
                    expand: true,
                    flatten: true,
                    src: [
                        'node_modules/moment/locale/cs.js',
                        'node_modules/moment/locale/de.js',
                        'node_modules/moment/locale/el.js',
                        'node_modules/moment/locale/en-gb.js',
                        'node_modules/moment/locale/es.js',
                        'node_modules/moment/locale/fi.js',
                        'node_modules/moment/locale/fr.js',
                        'node_modules/moment/locale/hu.js',
                        'node_modules/moment/locale/id.js',
                        'node_modules/moment/locale/it.js',
                        'node_modules/moment/locale/ja.js',
                        'node_modules/moment/locale/nb.js',
                        'node_modules/moment/locale/nl.js',
                        'node_modules/moment/locale/nb.js',
                        'node_modules/moment/locale/nl.js',
                        'node_modules/moment/locale/pt.js',
                        'node_modules/moment/locale/ru.js',
                        'node_modules/moment/locale/zh-cn.js',
                        'node_modules/moment/min/moment.min.js',
                    ],
                    filter: 'isFile',
                    dest: 'lib/moment/'
                }]
            }
        },

        concat: {
            lib: {
                options: {
                    separator: '\n\n'
                },
                nonull: true,
                src: [
                    'lib/jquery-1.11.2/jquery.min.js',                              //  96 kb
                    'lib/jquery-ui-1.10.3/jquery-ui.custom.min.js',                 //  96 kb
                    'lib/bootstrap-file-input/bootstrap-file-input.min.js',         //   1 kb
                    'lib/jquery-tagcloud/jquery-tagcloud.min.js',                   //   1 kb
                    'lib/jquery-hotkeys/jquery-hotkeys.min.js',                     //   2 kb
                    'lib/jquery-watchchanges/jquery-watchchanges.min.js',           //   1 kb
                    'lib/jquery-cookie-1.4.0/jquery-cookie.min.js',                 //   1 kb
                    'lib/jquery-formatdatetime-1.1.4/jquery-formatdatetime.min.js', //   3 kb
                    'lib/jquery-fileupload-5.26/jquery-iframe-transport.min.js',    //   2 kb
                    'lib/jquery-fileupload-5.26/jquery-fileupload.min.js',          //  15 kb
                    'lib/underscore-1.7.0/underscore-min.js',                       //  16 kb
                    'lib/backbone/backbone-min.js',                                 //  20 kb
                    'lib/bootstrap-sass.generated/bootstrap.min.js',                //   2 kb
                    'lib/magnific-popup-0.9.9/magnific-popup.min.js',               //  21 kb
                    'lib/select2-3.5.1/select2.min.js',                             //  66 kb
                    'lib/moment/moment.min.js',                                     //  35 kb
                    'lib/bootbox-4.3.0/bootbox.min.js',                             //   9 kb
                    'lib/modernizr-2.8.3/modernizr.custom.min.js'                   //   5 kb
                ],
                dest: 'js/lib.min.js'
            }
        },

        cssmin: {
            lib: {
                options: {
                    compatibility: 'ie8',
                    relativeTo: './css/',
                    target: './css/'
                },
                files: {
                    'css/lib.css': [
                        'lib/jquery-ui-1.10.3/jquery-ui.custom.min.css',      // 20 kb
                        'lib/magnific-popup-0.9.9/magnific-popup.css',        //  9 kb
                        'lib/select2-3.5.1/select2.css',                      // 19 kb
                        'lib/jquery-fileupload-5.26/jquery-fileupload-ui.css' //  2 kb
                    ]
                }
            }
        },

        uglify: {
            lib: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    ext: '.min.js',
                    src: [
                        'lib/bootstrap-file-input/bootstrap-file-input.js',
                        'lib/jquery-cookie-1.4.0/jquery-cookie.js',
                        'lib/jquery-fileupload-5.26/jquery-fileupload.js',
                        'lib/jquery-fileupload-5.26/jquery-iframe-transport.js',
                        'lib/jquery-formatdatetime-1.1.4/jquery-formatdatetime.js',
                        'lib/jquery-hotkeys/jquery-hotkeys.js',
                        'lib/jquery-tagcloud/jquery-tagcloud.js',
                        'lib/jquery-watchchanges/jquery-watchchanges.js'
                    ]
                }]
            },
            locale_datepicker: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    ext: '.min.js',
                    cwd: 'lib/datepicker',
                    src: '*.js',
                    dest: 'js/locale/datepicker',
                    rename: function(destBase, destPath) {
                        return destBase + '/' + destPath.replace('datepicker-', '').replace('-', '_');
                    }
                }]
            },
            locale_moment: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    ext: '.min.js',
                    cwd: 'node_modules/moment/locale',
                    src: '*.js',
                    dest: 'js/locale/moment',
                    rename: function(destBase, destPath) {
                        return destBase + '/' + destPath.replace(/([a-z]+)-([a-z]+)/, function(_, a, b) {
                            return a + '_' + b.toUpperCase();
                        });
                    }
                }]
            },
            bootstrap: {
                files: {
                    'lib/bootstrap-sass.generated/bootstrap.min.js': [
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/alert.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/button.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tab.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/transition.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/modal.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/popover.js'
                    ]
                }
            },
            bolt: {
                options: {
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED JS files!\n" +
                            " * You can edit files in <js/src/*.js> and run 'grunt' to generate this file.\n" +
                            " */",
                    sourceMap: true
                },
                files: {
                    'js/bolt.min.js': [
                        'lib/bolt/console.js',
                        'lib/bolt/fnc-helpers.js',
                        'lib/bolt/activity.js',
                        'lib/bolt/bind-fileupload.js',
                        'lib/bolt/make-uri-slug.js',
                        'lib/bolt/video-embed.js',
                        'lib/bolt/geolocation.js',
                        'lib/bolt/upload-files.js',
                        'lib/bolt/obj-sidebar.js',
                        'lib/bolt/obj-navpopups.js',
                        'lib/bolt/obj-moments.js',
                        'lib/bolt/obj-files.js',
                        'lib/bolt/obj-stack.js',
                        'lib/bolt/obj-folders.js',
                        'lib/bolt/obj-datetime.js',
                        'lib/bolt/extend.js',
                        'lib/bolt/init.js',
                        'lib/bolt/start.js'
                    ]
                }
            }
        },

        jshint: {
            options: {
                browser: true,          // Defines globals exposed by modern browsers
                curly: true,            // Always put curly braces around blocks
                devel: true,            // Defines globals that are usually used for logging/debugging
                immed: true,            // Prohibits the use of immediate function invocations without parentheses
                indent: 4,              // Tab width
                latedef: true,          // Prohibits the use of a variable before it was defined
                maxlen: 120,            // Maximum length of a line
                noarg: true,            // Prohibits the use of arguments.caller and arguments.callee
                nonbsp: true,           // Warns about "non-breaking whitespace" characters
                singleGroups: true,     // Prohibits the use of the grouping operator for single-expression statements
                undef: true,            // Prohibits the use of undeclared variables
                globals: {
                    // Bolt
                    bolt: true,                 // src/console.js
                    FilelistHolder: true,       // src/upload-files.js
                    Files: true,                // src/obj-files.js
                    Folders: true,              // src/obj-folders.js
                    init: true,                 // src/init.js
                    Moments: true,              // src/obj-moments.js
                    Navpopups: true,            // src/obj-navpopups.js
                    Sidebar: true,              // src/obj-sidebar.js
                    Stack: true,                // src/obj-stack.js
                    site: true,                 // src/extend.js/extend.twig
                    baseurl: true,              // src/extend.js/extend.twig
                    rootpath: true,             // src/extend.js/extend.twig
                    // Bolt global functions
                    bindFileUpload: true,       // src/bindfileuploads.js
                    bindGeolocation: true,      // src/geolocation.js
                    bindVideoEmbed: true,       // src/video-embed.js
                    getSelectedItems: true,     // src/fnc-helpers.js
                    makeUri: true,              // src/make-uri-slug.js
                    makeUriAjax: true,          // src/make-uri-slug.js
                    stopMakeUri: true,          // src/make-uri-slug.js
                    updateLatestActivity: true, // src/activity.js
                    validateContent: true,      // src/fnc-helpers.js
                    // Vendor
                    $: true,                    // jQuery
                    _: true,                    // underscore.js
                    Backbone: true,             // backbone.min.js
                    bootbox: true,              // bootbox.min.js
                    CKEDITOR: true,             // ckeditor.js
                    CodeMirror: true,           // ckeditor.js
                    google: true,               // Google
                    jQuery: true,               // jQuery
                    moment: true,               // moment.min.js
                    UAParser: true              // ua-parser.min.js
                }
            },
            src: ['lib/bolt/*.js']
        }

    });

    require('load-grunt-tasks')(grunt);

    grunt.registerTask('default', ['sass', 'jshint', 'uglify', 'cssmin', 'concat', 'watch']);

};
<commit_msg>JS style fixes<commit_after>module.exports = function(grunt) {
    grunt.util.linefeed = '\n';

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readYAML('../config/config.yml'),

        watch: {
            options: {
                spawn: false,
                livereload: true
            },
            scripts: {
                files: [
                    'sass/*.scss',
                    'sass/nav/*.scss',
                    'sass/modules/*.scss'
                ],
                tasks: [
                    'sass'
                ]
            },
            js: {
                files: [
                    'lib/bolt/*.js'
                ],
                tasks: [
                    'uglify:bolt'
                ]
            }
        },

        sass: {
            dist: {
                options: {
                    outputStyle: 'compressed',
                    includePaths: [
                        'node_modules/bootstrap-sass/assets/stylesheets/',
                        'node_modules/font-awesome/scss/'
                    ],
                    lineNumbers: false,
                    unixNewlines: true,
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED CSS files!\n" +
                            " * Do not edit these files, because all changes will be lost.\n" +
                            " * You can edit ../scss/app.scss & ../scss/app-old-ie.scss, and run 'grunt' to generate this file.\n" +
                            " */\n",
                    precision: 5
                },
                files: {
                    'css/bolt-old-ie.css': 'sass/app-old-ie.scss',
                    'css/bolt.css': 'sass/app.scss'
                }
            }

        },

        copy: {
            main: {
                // Includes files within path
                files: [{
                    expand: true,
                    flatten: true,
                    src: [
                        'node_modules/font-awesome/fonts/*'
                    ],
                    filter: 'isFile',
                    dest: 'fonts/'
                }]
            },
            momentlocales: {
                files: [{
                    expand: true,
                    flatten: true,
                    src: [
                        'node_modules/moment/locale/cs.js',
                        'node_modules/moment/locale/de.js',
                        'node_modules/moment/locale/el.js',
                        'node_modules/moment/locale/en-gb.js',
                        'node_modules/moment/locale/es.js',
                        'node_modules/moment/locale/fi.js',
                        'node_modules/moment/locale/fr.js',
                        'node_modules/moment/locale/hu.js',
                        'node_modules/moment/locale/id.js',
                        'node_modules/moment/locale/it.js',
                        'node_modules/moment/locale/ja.js',
                        'node_modules/moment/locale/nb.js',
                        'node_modules/moment/locale/nl.js',
                        'node_modules/moment/locale/nb.js',
                        'node_modules/moment/locale/nl.js',
                        'node_modules/moment/locale/pt.js',
                        'node_modules/moment/locale/ru.js',
                        'node_modules/moment/locale/zh-cn.js',
                        'node_modules/moment/min/moment.min.js',
                    ],
                    filter: 'isFile',
                    dest: 'lib/moment/'
                }]
            }
        },

        concat: {
            lib: {
                options: {
                    separator: '\n\n'
                },
                nonull: true,
                src: [
                    'lib/jquery-1.11.2/jquery.min.js',                              //  96 kb
                    'lib/jquery-ui-1.10.3/jquery-ui.custom.min.js',                 //  96 kb
                    'lib/bootstrap-file-input/bootstrap-file-input.min.js',         //   1 kb
                    'lib/jquery-tagcloud/jquery-tagcloud.min.js',                   //   1 kb
                    'lib/jquery-hotkeys/jquery-hotkeys.min.js',                     //   2 kb
                    'lib/jquery-watchchanges/jquery-watchchanges.min.js',           //   1 kb
                    'lib/jquery-cookie-1.4.0/jquery-cookie.min.js',                 //   1 kb
                    'lib/jquery-formatdatetime-1.1.4/jquery-formatdatetime.min.js', //   3 kb
                    'lib/jquery-fileupload-5.26/jquery-iframe-transport.min.js',    //   2 kb
                    'lib/jquery-fileupload-5.26/jquery-fileupload.min.js',          //  15 kb
                    'lib/underscore-1.7.0/underscore-min.js',                       //  16 kb
                    'lib/backbone/backbone-min.js',                                 //  20 kb
                    'lib/bootstrap-sass.generated/bootstrap.min.js',                //   2 kb
                    'lib/magnific-popup-0.9.9/magnific-popup.min.js',               //  21 kb
                    'lib/select2-3.5.1/select2.min.js',                             //  66 kb
                    'lib/moment/moment.min.js',                                     //  35 kb
                    'lib/bootbox-4.3.0/bootbox.min.js',                             //   9 kb
                    'lib/modernizr-2.8.3/modernizr.custom.min.js'                   //   5 kb
                ],
                dest: 'js/lib.min.js'
            }
        },

        cssmin: {
            lib: {
                options: {
                    compatibility: 'ie8',
                    relativeTo: './css/',
                    target: './css/'
                },
                files: {
                    'css/lib.css': [
                        'lib/jquery-ui-1.10.3/jquery-ui.custom.min.css',      // 20 kb
                        'lib/magnific-popup-0.9.9/magnific-popup.css',        //  9 kb
                        'lib/select2-3.5.1/select2.css',                      // 19 kb
                        'lib/jquery-fileupload-5.26/jquery-fileupload-ui.css' //  2 kb
                    ]
                }
            }
        },

        uglify: {
            lib: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    ext: '.min.js',
                    src: [
                        'lib/bootstrap-file-input/bootstrap-file-input.js',
                        'lib/jquery-cookie-1.4.0/jquery-cookie.js',
                        'lib/jquery-fileupload-5.26/jquery-fileupload.js',
                        'lib/jquery-fileupload-5.26/jquery-iframe-transport.js',
                        'lib/jquery-formatdatetime-1.1.4/jquery-formatdatetime.js',
                        'lib/jquery-hotkeys/jquery-hotkeys.js',
                        'lib/jquery-tagcloud/jquery-tagcloud.js',
                        'lib/jquery-watchchanges/jquery-watchchanges.js'
                    ]
                }]
            },
            locale_datepicker: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    ext: '.min.js',
                    cwd: 'lib/datepicker',
                    src: '*.js',
                    dest: 'js/locale/datepicker',
                    rename: function (destBase, destPath) {
                        return destBase + '/' + destPath.replace('datepicker-', '').replace('-', '_');
                    }
                }]
            },
            locale_moment: {
                options: {
                    preserveComments: 'some'
                },
                files: [{
                    expand: true,
                    ext: '.min.js',
                    cwd: 'node_modules/moment/locale',
                    src: '*.js',
                    dest: 'js/locale/moment',
                    rename: function (destBase, destPath) {
                        return destBase + '/' + destPath.replace(/([a-z]+)-([a-z]+)/, function (_, a, b) {
                            return a + '_' + b.toUpperCase();
                        });
                    }
                }]
            },
            bootstrap: {
                files: {
                    'lib/bootstrap-sass.generated/bootstrap.min.js': [
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/alert.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/button.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tab.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/transition.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/modal.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/popover.js'
                    ]
                }
            },
            bolt: {
                options: {
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED JS files!\n" +
                            " * You can edit files in <js/src/*.js> and run 'grunt' to generate this file.\n" +
                            " */",
                    sourceMap: true
                },
                files: {
                    'js/bolt.min.js': [
                        'lib/bolt/console.js',
                        'lib/bolt/fnc-helpers.js',
                        'lib/bolt/activity.js',
                        'lib/bolt/bind-fileupload.js',
                        'lib/bolt/make-uri-slug.js',
                        'lib/bolt/video-embed.js',
                        'lib/bolt/geolocation.js',
                        'lib/bolt/upload-files.js',
                        'lib/bolt/obj-sidebar.js',
                        'lib/bolt/obj-navpopups.js',
                        'lib/bolt/obj-moments.js',
                        'lib/bolt/obj-files.js',
                        'lib/bolt/obj-stack.js',
                        'lib/bolt/obj-folders.js',
                        'lib/bolt/obj-datetime.js',
                        'lib/bolt/extend.js',
                        'lib/bolt/init.js',
                        'lib/bolt/start.js'
                    ]
                }
            }
        },

        jshint: {
            options: {
                browser: true,          // Defines globals exposed by modern browsers
                curly: true,            // Always put curly braces around blocks
                devel: true,            // Defines globals that are usually used for logging/debugging
                immed: true,            // Prohibits the use of immediate function invocations without parentheses
                indent: 4,              // Tab width
                latedef: true,          // Prohibits the use of a variable before it was defined
                maxlen: 120,            // Maximum length of a line
                noarg: true,            // Prohibits the use of arguments.caller and arguments.callee
                nonbsp: true,           // Warns about "non-breaking whitespace" characters
                singleGroups: true,     // Prohibits the use of the grouping operator for single-expression statements
                undef: true,            // Prohibits the use of undeclared variables
                globals: {
                    // Bolt
                    bolt: true,                 // src/console.js
                    FilelistHolder: true,       // src/upload-files.js
                    Files: true,                // src/obj-files.js
                    Folders: true,              // src/obj-folders.js
                    init: true,                 // src/init.js
                    Moments: true,              // src/obj-moments.js
                    Navpopups: true,            // src/obj-navpopups.js
                    Sidebar: true,              // src/obj-sidebar.js
                    Stack: true,                // src/obj-stack.js
                    site: true,                 // src/extend.js/extend.twig
                    baseurl: true,              // src/extend.js/extend.twig
                    rootpath: true,             // src/extend.js/extend.twig
                    // Bolt global functions
                    bindFileUpload: true,       // src/bindfileuploads.js
                    bindGeolocation: true,      // src/geolocation.js
                    bindVideoEmbed: true,       // src/video-embed.js
                    getSelectedItems: true,     // src/fnc-helpers.js
                    makeUri: true,              // src/make-uri-slug.js
                    makeUriAjax: true,          // src/make-uri-slug.js
                    stopMakeUri: true,          // src/make-uri-slug.js
                    updateLatestActivity: true, // src/activity.js
                    validateContent: true,      // src/fnc-helpers.js
                    // Vendor
                    $: true,                    // jQuery
                    _: true,                    // underscore.js
                    Backbone: true,             // backbone.min.js
                    bootbox: true,              // bootbox.min.js
                    CKEDITOR: true,             // ckeditor.js
                    CodeMirror: true,           // ckeditor.js
                    google: true,               // Google
                    jQuery: true,               // jQuery
                    moment: true,               // moment.min.js
                    UAParser: true              // ua-parser.min.js
                }
            },
            src: ['lib/bolt/*.js']
        }

    });

    require('load-grunt-tasks')(grunt);

    grunt.registerTask('default', ['sass', 'jshint', 'uglify', 'cssmin', 'concat', 'watch']);

};
<|endoftext|>


<commit_before>module.exports = function(grunt) {
    grunt.util.linefeed = '\n';

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readYAML('../config/config.yml'),

        watch: {
            options: {
                spawn: false,
                livereload: true
            },
            scripts: {
                files: [
                    'sass/*.scss',
                    'sass/nav/*.scss',
                    'sass/modules/*.scss'
                ],
                tasks: [
                    'sass'
                ]
            },
            js: {
                files: [
                    'js/*.js',
                    'js/src/*.js'
                ],
                tasks: [
                    'concat:js'
                ]
            }
        },

        sass: {
            dist: {
                options: {
                    outputStyle: 'compressed',
                    includePaths: [
                        'node_modules/bootstrap-sass/assets/stylesheets/',
                        'node_modules/font-awesome/scss/'
                    ],
                    lineNumbers: false,
                    unixNewlines: true,
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED CSS files!\n" +
                            " * Do not edit these files, because all changes will be lost.\n" +
                            " * You can edit ../scss/app.scss & ../scss/app-old-ie.scss, and run 'grunt' to generate this file.\n" +
                            " */\n",
                    precision: 5
                },
                files: {
                    'css/bolt-old-ie.css': 'sass/app-old-ie.scss',
                    'css/bolt.css': 'sass/app.scss'
                }
            }

        },

        copy: {
            main: {
                // Includes files within path
                files: [{
                    expand: true,
                    flatten: true,
                    src: [
                        'node_modules/font-awesome/fonts/*'
                    ],
                    filter: 'isFile',
                    dest: 'fonts/'
                }]
            }
        },

        concat: {
            js: {
                options: {
                    separator: '\n/**********************************************************************************************************************/\n\n',
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED JS files!\n" +
                            " * Do not edit these files, because all changes will be lost.\n" +
                            " * You can edit files in <js/src/*.js> and run 'grunt' to generate this file.\n" +
                            " */\n\n",
                    sourceMap: true,
                    sourceMapStyle: 'link'
                },
                nonull: true,
                src: [
                    'js/src/console.js',
                    'js/src/fnc-helpers.js',
                    'js/src/activity.js',
                    'js/src/bind-fileupload.js',
                    'js/src/make-uri-slug.js',
                    'js/src/video-embed.js',
                    'js/src/geolocation.js',
                    'js/src/upload-files.js',
                    'js/src/obj-sidebar.js',
                    'js/src/obj-navpopups.js',
                    'js/src/obj-moments.js',
                    'js/src/obj-files.js',
                    'js/src/obj-stack.js',
                    'js/src/obj-folders.js',
                    'js/src/obj-datetime.js',
                    'js/src/init.js',
                    'js/src/start.js'
                ],
                dest: 'js/bolt.js'
            }
        },

        uglify: {
            bootstrap: {
                files: {
                    'js/bootstrap.js': [
                        'node_modules/bootstrap-sass/vendor/assets/javascripts/bootstrap/alert.js',
                        'node_modules/bootstrap-sass/vendor/assets/javascripts/bootstrap/button.js',
                        'node_modules/bootstrap-sass/vendor/assets/javascripts/bootstrap/dropdown.js',
                        'node_modules/bootstrap-sass/vendor/assets/javascripts/bootstrap/tab.js',
                        'node_modules/bootstrap-sass/vendor/assets/javascripts/bootstrap/transition.js',
                        'node_modules/bootstrap-sass/vendor/assets/javascripts/bootstrap/modal.js',
                        'node_modules/bootstrap-sass/vendor/assets/javascripts/bootstrap/tooltip.js',
                        'node_modules/bootstrap-sass/vendor/assets/javascripts/bootstrap/popover.js'
                    ]
                }
            },
            bolt: {
                files: {
                    'js/bolt.min.js': [
                        'js/src/console.js',
                        'js/src/fnc-helpers.js',
                        'js/src/activity.js',
                        'js/src/bind-fileupload.js',
                        'js/src/make-uri-slug.js',
                        'js/src/video-embed.js',
                        'js/src/geolocation.js',
                        'js/src/upload-files.js',
                        'js/src/obj-sidebar.js',
                        'js/src/obj-navpopups.js',
                        'js/src/obj-moments.js',
                        'js/src/obj-files.js',
                        'js/src/obj-stack.js',
                        'js/src/obj-folders.js',
                        'js/src/obj-datetime.js',
                        'js/src/init.js',
                        'js/src/start.js'
                    ]
                }
            }
        },

        jshint: {
            options: {
                browser: true,          // Defines globals exposed by modern browsers
                curly: true,            // Always put curly braces around blocks
                devel: true,            // Defines globals that are usually used for logging/debugging
                immed: true,            // Prohibits the use of immediate function invocations without parentheses
                indent: 4,              // Tab width
                latedef: true,          // Prohibits the use of a variable before it was defined
                maxlen: 120,            // Maximum length of a line
                noarg: true,            // Prohibits the use of arguments.caller and arguments.callee
                nonbsp: true,           // Warns about "non-breaking whitespace" characters
                singleGroups: true,     // Prohibits the use of the grouping operator for single-expression statements
                undef: true,            // Prohibits the use of undeclared variables
                globals: {
                    // Bolt
                    bolt: true,                 // src/console.js
                    FilelistHolder: true,       // src/upload-files.js
                    Files: true,                // src/obj-files.js
                    Folders: true,              // src/obj-folders.js
                    init: true,                 // src/init.js
                    Moments: true,              // src/obj-moments.js
                    Navpopups: true,            // src/obj-navpopups.js
                    Sidebar: true,              // src/obj-sidebar.js
                    Stack: true,                // src/obj-stack.js
                    // Bolt global functions
                    bindFileUpload: true,       // src/bindfileuploads.js
                    bindGeolocation: true,      // src/geolocation.js
                    bindVideoEmbed: true,       // src/video-embed.js
                    getSelectedItems: true,     // src/fnc-helpers.js
                    makeUri: true,              // src/make-uri-slug.js
                    makeUriAjax: true,          // src/make-uri-slug.js
                    stopMakeUri: true,          // src/make-uri-slug.js
                    updateLatestActivity: true, // src/activity.js
                    validateContent: true,      // src/fnc-helpers.js
                    // Vendor
                    $: true,                    // jQuery
                    _: true,                    // underscore.js
                    Backbone: true,             // backbone.min.js
                    bootbox: true,              // bootbox.min.js
                    CKEDITOR: true,             // ckeditor.js
                    CodeMirror: true,           // ckeditor.js
                    google: true,               // Google
                    jQuery: true,               // jQuery
                    moment: true,               // moment.min.js
                    UAParser: true              // ua-parser.min.js
                }
            },
            src: ['js/src/*.js']
        }

    });

    require('load-grunt-tasks')(grunt);

    grunt.registerTask('default', ['sass', 'jshint', 'concat:js', 'uglify', 'watch']);

};
<commit_msg>Corrected paths to bootstrap files and renamed output to bootstrap.min.js<commit_after>module.exports = function(grunt) {
    grunt.util.linefeed = '\n';

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        config: grunt.file.readYAML('../config/config.yml'),

        watch: {
            options: {
                spawn: false,
                livereload: true
            },
            scripts: {
                files: [
                    'sass/*.scss',
                    'sass/nav/*.scss',
                    'sass/modules/*.scss'
                ],
                tasks: [
                    'sass'
                ]
            },
            js: {
                files: [
                    'js/*.js',
                    'js/src/*.js'
                ],
                tasks: [
                    'concat:js'
                ]
            }
        },

        sass: {
            dist: {
                options: {
                    outputStyle: 'compressed',
                    includePaths: [
                        'node_modules/bootstrap-sass/assets/stylesheets/',
                        'node_modules/font-awesome/scss/'
                    ],
                    lineNumbers: false,
                    unixNewlines: true,
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED CSS files!\n" +
                            " * Do not edit these files, because all changes will be lost.\n" +
                            " * You can edit ../scss/app.scss & ../scss/app-old-ie.scss, and run 'grunt' to generate this file.\n" +
                            " */\n",
                    precision: 5
                },
                files: {
                    'css/bolt-old-ie.css': 'sass/app-old-ie.scss',
                    'css/bolt.css': 'sass/app.scss'
                }
            }

        },

        copy: {
            main: {
                // Includes files within path
                files: [{
                    expand: true,
                    flatten: true,
                    src: [
                        'node_modules/font-awesome/fonts/*'
                    ],
                    filter: 'isFile',
                    dest: 'fonts/'
                }]
            }
        },

        concat: {
            js: {
                options: {
                    separator: '\n/**********************************************************************************************************************/\n\n',
                    banner: "/**\n" +
                            " * These are Bolt's COMPILED JS files!\n" +
                            " * Do not edit these files, because all changes will be lost.\n" +
                            " * You can edit files in <js/src/*.js> and run 'grunt' to generate this file.\n" +
                            " */\n\n",
                    sourceMap: true,
                    sourceMapStyle: 'link'
                },
                nonull: true,
                src: [
                    'js/src/console.js',
                    'js/src/fnc-helpers.js',
                    'js/src/activity.js',
                    'js/src/bind-fileupload.js',
                    'js/src/make-uri-slug.js',
                    'js/src/video-embed.js',
                    'js/src/geolocation.js',
                    'js/src/upload-files.js',
                    'js/src/obj-sidebar.js',
                    'js/src/obj-navpopups.js',
                    'js/src/obj-moments.js',
                    'js/src/obj-files.js',
                    'js/src/obj-stack.js',
                    'js/src/obj-folders.js',
                    'js/src/obj-datetime.js',
                    'js/src/init.js',
                    'js/src/start.js'
                ],
                dest: 'js/bolt.js'
            }
        },

        uglify: {
            bootstrap: {
                files: {
                    'js/bootstrap.min.js': [
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/alert.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/button.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/dropdown.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tab.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/transition.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/modal.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/tooltip.js',
                        'node_modules/bootstrap-sass/assets/javascripts/bootstrap/popover.js'
                    ]
                }
            },
            bolt: {
                files: {
                    'js/bolt.min.js': [
                        'js/src/console.js',
                        'js/src/fnc-helpers.js',
                        'js/src/activity.js',
                        'js/src/bind-fileupload.js',
                        'js/src/make-uri-slug.js',
                        'js/src/video-embed.js',
                        'js/src/geolocation.js',
                        'js/src/upload-files.js',
                        'js/src/obj-sidebar.js',
                        'js/src/obj-navpopups.js',
                        'js/src/obj-moments.js',
                        'js/src/obj-files.js',
                        'js/src/obj-stack.js',
                        'js/src/obj-folders.js',
                        'js/src/obj-datetime.js',
                        'js/src/init.js',
                        'js/src/start.js'
                    ]
                }
            }
        },

        jshint: {
            options: {
                browser: true,          // Defines globals exposed by modern browsers
                curly: true,            // Always put curly braces around blocks
                devel: true,            // Defines globals that are usually used for logging/debugging
                immed: true,            // Prohibits the use of immediate function invocations without parentheses
                indent: 4,              // Tab width
                latedef: true,          // Prohibits the use of a variable before it was defined
                maxlen: 120,            // Maximum length of a line
                noarg: true,            // Prohibits the use of arguments.caller and arguments.callee
                nonbsp: true,           // Warns about "non-breaking whitespace" characters
                singleGroups: true,     // Prohibits the use of the grouping operator for single-expression statements
                undef: true,            // Prohibits the use of undeclared variables
                globals: {
                    // Bolt
                    bolt: true,                 // src/console.js
                    FilelistHolder: true,       // src/upload-files.js
                    Files: true,                // src/obj-files.js
                    Folders: true,              // src/obj-folders.js
                    init: true,                 // src/init.js
                    Moments: true,              // src/obj-moments.js
                    Navpopups: true,            // src/obj-navpopups.js
                    Sidebar: true,              // src/obj-sidebar.js
                    Stack: true,                // src/obj-stack.js
                    // Bolt global functions
                    bindFileUpload: true,       // src/bindfileuploads.js
                    bindGeolocation: true,      // src/geolocation.js
                    bindVideoEmbed: true,       // src/video-embed.js
                    getSelectedItems: true,     // src/fnc-helpers.js
                    makeUri: true,              // src/make-uri-slug.js
                    makeUriAjax: true,          // src/make-uri-slug.js
                    stopMakeUri: true,          // src/make-uri-slug.js
                    updateLatestActivity: true, // src/activity.js
                    validateContent: true,      // src/fnc-helpers.js
                    // Vendor
                    $: true,                    // jQuery
                    _: true,                    // underscore.js
                    Backbone: true,             // backbone.min.js
                    bootbox: true,              // bootbox.min.js
                    CKEDITOR: true,             // ckeditor.js
                    CodeMirror: true,           // ckeditor.js
                    google: true,               // Google
                    jQuery: true,               // jQuery
                    moment: true,               // moment.min.js
                    UAParser: true              // ua-parser.min.js
                }
            },
            src: ['js/src/*.js']
        }

    });

    require('load-grunt-tasks')(grunt);

    grunt.registerTask('default', ['sass', 'jshint', 'concat:js', 'uglify', 'watch']);

};
<|endoftext|>


<commit_before>var $ = require('jquery-browserify');
var chosen = require('chosen-jquery-browserify');
var _ = require('underscore');
_.merge = require('deepmerge');
var jsyaml = require('js-yaml');
var Backbone = require('backbone');
var templates = require('../../dist/templates');
var util = require('../util');

var forms = {
  Checkbox: require('./meta/checkbox'),
  TextForm: require('./meta/text'),
  TextArea: require('./meta/textarea'),
  Button: require('./meta/button'),
  Select: require('./meta/select'),
  Multiselect: require('./meta/multiselect'),
};

// Creates form elements that correspond to Jekyll frontmatter.
//
// Handles the reading and updating of object/key pairs
// on the file model.
//
// This view is always a child view of views/file.js.

module.exports = Backbone.View.extend({
  template: templates.metadata,

  events: {
    'change .metafield': 'updateModel',
    'click button.metafield': 'updateModel',
    'click .create-select': 'createSelect',
    'click .finish': 'exit'
  },

  // @options object
  // @options.model object (file model attached to file view)
  // @options.view object (file view)
  // @options.titleAsHeading boolean
  //
  // titleAsHeading is true when the filetype is markdown,
  // and when there exists a meta field called title.
  initialize: function(options) {
    _.bindAll(this);

    this.model = options.model;
    this.titleAsHeading = options.titleAsHeading;
    this.view = options.view;

    this.subviews = [];
    this.codeMirrorInstances = {};
  },

  // Parent file view calls this render func immediately
  // after initializing this view.
  // This is responsible for rendering metadata fields for each *default*.
  render: function() {
    this.$el.empty().append(_.template(this.template));

    var $form = this.$el.find('.form');

    var metadata = this.model.get('metadata') || {};
    var lang = metadata && metadata.lang ? metadata.lang : 'en';

    // Using the yml configuration file for metadata,
    // render form fields.
    _.each(this.model.get('defaults'), (function(data, key) {

      // Tests that 1. This is the title metadata,
      // and 2. We've decided to combine the title form UI as the page header.
      // If both are true, then don't render this default.
      if (data && data.name === 'title' && this.titleAsHeading) {
        return;
      }

      var view = null;

      // If there's no data field, default to text field.
      if (!data || (data && !data.field)) {
        var field = {
          label: key,
          value: data,
        }
        var name = data.name ? data.name : key;
        view = new forms.TextForm({data: {
          name: name,
          type: 'text',
          field: field
        }});
      }

      // Use the data field to determine the kind of meta form to draw.
      else {
        switch (data.field.element) {
          case 'button':
            view = new forms.Button({data: data});
          break;
          case 'checkbox':
            view = new forms.Checkbox({data: data});
          break;
          case 'text':
            view = new forms.TextForm({
              data: _.extend({}, data, {type: 'text'})
            });
          break;
          case 'textarea':
            view = new forms.TextArea({
              data: _.extend({}, data, {id: util.stringToUrl(data.name)})
            });
          break;
          case 'number':
            view = new forms.TextForm({
              data: _.extend({}, data, {type: 'number'})
            });
          break;
          case 'select':
            view = new forms.Select({
              data: _.extend({}, data, {lang: lang})
            });
          break;
          case 'multiselect':
            view = new forms.Multiselect({
              data: _.extend({}, data, {lang: lang})
            });
          break;

          // On hidden values, we obviously don't have to render anything.
          // Just make sure this default is saved on the metadata object.
          case 'hidden':
            var preExisting = metadata[data.name];
            var newDefault = data.field.value;
            var newMeta = {};

            // If the pre-existing metadata is an array,
            // make sure we don't just override it, but we find the difference.
            if (_.isArray(preExisting)) {
              newMeta[data.name] = _.difference(newDefault, preExisting).length ?
                _.union(newDefault, preExisting) : preExisting;
            }
            // If pre-existing is a single property or undefined,
            // use _.extend to default to pre-existing if it exists, or
            // newDefault if there is no pre-existing.
            else {
              newMeta[data.name] = newDefault;
            }
            this.model.set('metadata', _.extend(newMeta, metadata));
          break;
        }
      }

      if (view !== null) {
        $form.append(view.render());
        this.subviews.push(view);

        // If the view is a text area, we'll need to init codemirror.
        if (data && data.field && data.field.element === 'textarea') {
          var id = util.stringToUrl(data.name);

          // TODO passing in a bound callback is not the best
          // as it increases the debugging surface area.
          // Find some way to get around this.
          var codeMirror = view.initCodeMirror(this.updateModel.bind(this));

          this.codeMirrorInstances[id] = codeMirror;
        }
      }
    }).bind(this));

    // Attach a change event listener
    this.$el.find('.chzn-select').chosen().change(this.updateModel);

    // Renders the raw metadata textarea form
    this.renderRawEditor();

    // Now that we've rendered the form elements according
    // to defaults, sync the form elements with the current metadata.
    this.setValue(this.model.get('metadata'));

    // Now that we've synced the values from metadata, do a save to model.
    // Important because some elements, such as buttons, rely on a default
    // and don't save to the metadata model until we explicitly call this.
    this.model.set('metadata', this.getValue());

    return this;
  },

  // Record metadata, signal to file that a save is possible.
  updateModel: function() {
    this.model.set('metadata', this.getValue());
    this.view.makeDirty();
  },

  // TODO This.view is vague and doesn't get across that we're
  // communicating with the parent file view.
  //
  // It also doesn't get across that we're performing a save operation.
  rawKeyMap: function() {
    return {
      'Ctrl-S': this.view.updateFile
    };
  },

  // Responsible for rendering the raw metadata element
  // and listening for changes.
  renderRawEditor: function() {
    var isYaml = this.model.get('lang') === 'yaml';
    var $parent
    if (isYaml) {
      $parent = this.view.$el.find('#code');
      $parent.empty();
    }
    else {
      this.$el.find('.form').append(_.template(templates.meta.raw));
      $parent = this.$el.find('#raw');
    }

    this.codeMirrorInstances.rawEditor = CodeMirror($parent[0], {
      mode: 'yaml',
      value: '',
      lineWrapping: true,
      lineNumbers: isYaml,
      extraKeys: this.rawKeyMap(),
      theme: 'prose-bright'
    });

    this.listenTo(this.codeMirrorInstances.rawEditor, 'blur', (function(codeMirror) {
      try {
        var rawValue = jsyaml.safeLoad(codeMirror.getValue());
      } catch(err) {
        console.log("Error parsing CodeMirror editor text");
        console.log(err);
      }
      if (rawValue) {
        var metadata = this.model.get('metadata');
        this.model.set('metadata', _.extend(metadata, rawValue));
        this.view.makeDirty();
      }
    }).bind(this));
  },

  getValue: function() {
    var view = this;
    var metadata = this.model.get('metadata') || {};

    // It's important to save only the data that's represented
    // by the current meta elements.
    // Even if there are elements with the same name.
    _.chain(this.subviews).map(function(view) {
      return {
        value: view.getValue(),
        name: view.name
      };
    }).groupBy('name').each(function(group) {
      var name = group[0].name;
      metadata[name] = group.length === 1 ?
        group[0].value : _.pluck(group, 'value');
    });

    // TODO does this always default metadata.published to true?
    if (this.view.toolbar &&
       this.view.toolbar.publishState() ||
       (metadata && metadata.published)) {
      metadata.published = true;
    } else {
      metadata.published = false;
    }

    // Get the title value from heading if it's available.
    // In testing environment, if the header doesn't render
    // then this.view.header is undefined.
     if (this.titleAsHeading && this.view.header) {
      metadata.title = this.view.header.inputGet();
    }

    // Load any data coming from not defined raw yaml front matter.
    if (this.codeMirrorInstances.rawEditor) {
      try {
        metadata = _.merge(metadata, jsyaml.safeLoad(this.codeMirrorInstances.rawEditor.getValue()) || {});
      } catch (err) {
        console.log("Error parsing not defined raw yaml front matter");
        console.log(err);
      }
    }

    // TODO Currently, it's not possible to delete metadata.
    // There should be some logic here that looks to see if there's any metadata
    // that isn't an element, isn't hidden, and isn't in the raw editor.
    return metadata;
  },

  // @metadata object metadata key/value pairs
  // Syncs the visual UI with what's currently saved on the model.
  setValue: function(metadata) {

    // The key point here is that the UI reflects exactly
    // what's in the metadata.
    // By now we've already rendered our elements along with
    // the correct defaults.
    metadata = metadata || {};
    var rawEditor = this.codeMirrorInstances.rawEditor;
    var subviews = this.subviews;
    var defaults = this.model.get('defaults') || [];
    var hidden = defaults.filter(function(d) {
      return d.field && d.field.element === 'hidden';
    });

    // Easiest thing to do is update metadata fields
    // that are rendered already, ie. have defaults specified
    // in _config.yml or _prose.yml
    _.each(metadata, function(value, key) {

      // Filter instead of find, because you never know if someone
      // is using the same key for two different elements.
      var renderedViews = _.filter(subviews, function(view) {
        return view.name === key;
      });

      // If there are n rendered views corresponding to n
      // metadata values of the same name, assign values based on order.
      // This works because metadata yaml is parsed in order, and
      // subviews is an ordered array.
      if (renderedViews.length && _.isArray(value)
          && value.length === renderedViews.length) {
        _.each(renderedViews, function(view, i) {
          view.setValue(value[i]);
        });
      }
      else if (renderedViews.length === 1) {
        renderedViews[0].setValue(value);
      }

      // Next is to take any metadata field that doesn't have a form,
      // and throw it into the raw editor.
      // Note, we don't want to include hidden elements,
      // titles, or published states here.
      else if (!renderedViews.length && value &&
               key !== 'title' && key !== 'published' &&
               !_.find(hidden, function(d) { return d.name === key })){
        var raw = {};
        raw[key] = value;
        rawEditor.setValue(rawEditor.getValue() + jsyaml.safeDump(raw));
      }
    });
  },

  refresh: function() {
    _.each(this.codeMirrorInstances, function(codeMirror) {
      codeMirror.refresh();
    });
  },

  createSelect: function(e) {
    var $parent = $(e.target).parent();
    var $input = $parent.find('input');
    var selectTarget = $(e.target).data('select');
    var $select = this.$el.find('#' + selectTarget);
    var value = _($input.val()).escape();

    if (value.length > 0) {
      var option = '<option value="' + value + '" selected="selected">' + value + '</option>';

      // Append this new option to the select list.
      $select.append(option);

      // Clear the now added value.
      $input.attr('value', '');

      // Update the list
      $select.trigger('liszt:updated');
      $select.trigger('change');
    }

    return false;
  },

  exit: function() {
    this.view.nav.active(this.view.mode);

    if (this.view.mode === 'blob') {
      this.view.blob();
    } else {
      this.view.edit();
    }

    return false;
  },

  remove: function() {
    _.invoke(this.subviews, 'remove');
    this.subviews = [];
    Backbone.View.prototype.remove.apply(this, arguments);
  }
});
<commit_msg>When building hidden elements, ensure we extend from the existing hash<commit_after>var $ = require('jquery-browserify');
var chosen = require('chosen-jquery-browserify');
var _ = require('underscore');
_.merge = require('deepmerge');
var jsyaml = require('js-yaml');
var Backbone = require('backbone');
var templates = require('../../dist/templates');
var util = require('../util');

var forms = {
  Checkbox: require('./meta/checkbox'),
  TextForm: require('./meta/text'),
  TextArea: require('./meta/textarea'),
  Button: require('./meta/button'),
  Select: require('./meta/select'),
  Multiselect: require('./meta/multiselect'),
};

// Creates form elements that correspond to Jekyll frontmatter.
//
// Handles the reading and updating of object/key pairs
// on the file model.
//
// This view is always a child view of views/file.js.

module.exports = Backbone.View.extend({
  template: templates.metadata,

  events: {
    'change .metafield': 'updateModel',
    'click button.metafield': 'updateModel',
    'click .create-select': 'createSelect',
    'click .finish': 'exit'
  },

  // @options object
  // @options.model object (file model attached to file view)
  // @options.view object (file view)
  // @options.titleAsHeading boolean
  //
  // titleAsHeading is true when the filetype is markdown,
  // and when there exists a meta field called title.
  initialize: function(options) {
    _.bindAll(this);

    this.model = options.model;
    this.titleAsHeading = options.titleAsHeading;
    this.view = options.view;

    this.subviews = [];
    this.codeMirrorInstances = {};
  },

  // Parent file view calls this render func immediately
  // after initializing this view.
  // This is responsible for rendering metadata fields for each *default*.
  render: function() {
    this.$el.empty().append(_.template(this.template));

    var $form = this.$el.find('.form');

    var metadata = this.model.get('metadata') || {};
    var lang = metadata && metadata.lang ? metadata.lang : 'en';

    // Using the yml configuration file for metadata,
    // render form fields.
    _.each(this.model.get('defaults'), (function(data, key) {

      // Tests that 1. This is the title metadata,
      // and 2. We've decided to combine the title form UI as the page header.
      // If both are true, then don't render this default.
      if (data && data.name === 'title' && this.titleAsHeading) {
        return;
      }

      var view = null;

      // If there's no data field, default to text field.
      if (!data || (data && !data.field)) {
        var field = {
          label: key,
          value: data,
        }
        var name = data.name ? data.name : key;
        view = new forms.TextForm({data: {
          name: name,
          type: 'text',
          field: field
        }});
      }

      // Use the data field to determine the kind of meta form to draw.
      else {
        switch (data.field.element) {
          case 'button':
            view = new forms.Button({data: data});
          break;
          case 'checkbox':
            view = new forms.Checkbox({data: data});
          break;
          case 'text':
            view = new forms.TextForm({
              data: _.extend({}, data, {type: 'text'})
            });
          break;
          case 'textarea':
            view = new forms.TextArea({
              data: _.extend({}, data, {id: util.stringToUrl(data.name)})
            });
          break;
          case 'number':
            view = new forms.TextForm({
              data: _.extend({}, data, {type: 'number'})
            });
          break;
          case 'select':
            view = new forms.Select({
              data: _.extend({}, data, {lang: lang})
            });
          break;
          case 'multiselect':
            view = new forms.Multiselect({
              data: _.extend({}, data, {lang: lang})
            });
          break;

          // On hidden values, we obviously don't have to render anything.
          // Just make sure this default is saved on the metadata object.
          case 'hidden':
            var preExisting = metadata[data.name];
            var newDefault = data.field.value;
            var newMeta = {};

            // If the pre-existing metadata is an array,
            // make sure we don't just override it, but we find the difference.
            if (_.isArray(preExisting)) {
              newMeta[data.name] = _.difference(newDefault, preExisting).length ?
                _.union(newDefault, preExisting) : preExisting;
            }
            // If pre-existing is a single property or undefined,
            // use _.extend to default to pre-existing if it exists, or
            // newDefault if there is no pre-existing.
            else {
              newMeta[data.name] = newDefault;
            }
            this.model.set('metadata', _.extend(newMeta, this.model.get('metadata')));
          break;
        }
      }

      if (view !== null) {
        $form.append(view.render());
        this.subviews.push(view);

        // If the view is a text area, we'll need to init codemirror.
        if (data && data.field && data.field.element === 'textarea') {
          var id = util.stringToUrl(data.name);

          // TODO passing in a bound callback is not the best
          // as it increases the debugging surface area.
          // Find some way to get around this.
          var codeMirror = view.initCodeMirror(this.updateModel.bind(this));

          this.codeMirrorInstances[id] = codeMirror;
        }
      }
    }).bind(this));

    // Attach a change event listener
    this.$el.find('.chzn-select').chosen().change(this.updateModel);

    // Renders the raw metadata textarea form
    this.renderRawEditor();

    // Now that we've rendered the form elements according
    // to defaults, sync the form elements with the current metadata.
    this.setValue(this.model.get('metadata'));

    // Now that we've synced the values from metadata, do a save to model.
    // Important because some elements, such as buttons, rely on a default
    // and don't save to the metadata model until we explicitly call this.
    this.model.set('metadata', this.getValue());

    return this;
  },

  // Record metadata, signal to file that a save is possible.
  updateModel: function() {
    this.model.set('metadata', this.getValue());
    this.view.makeDirty();
  },

  // TODO This.view is vague and doesn't get across that we're
  // communicating with the parent file view.
  //
  // It also doesn't get across that we're performing a save operation.
  rawKeyMap: function() {
    return {
      'Ctrl-S': this.view.updateFile
    };
  },

  // Responsible for rendering the raw metadata element
  // and listening for changes.
  renderRawEditor: function() {
    var isYaml = this.model.get('lang') === 'yaml';
    var $parent
    if (isYaml) {
      $parent = this.view.$el.find('#code');
      $parent.empty();
    }
    else {
      this.$el.find('.form').append(_.template(templates.meta.raw));
      $parent = this.$el.find('#raw');
    }

    this.codeMirrorInstances.rawEditor = CodeMirror($parent[0], {
      mode: 'yaml',
      value: '',
      lineWrapping: true,
      lineNumbers: isYaml,
      extraKeys: this.rawKeyMap(),
      theme: 'prose-bright'
    });

    this.listenTo(this.codeMirrorInstances.rawEditor, 'blur', (function(codeMirror) {
      try {
        var rawValue = jsyaml.safeLoad(codeMirror.getValue());
      } catch(err) {
        console.log("Error parsing CodeMirror editor text");
        console.log(err);
      }
      if (rawValue) {
        var metadata = this.model.get('metadata');
        this.model.set('metadata', _.extend(metadata, rawValue));
        this.view.makeDirty();
      }
    }).bind(this));
  },

  getValue: function() {
    var view = this;
    var metadata = this.model.get('metadata') || {};

    // It's important to save only the data that's represented
    // by the current meta elements.
    // Even if there are elements with the same name.
    _.chain(this.subviews).map(function(view) {
      return {
        value: view.getValue(),
        name: view.name
      };
    }).groupBy('name').each(function(group) {
      var name = group[0].name;
      metadata[name] = group.length === 1 ?
        group[0].value : _.pluck(group, 'value');
    });

    // TODO does this always default metadata.published to true?
    if (this.view.toolbar &&
       this.view.toolbar.publishState() ||
       (metadata && metadata.published)) {
      metadata.published = true;
    } else {
      metadata.published = false;
    }

    // Get the title value from heading if it's available.
    // In testing environment, if the header doesn't render
    // then this.view.header is undefined.
     if (this.titleAsHeading && this.view.header) {
      metadata.title = this.view.header.inputGet();
    }

    // Load any data coming from not defined raw yaml front matter.
    if (this.codeMirrorInstances.rawEditor) {
      try {
        metadata = _.merge(metadata, jsyaml.safeLoad(this.codeMirrorInstances.rawEditor.getValue()) || {});
      } catch (err) {
        console.log("Error parsing not defined raw yaml front matter");
        console.log(err);
      }
    }

    // TODO Currently, it's not possible to delete metadata.
    // There should be some logic here that looks to see if there's any metadata
    // that isn't an element, isn't hidden, and isn't in the raw editor.
    return metadata;
  },

  // @metadata object metadata key/value pairs
  // Syncs the visual UI with what's currently saved on the model.
  setValue: function(metadata) {

    // The key point here is that the UI reflects exactly
    // what's in the metadata.
    // By now we've already rendered our elements along with
    // the correct defaults.
    metadata = metadata || {};
    var rawEditor = this.codeMirrorInstances.rawEditor;
    var subviews = this.subviews;
    var defaults = this.model.get('defaults') || [];
    var hidden = defaults.filter(function(d) {
      return d.field && d.field.element === 'hidden';
    });

    // Easiest thing to do is update metadata fields
    // that are rendered already, ie. have defaults specified
    // in _config.yml or _prose.yml
    _.each(metadata, function(value, key) {

      // Filter instead of find, because you never know if someone
      // is using the same key for two different elements.
      var renderedViews = _.filter(subviews, function(view) {
        return view.name === key;
      });

      // If there are n rendered views corresponding to n
      // metadata values of the same name, assign values based on order.
      // This works because metadata yaml is parsed in order, and
      // subviews is an ordered array.
      if (renderedViews.length && _.isArray(value)
          && value.length === renderedViews.length) {
        _.each(renderedViews, function(view, i) {
          view.setValue(value[i]);
        });
      }
      else if (renderedViews.length === 1) {
        renderedViews[0].setValue(value);
      }

      // Next is to take any metadata field that doesn't have a form,
      // and throw it into the raw editor.
      // Note, we don't want to include hidden elements,
      // titles, or published states here.
      else if (!renderedViews.length && value &&
               key !== 'title' && key !== 'published' &&
               !_.find(hidden, function(d) { return d.name === key })){
        var raw = {};
        raw[key] = value;
        rawEditor.setValue(rawEditor.getValue() + jsyaml.safeDump(raw));
      }
    });
  },

  refresh: function() {
    _.each(this.codeMirrorInstances, function(codeMirror) {
      codeMirror.refresh();
    });
  },

  createSelect: function(e) {
    var $parent = $(e.target).parent();
    var $input = $parent.find('input');
    var selectTarget = $(e.target).data('select');
    var $select = this.$el.find('#' + selectTarget);
    var value = _($input.val()).escape();

    if (value.length > 0) {
      var option = '<option value="' + value + '" selected="selected">' + value + '</option>';

      // Append this new option to the select list.
      $select.append(option);

      // Clear the now added value.
      $input.attr('value', '');

      // Update the list
      $select.trigger('liszt:updated');
      $select.trigger('change');
    }

    return false;
  },

  exit: function() {
    this.view.nav.active(this.view.mode);

    if (this.view.mode === 'blob') {
      this.view.blob();
    } else {
      this.view.edit();
    }

    return false;
  },

  remove: function() {
    _.invoke(this.subviews, 'remove');
    this.subviews = [];
    Backbone.View.prototype.remove.apply(this, arguments);
  }
});
<|endoftext|>


<commit_before>

function export_as_img( layer, filename ){
  // Actual writing of asset
  var slice,
    rect = [layer rectByAccountingForStyleSize:[[layer absoluteRect] rect]]

  slice = [[MSSliceMaker slicesFromExportableLayer:layer inRect:rect] firstObject]
  slice.page = [[doc currentPage] copyLightweight]
  slice.format = "png"
  slice.scale = 2

//  Util.log("— writing asset " + slice + " to disk: " + filename)
  var imageData = [MSSliceExporter dataForRequest:slice]
  [imageData writeToFile:filename atomically:true]

}

function output( processResult ){

    //1. Create folders
    if( Util.folder_exist( Config.target_folder) ){
        Util.remove_folder( Config.target_folder)
    }
    Util.create_folders(
        [NSArray arrayWithObjects:
            Config.target_folder,Config.images_folder,Config.js_folder,Config.css_folder,nil])

    //2. Save inline script as single file
    // Single script structure : {comment:"",src:"",script:""}
    var scriptStr = "", scripts = Config.global_scripts.concat(processResult.scripts), scriptsCache={}
    Util.log( 'scripts length:' + scripts.length)
    Util.each( scripts, function(scriptObj){
        if( scriptObj.src && !scriptsCache[scriptObj.src]){
            var src = scriptObj.src.replace(Config.target_folder+'/',''),
                scriptNode = Dom.create('script').attr('src',src)
            processResult.dom.append(scriptNode)

            if( scriptObj.origin ){
                Util.copy( scriptObj.origin, scriptObj.src )
            }
            scriptsCache[scriptObj.src] = true
        }else{
            scriptStr += ['/* '+scriptObj.comment+' */',Util.script_to_string(scriptObj.script),''].join('\n')
        }
    })

    if( scriptStr ){
        var scriptSrc = Config.target_folder + "/index.js"
        processResult.dom.append( Dom.create('script').attr('src', 'index.js' ) )
        Util.save_file_from_string( scriptSrc, scriptStr);
    }

    //3. Save css as single file
    // Single style structure: {comment:"",href:"",style:{name:value}}
    var styleStr = "", styles = Config.global_styles.concat( processResult.styles),stylesCache = {}
    Util.each( styles, function(styleObj){
        if( styleObj.href && !scriptsCache[styleObj.href] ){
            var href = styleObj.href.replace( Config.target_folder+'/',''),
                linkDom = Dom.create('link').attr('href',href).attr('type','text/css').attr('rel','stylesheet')
            processResult.dom.prepend(linkDom)

            if( styleObj.origin ){
                Util.copy( styleObj.origin, styleObj.href )
            }
            stylesCache[styleObj.href] = true
        }else{
            styleStr += ['/* '+styleObj.comment+' */', Util.style_to_string(styleObj.style) ,''].join('\n')
        }
    })
    if( styleStr ){
        var styleSrc = Config.target_folder + "/index.css"
        processResult.dom.append(
            Dom.create('link').attr('href','index.css').attr('type','text/css').attr('rel','stylesheet'))

        Util.save_file_from_string( styleSrc, styleStr);
    }

    //4. Export images
    if( processResult.exportFiles ){
        Util.each( processResult.exportFiles, function( fileObj ){
//            Util.log("exporting "+fileObj.target)
            export_as_img( fileObj.layer, fileObj.target )
        })
    }

    //5. Save html
    Util.save_file_from_string(Config.target_folder + "/index.html", processResult.dom.outerHTML);
//    Util.save_file_from_string(Config.target_folder + "/index.html", processResult.dom.outerHTML());
}

function main() {
    Util.execute(function() {
        var start = new Date().getTime()

        Util.log("###################")
        Util.log("### blade start ###")
        Util.log("###################")

        //1. Process layers
        var layers = [[doc currentPage] layers],
            processResult

        Util.each( layers, function( subLayer ){
            processResult = Binding.apply_bindings( subLayer );
        })

        //2. output files
//        Util.log( processResult.dom.outerHTML )
        output( processResult )

        var end = new Date().getTime()
        Util.log("Time used: " +(end - start))
        Util.log("###################")
        Util.log("###  blade end  ###")
        Util.log("###################")
        [doc showMessage:"Export Complete"]

    })
}

main();<commit_msg>Modification for newer Version of Sketch/Changed API function rectByAccountingForStyleSize -> absoluteDirtyRect<commit_after>

function export_as_img( layer, filename ){
  // Actual writing of asset
  var slice,
    rect = [layer absoluteDirtyRect]

  slice = [[MSSliceMaker slicesFromExportableLayer:layer inRect:rect] firstObject]
  slice.page = [[doc currentPage] copyLightweight]
  slice.format = "png"
  slice.scale = 2

//  Util.log("— writing asset " + slice + " to disk: " + filename)
  var imageData = [MSSliceExporter dataForRequest:slice]
  [imageData writeToFile:filename atomically:true]

}

function output( processResult ){

    //1. Create folders
    if( Util.folder_exist( Config.target_folder) ){
        Util.remove_folder( Config.target_folder)
    }
    Util.create_folders(
        [NSArray arrayWithObjects:
            Config.target_folder,Config.images_folder,Config.js_folder,Config.css_folder,nil])

    //2. Save inline script as single file
    // Single script structure : {comment:"",src:"",script:""}
    var scriptStr = "", scripts = Config.global_scripts.concat(processResult.scripts), scriptsCache={}
    Util.log( 'scripts length:' + scripts.length)
    Util.each( scripts, function(scriptObj){
        if( scriptObj.src && !scriptsCache[scriptObj.src]){
            var src = scriptObj.src.replace(Config.target_folder+'/',''),
                scriptNode = Dom.create('script').attr('src',src)
            processResult.dom.append(scriptNode)

            if( scriptObj.origin ){
                Util.copy( scriptObj.origin, scriptObj.src )
            }
            scriptsCache[scriptObj.src] = true
        }else{
            scriptStr += ['/* '+scriptObj.comment+' */',Util.script_to_string(scriptObj.script),''].join('\n')
        }
    })

    if( scriptStr ){
        var scriptSrc = Config.target_folder + "/index.js"
        processResult.dom.append( Dom.create('script').attr('src', 'index.js' ) )
        Util.save_file_from_string( scriptSrc, scriptStr);
    }

    //3. Save css as single file
    // Single style structure: {comment:"",href:"",style:{name:value}}
    var styleStr = "", styles = Config.global_styles.concat( processResult.styles),stylesCache = {}
    Util.each( styles, function(styleObj){
        if( styleObj.href && !scriptsCache[styleObj.href] ){
            var href = styleObj.href.replace( Config.target_folder+'/',''),
                linkDom = Dom.create('link').attr('href',href).attr('type','text/css').attr('rel','stylesheet')
            processResult.dom.prepend(linkDom)

            if( styleObj.origin ){
                Util.copy( styleObj.origin, styleObj.href )
            }
            stylesCache[styleObj.href] = true
        }else{
            styleStr += ['/* '+styleObj.comment+' */', Util.style_to_string(styleObj.style) ,''].join('\n')
        }
    })
    if( styleStr ){
        var styleSrc = Config.target_folder + "/index.css"
        processResult.dom.append(
            Dom.create('link').attr('href','index.css').attr('type','text/css').attr('rel','stylesheet'))

        Util.save_file_from_string( styleSrc, styleStr);
    }

    //4. Export images
    if( processResult.exportFiles ){
        Util.each( processResult.exportFiles, function( fileObj ){
//            Util.log("exporting "+fileObj.target)
            export_as_img( fileObj.layer, fileObj.target )
        })
    }

    //5. Save html
    Util.save_file_from_string(Config.target_folder + "/index.html", processResult.dom.outerHTML);
//    Util.save_file_from_string(Config.target_folder + "/index.html", processResult.dom.outerHTML());
}

function main() {
    Util.execute(function() {
        var start = new Date().getTime()

        Util.log("###################")
        Util.log("### blade start ###")
        Util.log("###################")

        //1. Process layers
        var layers = [[doc currentPage] layers],
            processResult

        Util.each( layers, function( subLayer ){
            processResult = Binding.apply_bindings( subLayer );
        })

        //2. output files
//        Util.log( processResult.dom.outerHTML )
        output( processResult )

        var end = new Date().getTime()
        Util.log("Time used: " +(end - start))
        Util.log("###################")
        Util.log("###  blade end  ###")
        Util.log("###################")
        [doc showMessage:"Export Complete"]

    })
}

main();<|endoftext|>