neos/neos-development-collection

View on GitHub
Neos.Neos/Scripts/Gruntfile.js

Summary

Maintainability
F
4 days
Test Coverage
module.exports = function (grunt) {
    var path = require('path'),
        packagePath = path.join(__dirname, '../'),
        libraryPath = path.join(packagePath, 'Resources/Public/Library/');

    grunt.initConfig({
        watch: {
            css: {
                files: path.join(packagePath, 'Resources/Private/Styles/**/*.scss'),
                tasks: ['compile-css'],
                options: {
                    spawn: false,
                    debounceDelay: 250,
                    interrupt: true
                }
            }
        },
        compass: {
            compile: {
                options: {
                    config: path.join(packagePath, 'Resources/Private/Styles/config.rb'),
                    basePath: path.join(packagePath, 'Resources/Private/Styles'),
                    trace: false,
                    quiet: true,
                    sourcemap: false,
                    bundleExec: true
                }
            }
        },
        concat: {
            css: {
                options: {
                    process: function (src, filepath) {
                        if (filepath.indexOf('jcrop') !== -1) {
                            console.log('jcrop', filepath);
                            src = src.replace('url("', 'url("../Library/jcrop/css/');
                        }
                        if (filepath.indexOf('jquery-ui') !== -1) {
                            console.log('jcrop', filepath);
                            src = src.replace(/url\(/g, 'url(../Library/jquery-ui/css/custom-theme/');
                        }
                        return src;
                    }
                },
                src: [
                    path.join(packagePath, 'Resources/Public/Styles/Neos.css'),
                    path.join(libraryPath, 'jquery-ui/css/custom-theme/jquery-ui-1.8.16.custom.css'),
                    path.join(libraryPath, 'jcrop/css/jquery.Jcrop.css'),
                    path.join(libraryPath, 'chosen/chosen/chosen.min.css')
                ],
                dest: path.join(packagePath, 'Resources/Public/Styles', 'Includes-built.css')
            }
        },
        requirejs: {
            compile: {
                options: {
                    mainConfigFile: 'build.js'
                }
            }
        },
        trimtrailingspaces: {
            js: {
                src: [libraryPath + 'jquery-with-dependencies.js'],
                filter: 'isFile',
                encoding: 'utf8'
            }
        }
    });

    /**
     * QUnit tasks
     *
     * We run the tests on phantomjs, if you don't have that installed and running a ubuntu like machine
     * run ./install-phantomjs.sh first
     */
    grunt.config.merge({
        qunit: {
            all: [
                '../Tests/JavaScript/**/*.html'
            ]
        }
    });

    grunt.config.merge({
        concat: {
            requirejs: {
                src: [
                    libraryPath + 'requirejs/src/require.js'
                ],
                dest: libraryPath + 'requirejs/require.js',
                options: {
                    banner: 'if (!requirejs) {',
                    footer: '}'
                }
            },

            aloha: {
                src: [
                    libraryPath + 'aloha/aloha.js'
                ],
                dest: libraryPath + 'aloha/aloha.js',
                options: {
                    process: function(src, filepath) {
                        src = src.replace(/\$\(function \(\) {\s*\n\s+element.appendTo\('body'\);\n\s+}\);/, "$(function(){ element.appendTo('#neos-application'); });");
                        src = src.replace("jQuery('body').append(layer).bind('click', function(e) {", "jQuery('#neos-application').append(layer).bind('click', function(e) {");
                        src = src.replace('var editableTrimedContent = jQuery.trim(this.getContents()),', "var editableTrimedContent = $('<div />').html(this.getContents()).text().trim(),");

                        // Compatibility with no conflict jQuery UI
                        src = src.replace(/\.button\(/g, '.uibutton(');

                        // Fix broken this reference in list plugin
                        src = src.replace('jQuery.each(this.templates[nodeName].classes, function () {', 'jQuery.each(this.templates[nodeName].classes, function (i, cssClass) {');
                        src = src.replace(/listToStyle\.removeClass\(this\);/g, 'listToStyle.removeClass(cssClass);');
                        src = src.replace('jQuery.each(plugin.templates[listtype].classes, function () {', 'jQuery.each(plugin.templates[listtype].classes, function (i, cssClass) {');

                        // Workaround for jQueryUI menu issue / poorly written code in list plugin
                        src = src.replace("elem.data('aloha-ui-menubutton-select', function (){", "elem.click(function (){");

                        // We need to patch this file to make Aloha compatible with jquery UI 1.10.4
                        src = src.replace("this.container.tabs('select', this.index);", "this.container.tabs('option', 'active', this.index);");

                        // Remove instantiation of language repository in link plugin
                        src = src.replace(/\s+LANG_REPOSITORY[\s\w=('\-./,]+\);/, '');

                        // Remove setting of hreflang attribute on links
                        src = src.replace(/\s+this.hrefField.setAttribute\('hreflang', ''\);/, '');

                        // add "code" element
                        src = src.replace(/var componentNameByElement = {\n/, "var componentNameByElement = { 'code': 'code'," + "\n");
                        src = src.replace("availableButtons: [ 'u',", "availableButtons: [ 'code', 'u',");

                        // tooltips
                        src = src.replace(/tooltipClass: 'aloha aloha-ui-tooltip',/g, "placement: 'bottom',");
                        src = src.replace(".tooltip('close', null, true);", ".tooltip('hide');");
                        src = src.replace(".tooltip('disable');", ".tooltip('hide');");
                        src = src.replace(".tooltip('enable');", ".tooltip('show');");

                        // fix https://github.com/alohaeditor/Aloha-Editor/issues/1525
                        src = src.replace('RepositoryManager.markObject(targetObject, item);', 'executeForTargets(function (target) { RepositoryManager.markObject(target, item); });');

                        return src;
                    }
                }
            },

            bootstrap: {
                src: [
                    libraryPath + 'twitter-bootstrap/js/bootstrap-alert.js',
                    libraryPath + 'twitter-bootstrap/js/bootstrap-dropdown.js',
                    libraryPath + 'twitter-bootstrap/js/bootstrap-tooltip.js',
                    libraryPath + 'twitter-bootstrap/js/bootstrap-popover.js',
                    libraryPath + 'bootstrap-datetimepicker/js/bootstrap-datetimepicker.js'
                ],
                dest: libraryPath + 'bootstrap-components.js',
                options: {
                    banner: '',
                    footer: '',
                    process: function (src, filepath) {
                        src = src.replace(/keydown\./g, 'keydown.neos-');
                        src = src.replace(/focus\./g, 'focus.neos-');
                        src = src.replace(/click\./g, 'click.neos-');
                        src = src.replace(/Class\('(?!icon)/g, "Class('neos-");
                        src = src.replace(/\.divider/g, ".neos-divider");
                        src = src.replace(/pull-right/g, 'neos-pull-right');
                        src = src.replace(/class="(?!icon)/g, 'class="neos-');
                        src = src.replace(/(find|is|closest|filter)\(('|")\./g, "$1($2.neos-");
                        src = src.replace(/, \./g, '., .neos-');

                        // Dropdown
                        src = src.replace(/' dropdown-menu'/g, "' neos-dropdown-menu'");
                        src = src.replace(/\.dropdown form/g, '.neos-dropdown form');
                        src = src.replace('data-toggle', 'data-neos-toggle');

                        // Tooltip
                        src = src.replace(/in top bottom left right/g, 'neos-in neos-top neos-bottom neos-left neos-right');
                        src = src.replace(/\.addClass\(placement\)/g, ".addClass('neos-' + placement)");
                        src = src.replace('delay: 0', "delay: { 'show': 500, 'hide': 100 }");

                        // Popover
                        src = src.replace(/fade top bottom left right in/g, 'neos-fade neos-top neos-bottom neos-left neos-right neos-in');

                        // Datetimepicker
                        src = src.replace(/case '(switch|prev|next|today)'/g, "case 'neos-$1'");
                        src = src.replace(/'prev'/g, "'neos-prev'");
                        src = src.replace(/= ' (old|new|disabled|active|today)'/g, "= ' neos-$1'");
                        src = src.replace(/th\.today/g, 'th.neos-today');

                        // clean up the mess:
                        src = src.replace(/neos-neos/g, 'neos');

                        return src;
                    }
                }
            },

            select2: {
                src: [
                    libraryPath + 'select2/select2.js'
                ],
                dest: libraryPath + 'select2.js',
                options: {
                    process: function (src, filepath) {
                        src = src.replace(/window\.Select2/g, 'Utility.Select2');
                        src = src.replace(/select2-(dropdown-open|measure-scrollbar|choice|resizer|chosen|search-choice-close|arrow|focusser|offscreen|drop|display-none|search|input|results|no-results|selected|selection-limit|more-results|match|active|container-active|container|default|allowclear|with-searchbox|focused|sizer|result|disabled|highlighted|locked)/g, 'neos-select2-$1');

                        src = src.replace('if (this.indexOf("select2-") === 0) {', 'if (this.indexOf("neos-select2-") === 0) {');
                        src = src.replace('if (this.indexOf("select2-") !== 0) {', 'if (this.indexOf("neos-select2-") !== 0) {');

                        // make it work with position:fixed in the sidebar
                        src = src.replace('if (above) {', 'if (false) {');
                        src = src.replace('css.top = dropTop;', 'css.top = dropTop - $window.scrollTop();');

                        // add bootstrap icon-close
                        src = src.replace("<a href='#' onclick='return false;' class='neos-select2-search-choice-close' tabindex='-1'></a>", "<a href='#' onclick='return false;' class='neos-select2-search-choice-close'><i class='fas fa-times'></i></a>");
                        src = src.replace("<abbr class='neos-select2-search-choice-close'></abbr>", "<abbr class='neos-select2-search-choice-close'><i class='fas fa-times'></i></abbr>");

                        src = src.replace('this.body = thunk(function() { return opts.element.closest("body"); });', "this.body = thunk(function() { return opts.relative ? opts.element.parent() : $('#neos-application'); });");

                        return src;
                    }
                }
            },

            select2Css: {
                src: [
                    libraryPath + 'select2/select2.css'
                ],
                dest: libraryPath + 'select2/select2-prefixed.scss',
                options: {
                    banner: '/* This file is autogenerated using the Gruntfile.*/',
                    footer: '',
                    process: function (src, filepath) {
                        src = src.replace(/select2-(dropdown-open|measure-scrollbar|choice|resizer|chosen|search-choice-close|arrow|focusser|offscreen|drop|display-none|search|input|results|no-results|selected|selection-limit|more-results|match|active|container-active|container|default|allowclear|with-searchbox|focused|sizer|result|disabled|highlighted|locked)/g, 'neos-select2-$1');

                        src = src.replace(/url\('select2.png'\)/g, "url('../Library/select2/select2.png')");
                        src = src.replace(/url\('select2x2.png'\)/g, "url('../Library/select2/select2x2.png')");

                        return src;
                    }
                }
            },

            handlebars: {
                src: [
                    libraryPath + 'handlebars/handlebars-1.0.0.js'
                ],
                dest: libraryPath + 'handlebars.js',
                options: {
                    banner: 'define(function() {',
                    footer: '  return Handlebars;' +
                    '});'
                }
            },

            // This file needs jQueryWithDependencies first
            ember: {
                src: [
                    libraryPath + 'emberjs/ember-1.0.0.js',
                    libraryPath + 'ember-i18n/lib/i18n.js'
                ],
                dest: libraryPath + 'ember.js',
                options: {
                    banner: 'define(["Library/jquery-with-dependencies", "Library/handlebars", "Library/cldr"], function(jQuery, Handlebars, CLDR) {' +
                    '  CLDR.defaultLocale = window.T3Configuration.locale;' + // TODO: make configurable, as this is only used for plurals this is not highest prio (same behavior in cldr for most languages)
                    '  var Ember = {exports: {}};' +
                    '  var ENV = {LOG_VERSION: false};' +
                    '  Ember.imports = {jQuery: jQuery, Handlebars: Handlebars};' +
                        // TODO: window.T3 can be removed!
                    '  Ember.lookup = { Ember: Ember, T3: window.T3};' +
                    '  window.Ember = Ember;',
                    footer: '  return Ember;' +
                    '});',
                    process: function(src) {
                        src = src.replace('I18n.t(', 'I18n.translate(');
                        src = src.replace("Handlebars.registerHelper('t'", "Handlebars.registerHelper('translate'");
                        src = src.replace('t: function(key, context)', 'translate: function(key, context)');

                        return src;
                    }
                }
            },

            // This file needs jQueryWithDependencies first
            underscore: {
                src: [
                    libraryPath + 'vie/lib/underscoreJS/underscore.js'
                ],
                dest: libraryPath + 'underscore.js',
                options: {
                    banner: 'define(function() {' +
                    '  var root = {};' +
                    '  (function() {',
                    footer: '  }).apply(root);' +
                    '  return root._;' +
                    '});'
                }
            },

            backbone: {
                src: [
                    libraryPath + 'vie/lib/backboneJS/backbone.js'
                ],
                dest: libraryPath + 'backbone.js',
                options: {
                    banner: 'define(["Library/underscore", "Library/jquery-with-dependencies"], function(_, jQuery) {' +
                    '  var root = {_:_, jQuery:jQuery};' +
                    '  (function() {',
                    footer: '  }).apply(root);' +
                    '  return root.Backbone;' +
                    '});'
                }
            },

            vie: {
                src: [
                    libraryPath + 'vie/vie.js'
                ],
                dest: libraryPath + 'vie.js',
                options: {
                    banner: 'define(["Library/underscore", "Library/backbone", "Library/jquery-with-dependencies"], function(_, Backbone, jQuery) {' +
                    '  var root = {_:_, jQuery: jQuery, Backbone: Backbone};' +
                    '  (function() {',
                    footer: '  }).apply(root);' +
                    '  return root.VIE;' +
                    '});',
                    process: function(src) {
                        // Set "overrideAttributes" option when updating existing entities to prevent it from converting values into an array with old values
                        return src.replace('entityInstance = this.vie.entities.addOrUpdate(entityInstance, {', 'entityInstance = this.vie.entities.addOrUpdate(entityInstance, {overrideAttributes: true,');
                    }
                }
            },

            mousetrap: {
                src: [
                    libraryPath + 'mousetrap/mousetrap.js'
                ],
                dest: libraryPath + 'mousetrap.js'
            },

            create: {
                src: [
                    libraryPath + 'createjs/create.js'
                ],
                dest: libraryPath + 'create.js',
                options: {
                    banner: 'define(["Library/underscore", "Library/backbone", "Library/jquery-with-dependencies"], function(_, Backbone, jQuery) {',
                    footer: '});',
                    process: function(src, filepath) {
                        src = src.replace(/widget.options.autoSaveInterval/g, 200);
                        src = src.replace(
                            /[ ]*widget.saveRemoteAll\({(.|\n)*?}\);/i,

                            '        var version = widget.changedModels.length > 0 ? widget.changedModels[0].midgardStorageVersion : widget.changedModels.reduce(function(previousValue, currentValue) {' + "\n" +
                            '          return previousValue ? previousValue.midgardStorageVersion + currentValue.midgardStorageVersion : currentValue.midgardStorageVersion;' + "\n" +
                            '        });' + "\n" +
                            '        if (version !== currentVersion) {' + "\n" +
                            '          currentVersion = version;' + "\n" +
                            '          debouncedDoAutoSave();' + "\n" +
                            '        }'
                        );
                        src = src.replace(
                            '      var doAutoSave = function () {',

                            '      var throttledDoAutoSave = _.throttle(function() {' + "\n" +
                            '        widget.saveRemoteAll({' + "\n" +
                            '          // We make autosaves silent so that potential changes from server' + "\n" +
                            '          // don\'t disrupt user while writing.' + "\n" +
                            '          silent: true' + "\n" +
                            '        });' + "\n" +
                            '      }, widget.options.autoSaveInterval);' + "\n" +
                            '      var currentVersion;' + "\n" +
                            '      var debouncedDoAutoSave = _.debounce(function() {' + "\n" +
                            '        currentVersion = null;' + "\n" +
                            '        throttledDoAutoSave();' + "\n" +
                            '      }, 500);' + "\n" +
                            '      var doAutoSave = function () {'
                        );
                        return src;
                    }
                }
            },

            plupload: {
                src: [
                    libraryPath + 'plupload/js/plupload.js',
                    libraryPath + 'plupload/js/plupload.html5.js'
                ],
                dest: libraryPath + 'plupload.js',
                options: {
                    banner: 'define(["Library/jquery-with-dependencies"], function(jQuery) {',
                    // TODO: get rid of the global 'window.plupload'.
                    footer: '  return window.plupload;' +
                    '});'
                }
            },

            codemirror: {
                src: [
                    libraryPath + 'codemirror/lib/codemirror.js',
                    libraryPath + 'codemirror/mode/xml/xml.js',
                    libraryPath + 'codemirror/mode/css/css.js',
                    libraryPath + 'codemirror/mode/javascript/javascript.js',
                    libraryPath + 'codemirror/mode/htmlmixed/htmlmixed.js'
                ],
                dest: libraryPath + 'codemirror.js',
                options: {
                    banner: 'define(function() {',
                    footer: '  window.CodeMirror = CodeMirror;' +
                    '  return CodeMirror;' +
                    '});'
                }
            },

            xregexp: {
                src: [
                    libraryPath + 'XRegExp/xregexp.min.js'
                ],
                dest: libraryPath + 'xregexp.js',
                options: {
                    banner: 'define(function() {',
                    footer: '  return XRegExp;' +
                    '});'
                }
            },

            iso8601JsPeriod: {
                src: [
                    libraryPath + 'iso8601-js-period/iso8601.min.js'
                ],
                dest: libraryPath + 'iso8601-js-period.js',
                options: {
                    banner: 'define(function() {' +
                    'var iso8601JsPeriod = {};',
                    footer: '  return iso8601JsPeriod.iso8601;' +
                    '});',
                    process: function (src, filepath) {
                        return src.replace('window.nezasa=window.nezasa||{}', 'iso8601JsPeriod');
                    }
                }
            },

            toastr: {
                src: [
                    libraryPath + 'toastr/toastr.js'
                ],
                dest: libraryPath + 'toastr.js',
                options: {
                    process: function (src, filepath) {
                        src = src.replace('toast-close-button', 'neos-close-button');
                        src = src.replace("define(['jquery']", "define(['Library/jquery-with-dependencies']");
                        return src;
                    }
                }
            },

            nprogress: {
                src: [
                    libraryPath + 'nprogress/nprogress.js'
                ],
                dest: libraryPath + 'nprogress.js',
                options: {
                    process: function (src, filepath) {
                        src = src.replace("id='nprogress'", "id='neos-nprogress'");
                        src = src.replace(/\#nprogress/g, '#neos-nprogress');
                        src = src.replace('appendTo(document.body)', "appendTo('#neos-application')");
                        src = src.replace(/\.(add|remove)Class\('/g, ".$1Class('neos-");
                        src = src.replace("define(['jquery']", "define(['Library/jquery-with-dependencies']");
                        return src;
                    }
                }
            },

            sly: {
                src: [
                    libraryPath + 'sly/sly.js'
                ],
                dest: libraryPath + 'sly.js',
                options: {
                    process: function (src, filepath) {
                        src = src.replace('jQuery.', '$.');
                        return src;
                    }
                }
            },

            jQueryWithDependencies: {
                src: [
                    libraryPath + 'jquery/jquery-2.0.3.js',
                    libraryPath + 'jquery/jquery-migrate-1.2.1.js',
                    libraryPath + 'jquery-easing/jquery.easing.1.3.js',
                    libraryPath + 'jquery-ui/js/jquery-ui-1.10.4.custom.js',
                    libraryPath + 'jquery-cookie/jquery.cookie.js',
                    libraryPath + 'jquery-dynatree/js/jquery.dynatree.js',
                    libraryPath + 'chosen/chosen/chosen.jquery.js',
                    libraryPath + 'jcrop/js/jquery.Jcrop.js',
                    libraryPath + 'select2.js',
                    libraryPath + 'sly.js',
                    libraryPath + 'bootstrap-components.js'
                ],
                dest: libraryPath + 'jquery-with-dependencies.js',
                options: {
                    banner: 'define(["Shared/Utility"], function(Utility) {' + "\n",
                    footer: "\n"  + 'return jQuery.noConflict(true);' + "\n" + '});',
                    process: function(src, filepath) {
                        switch (filepath) {
                            case libraryPath + 'jquery/jquery-2.0.3.js':
                                // Replace call to define() in jquery which conflicts with the dependency resolution in r.js
                                src = src.replace('define( "jquery", [], function () { return jQuery; } );', 'jQuery.migrateMute = true;');
                                break;
                            case libraryPath + 'jquery-ui/js/jquery-ui-1.10.4.custom.js':
                                // Prevent conflict with Twitter Bootstrap
                                src += "$.widget.bridge('uitooltip', $.ui.tooltip);";
                                src += "$.widget.bridge('uibutton', $.ui.button);";
                                break;
                        }
                        return src;
                    }
                }
            }
        },

        cldr: {
            src: [
                libraryPath + 'ember-i18n/vendor/cldr-1.0.0.js'
            ],
            dest: libraryPath + 'cldr.js',
            options: {
                banner: 'define(function() {' +
                '  var root = {};' +
                '  (function() {',
                footer: '  }).apply(root);' +
                '  return root.CLDR;' +
                '});'
            }
        }
    });

    /**
     * SECTION: Convenience Helpers for documentation rendering.
     *
     * In order to render documents automatically:
     * - make sure you have installed Node.js / NPM
     * - make sure you have installed grunt-cli GLOBALLY "npm install -g grunt-cli"
     * - install all dependencies of this grunt file: "npm install"
     *
     * Exposed Targets:
     * - "grunt watch-docs": compile docs with OmniGraffle support as soon as they change
     * - "grunt build-docs": compile docs with OmniGraffle support
     */
    grunt.config.merge({
        watch: {
            documentation: {
                files: '../Documentation/**/*.rst',
                tasks: ['bgShell:compileDocumentation'],
                options: {
                    debounceDelay: 100,
                    nospawn: true
                }
            }
        },
        omnigraffle: {
            files: '../Documentation/IntegratorGuide/IntegratorDiagrams.graffle',
            tasks: ['docs'],
            options: {
                debounceDelay: 100,
                nospawn: true
            }
        },
        generatedDocumentationChanged: {
            files: '../Documentation/_make/build/html/**',
            tasks: ['_empty'],
            options: {
                livereload: true,
                debounceDelay: 100
            }
        },
        bgShell: {
            compileDocumentation: {
                cmd: 'cd ../Documentation/_make; make html',
                bg: false
            },
            compileOmnigraffle: {
                cmd: 'cd ../Documentation/IntegratorGuide; rm -Rf Diagrams/; osascript ../../Scripts/export_from_omnigraffle.scpt png `pwd`/IntegratorDiagrams.graffle `pwd`/Diagrams'
            }
        }
    });

    /**
     * Load and register tasks
     */
    require('matchdep').filter('grunt-*').forEach(grunt.loadNpmTasks);

    // Empty task
    grunt.registerTask('_empty', function () {});

    /**
     * Build commands for execution in the build pipeline
     */
    grunt.registerTask('build', ['build-js', 'build-css']);
    grunt.registerTask('build-js', ['compile-js', 'requirejs:compile']);
    grunt.registerTask('build-css', ['compile-css', 'concat:css']);
    grunt.registerTask('build-docs', ['bgShell:compileOmnigraffle', 'bgShell:compileDocumentation']);

    /**
     * Compile commands for development context
     */
    grunt.registerTask('compile', ['compile-js', 'compile-css']);
    grunt.registerTask('compile-js', function() {
        grunt.util._.forEach(grunt.config.get().concat, function(taskConfiguration, taskName) {
            if (taskName !== 'css') {
                grunt.task.run('concat:' + taskName);
                grunt.task.run('trimtrailingspaces:js');
            }
        });
    });
    grunt.registerTask('compile-css', ['compass:compile']);

    /**
     * Watch commands
     */
    grunt.registerTask('watch-js', function() { console.log('JavaScript sources are loaded by requirejs. Use the setting "Neos.Neos.userInterface.loadMinifiedJavascript: FALSE" instead')});
    grunt.registerTask('watch-css', ['watch:css']);
    grunt.registerTask('watch-docs', ['watch:documentation']);

    /**
     * Testing commands
     */
    grunt.registerTask('test', ['qunit']);
};