<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2010 elements.at New Media Solutions GmbH (http://www.elements.at)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.tags.image");
pimcore.document.tags.image = Class.create(pimcore.document.tag, {

    initialize: function(id, name, options, data) {
        this.id = id;
        this.name = name;
        this.datax = new Object();

        if (!options) {
            options = {};
        }

        this.options = options;

        this.originalDimensions = {
            width: this.options.width,
            height: this.options.height
        };

        // set width & height
        /*if(!this.options.width) {
         this.options.width = 100;
         }*/
        if (!this.options.height) {
            this.options.height = 100;
        }

        if (data) {
            this.datax = data;
        }

        this.setupWrapper();

        this.options.name = id + "_editable";
        this.element = new Ext.Panel(this.options);

        this.element.on("render", function (el) {

            // contextmenu
            el.getEl().on("contextmenu", this.onContextMenu.bind(this));

            var domElement = el.getEl().dom;
            domElement.dndOver = false;

            domElement.reference = this;

            dndZones.push(domElement);
            el.getEl().on("mouseover", function (e) {
                this.dndOver = true;
            }.bind(domElement));
            el.getEl().on("mouseout", function (e) {
                this.dndOver = false;
            }.bind(domElement));

            el.getEl().setStyle({
                position: "relative"
            });

            // alt / title
            this.altBar = document.createElement("div");
            this.getBody().appendChild(this.altBar);

            this.altBar = Ext.get(this.altBar);
            this.altBar.addClass("pimcore_tag_image_alt");
            this.altBar.setStyle({
                opacity: 0.8,
                display: "none"
            });

            this.altInput = new Ext.form.TextField({
                name: "altText",
                width: this.options.width
            });
            this.altInput.render(this.altBar);

            if (this.datax.alt) {
                this.altInput.setValue(this.datax.alt);
            }

            if (this.options.hidetext == true) {
                this.altBar.setStyle({
                    display: "none",
                    visibility: "hidden"
                });
            }

            this.getBody().insertHtml("beforeEnd",'

');

            this.getBody().addClass("pimcore_tag_image_empty");

        }.bind(this));

        this.element.render(id);

        // insert image
        if (this.datax) {
            this.updateImage();
        }
    },

    onContextMenu: function (e) {

        var menu = new Ext.menu.Menu();

        if(this.datax.id) {
            menu.add(new Ext.menu.Item({
                text: t('edit'),
                iconCls: "pimcore_icon_edit",
                handler: function (item) {
                    item.parentMenu.destroy();

                    this.openEditWindow();
                }.bind(this)
            }));
        }

        menu.add(new Ext.menu.Item({
            text: t('empty'),
            iconCls: "pimcore_icon_delete",
            handler: function (item) {
                item.parentMenu.destroy();

                this.empty();

            }.bind(this)
        }));
        menu.add(new Ext.menu.Item({
            text: t('open'),
            iconCls: "pimcore_icon_open",
            handler: function (item) {
                item.parentMenu.destroy();
                pimcore.helpers.openAsset(this.datax.id, "image");
            }.bind(this)
        }));

        menu.add(new Ext.menu.Item({
            text: t('search'),
            iconCls: "pimcore_icon_search",
            handler: function (item) {
                item.parentMenu.destroy();

                this.openSearchEditor();
            }.bind(this)
        }));

        menu.showAt(e.getXY());
        e.stopEvent();
    },

    onNodeOver: function(target, dd, e, data) {
        if (this.dndAllowed(data)) {
            return Ext.dd.DropZone.prototype.dropAllowed;
        }
        else {
            return Ext.dd.DropZone.prototype.dropNotAllowed;
        }
    },

    onNodeDrop: function (target, dd, e, data) {

        if (data.node.attributes.type == "image") {
            this.resetData();
            this.datax.id = data.node.attributes.id;

            this.updateImage();
            this.reloadDocument();

            return true;
        }
    },

    dndAllowed: function(data) {

        if(data.node.attributes.elementType!="asset" || data.node.attributes.type!="image"){
            return false;
        } else return true;

    },

    openSearchEditor: function () {
        pimcore.helpers.itemselector(false, this.addDataFromSelector.bind(this), {
            type: ["asset"],
            subtype: {
                asset: ["image"]
            }
        });
    },

    addDataFromSelector: function (item) {
        if(item) {
            this.resetData();
            this.datax.id = item.id;

            this.updateImage();
            this.reloadDocument();

            return true;
        }
    },

    resetData: function () {
        this.datax = {
            id: null
        };
    },

    empty: function () {

        this.resetData();

        this.updateImage();
        this.getBody().addClass("pimcore_tag_image_empty");
        this.altBar.setStyle({
            display: "none"
        });
        this.reloadDocument();
    },

    getBody: function () {
        // get the id from the body element of the panel because there is no method to set body's html (only in configure)
        var body = Ext.get(this.element.getEl().query(".x-panel-body")[0]);
        return body;
    },

    updateImage: function () {

        var path = "";
        var existingImage = this.getBody().dom.getElementsByTagName("img")[0];
        if (existingImage) {
            Ext.get(existingImage).remove();
        }

        if (!this.datax.id) {
            return;
        }

        if (!this.options.thumbnail) {
            path = "/admin/asset/get-image-thumbnail/id/" + this.datax.id + "/width/" + this.element.getEl().getWidth() + "/aspectratio/true?" + Ext.urlEncode(this.datax);
        }
        else {
            if (typeof this.options.thumbnail == "string") {
                path = "/admin/asset/get-image-thumbnail/id/" + this.datax.id + "/thumbnail/" + this.options.thumbnail + "?" + Ext.urlEncode(this.datax);
            }
            else if (this.options.thumbnail.width || this.options.thumbnail.height) {
                path = "/admin/asset/get-image-thumbnail/id/" + this.datax.id + "/width/" + this.options.thumbnail.width + "/height/" + this.options.thumbnail.height + "?" + Ext.urlEncode(this.datax);
            }
        }

        var image = document.createElement("img");
        image.src = path;

        this.getBody().appendChild(image);

        // show alt input field
        this.altBar.setStyle({
            display: "block"
        });

        this.getBody().removeClass("pimcore_tag_image_empty");

        this.updateCounter = 0;
        this.updateDimensionsInterval = window.setInterval(this.updateDimensions.bind(this), 1000);
    },

    reloadDocument : function () {
        if (this.options.reload) {
            editWindow.reload();
        }
    },

    updateDimensions: function () {

        var image = this.element.getEl().dom.getElementsByTagName("img")[0];
        if (!image) {
            return;
        }
        image = Ext.get(image);

        var width = image.getWidth();
        var height = image.getHeight();

        if (width > 1 && height > 1) {

            var dimensionError = false;
            if(typeof this.options.minWidth != "undefined") {
                if(width < this.options.minWidth) {
                    dimensionError = true;
                }
            }
            if(typeof this.options.minHeight != "undefined") {
                if(height < this.options.minHeight) {
                    dimensionError = true;
                }
            }

            if(dimensionError) {
                this.empty();
                clearInterval(this.updateDimensionsInterval);

                Ext.MessageBox.alert(t("error"), t("image_is_too_small"));

                return;
            }

            if (typeof this.originalDimensions.width == "undefined") {
                this.element.setWidth(width);
            }
            if (typeof this.originalDimensions.height == "undefined") {
                this.element.setHeight(height);
            }

            this.altInput.setWidth(width);

            // show alt input field
            this.altBar.setStyle({
                display: "block"
            });
        }
        else {
            this.altBar.setStyle({
                display: "none"
            });
        }

        if (this.updateCounter > 20) {
            // only wait 20 seconds until image must be loaded
            clearInterval(this.updateDimensionsInterval);
        }

        this.updateCounter++;
    },

    openEditWindow: function() {

        var imageUrl = '/admin/asset/get-image-thumbnail/id/' + this.datax.id + '/width/500/height/400/contain/true';

        this.editWindow = new Ext.Window({
            width: 500,
            height: 400,
            modal: true,
            closeAction: "close",
            resizable: false,
            bodyStyle: "background: url(" + imageUrl + ") center center no-repeat;",
            bbar: ["->", {
                xtype: "button",
                iconCls: "pimcore_icon_apply",
                text: t("save"),
                handler: function () {

                    var originalWidth = this.editWindow.getInnerWidth();
                    var originalHeight = this.editWindow.getInnerHeight();

                    var dimensions = Ext.get("selector").getStyles("top","left","width","height");

                    var newWidth = intval(dimensions.width);
                    var newHeight = intval(dimensions.height);
                    var top = intval(dimensions.top);
                    var left = intval(dimensions.left);

                    this.datax.cropWidth = newWidth * 100 / originalWidth;
                    this.datax.cropHeight = newHeight * 100 / originalHeight;
                    this.datax.cropTop = top * 100 / originalHeight;
                    this.datax.cropLeft = left * 100 / originalWidth;
                    this.datax.cropPercent = true;

                    this.resizer = null;
                    this.editWindow.close();

                    this.updateImage();
                }.bind(this)
            }],
            html: '

'
        });

        this.editWindow.on("afterrender", function ( ){
            this.editWindowInterval = window.setInterval(function () {
                var el = Ext.get("selectorImage");
                if(el) {
                    if(el.getWidth() > 10) {
                        clearInterval(this.editWindowInterval);
                        this.editWindow.setSize(el.getWidth() + 14, el.getHeight() + 32 + 27);
                        Ext.get("selectorImage").remove();

                        this.resizer = new Ext.Resizable('selector', {
                            pinned:true,
                            minWidth:50,
                            minHeight: 50,
                            preserveRatio: false,
                            dynamic:true,
                            handles: 'all',
                            draggable:true,
                            width: 100,
                            height: 100
                        });
                    }
                }
            }.bind(this), 500);

        }.bind(this));

        this.editWindow.show();
    },

    getValue: function () {

        // alt alt value
        this.datax.alt = this.altInput.getValue();

        return this.datax;
    },

    getType: function () {
        return "image";
    }
});<commit_msg><commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2010 elements.at New Media Solutions GmbH (http://www.elements.at)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.document.tags.image");
pimcore.document.tags.image = Class.create(pimcore.document.tag, {

    initialize: function(id, name, options, data) {
        this.id = id;
        this.name = name;
        this.datax = new Object();

        if (!options) {
            options = {};
        }

        this.options = options;

        this.originalDimensions = {
            width: this.options.width,
            height: this.options.height
        };

        // set width & height
        /*if(!this.options.width) {
         this.options.width = 100;
         }*/
        if (!this.options.height) {
            this.options.height = 100;
        }

        if (data) {
            this.datax = data;
        }

        this.setupWrapper();

        this.options.name = id + "_editable";
        this.element = new Ext.Panel(this.options);

        this.element.on("render", function (el) {

            // contextmenu
            el.getEl().on("contextmenu", this.onContextMenu.bind(this));

            var domElement = el.getEl().dom;
            domElement.dndOver = false;

            domElement.reference = this;

            dndZones.push(domElement);
            el.getEl().on("mouseover", function (e) {
                this.dndOver = true;
            }.bind(domElement));
            el.getEl().on("mouseout", function (e) {
                this.dndOver = false;
            }.bind(domElement));

            el.getEl().setStyle({
                position: "relative"
            });

            // alt / title
            this.altBar = document.createElement("div");
            this.getBody().appendChild(this.altBar);

            this.altBar = Ext.get(this.altBar);
            this.altBar.addClass("pimcore_tag_image_alt");
            this.altBar.setStyle({
                opacity: 0.8,
                display: "none"
            });

            this.altInput = new Ext.form.TextField({
                name: "altText",
                width: this.options.width
            });
            this.altInput.render(this.altBar);

            if (this.datax.alt) {
                this.altInput.setValue(this.datax.alt);
            }

            if (this.options.hidetext == true) {
                this.altBar.setStyle({
                    display: "none",
                    visibility: "hidden"
                });
            }

            this.getBody().insertHtml("beforeEnd",'

');

            this.getBody().addClass("pimcore_tag_image_empty");

        }.bind(this));

        this.element.render(id);

        // insert image
        if (this.datax) {
            this.updateImage();
        }
    },

    onContextMenu: function (e) {

        var menu = new Ext.menu.Menu();

        if(this.datax.id) {
            menu.add(new Ext.menu.Item({
                text: t('edit'),
                iconCls: "pimcore_icon_edit",
                handler: function (item) {
                    item.parentMenu.destroy();

                    this.openEditWindow();
                }.bind(this)
            }));
        }

        menu.add(new Ext.menu.Item({
            text: t('empty'),
            iconCls: "pimcore_icon_delete",
            handler: function (item) {
                item.parentMenu.destroy();

                this.empty();

            }.bind(this)
        }));
        menu.add(new Ext.menu.Item({
            text: t('open'),
            iconCls: "pimcore_icon_open",
            handler: function (item) {
                item.parentMenu.destroy();
                pimcore.helpers.openAsset(this.datax.id, "image");
            }.bind(this)
        }));

        menu.add(new Ext.menu.Item({
            text: t('search'),
            iconCls: "pimcore_icon_search",
            handler: function (item) {
                item.parentMenu.destroy();

                this.openSearchEditor();
            }.bind(this)
        }));

        menu.showAt(e.getXY());
        e.stopEvent();
    },

    onNodeOver: function(target, dd, e, data) {
        if (this.dndAllowed(data)) {
            return Ext.dd.DropZone.prototype.dropAllowed;
        }
        else {
            return Ext.dd.DropZone.prototype.dropNotAllowed;
        }
    },

    onNodeDrop: function (target, dd, e, data) {

        if (data.node.attributes.type == "image") {
            this.resetData();
            this.datax.id = data.node.attributes.id;

            this.updateImage();
            this.reloadDocument();

            return true;
        }
    },

    dndAllowed: function(data) {

        if(data.node.attributes.elementType!="asset" || data.node.attributes.type!="image"){
            return false;
        } else return true;

    },

    openSearchEditor: function () {
        pimcore.helpers.itemselector(false, this.addDataFromSelector.bind(this), {
            type: ["asset"],
            subtype: {
                asset: ["image"]
            }
        });
    },

    addDataFromSelector: function (item) {
        if(item) {
            this.resetData();
            this.datax.id = item.id;

            this.updateImage();
            this.reloadDocument();

            return true;
        }
    },

    resetData: function () {
        this.datax = {
            id: null
        };
    },

    empty: function () {

        this.resetData();

        this.updateImage();
        this.getBody().addClass("pimcore_tag_image_empty");
        this.altBar.setStyle({
            display: "none"
        });
        this.reloadDocument();
    },

    getBody: function () {
        // get the id from the body element of the panel because there is no method to set body's html (only in configure)
        var body = Ext.get(this.element.getEl().query(".x-panel-body")[0]);
        return body;
    },

    updateImage: function () {

        var path = "";
        var existingImage = this.getBody().dom.getElementsByTagName("img")[0];
        if (existingImage) {
            Ext.get(existingImage).remove();
        }

        if (!this.datax.id) {
            return;
        }

        if (!this.options.thumbnail) {
            path = "/admin/asset/get-image-thumbnail/id/" + this.datax.id + "/width/" + this.element.getEl().getWidth() + "/aspectratio/true?" + Ext.urlEncode(this.datax);
        }
        else {
            if (typeof this.options.thumbnail == "string") {
                path = "/admin/asset/get-image-thumbnail/id/" + this.datax.id + "/thumbnail/" + this.options.thumbnail + "?" + Ext.urlEncode(this.datax);
            }
            else if (this.options.thumbnail.width || this.options.thumbnail.height) {
                path = "/admin/asset/get-image-thumbnail/id/" + this.datax.id + "/width/" + this.options.thumbnail.width + "/height/" + this.options.thumbnail.height + "?" + Ext.urlEncode(this.datax);
            }
        }

        var image = document.createElement("img");
        image.src = path;

        this.getBody().appendChild(image);

        // show alt input field
        this.altBar.setStyle({
            display: "block"
        });

        this.getBody().removeClass("pimcore_tag_image_empty");

        this.updateCounter = 0;
        this.updateDimensionsInterval = window.setInterval(this.updateDimensions.bind(this), 1000);
    },

    reloadDocument : function () {
        if (this.options.reload) {
            editWindow.reload();
        }
    },

    updateDimensions: function () {

        var image = this.element.getEl().dom.getElementsByTagName("img")[0];
        if (!image) {
            return;
        }
        image = Ext.get(image);

        var width = image.getWidth();
        var height = image.getHeight();

        if (width > 1 && height > 1) {

            var dimensionError = false;
            if(typeof this.options.minWidth != "undefined") {
                if(width < this.options.minWidth) {
                    dimensionError = true;
                }
            }
            if(typeof this.options.minHeight != "undefined") {
                if(height < this.options.minHeight) {
                    dimensionError = true;
                }
            }

            if(dimensionError) {
                this.empty();
                clearInterval(this.updateDimensionsInterval);

                Ext.MessageBox.alert(t("error"), t("image_is_too_small"));

                return;
            }

            if (typeof this.originalDimensions.width == "undefined") {
                this.element.setWidth(width);
            }
            if (typeof this.originalDimensions.height == "undefined") {
                this.element.setHeight(height);
            }

            this.altInput.setWidth(width);

            // show alt input field
            this.altBar.setStyle({
                display: "block"
            });
        }
        else {
            this.altBar.setStyle({
                display: "none"
            });
        }

        if (this.updateCounter > 20) {
            // only wait 20 seconds until image must be loaded
            clearInterval(this.updateDimensionsInterval);
        }

        this.updateCounter++;
    },

    openEditWindow: function() {

        var imageUrl = '/admin/asset/get-image-thumbnail/id/' + this.datax.id + '/width/500/height/400/contain/true';

        this.editWindow = new Ext.Window({
            width: 500,
            height: 400,
            modal: true,
            closeAction: "close",
            resizable: false,
            bodyStyle: "background: url(" + imageUrl + ") center center no-repeat;",
            bbar: ["->", {
                xtype: "button",
                iconCls: "pimcore_icon_apply",
                text: t("save"),
                handler: function () {

                    var originalWidth = this.editWindow.getInnerWidth();
                    var originalHeight = this.editWindow.getInnerHeight();

                    var dimensions = Ext.get("selector").getStyles("top","left","width","height");

                    var newWidth = intval(dimensions.width);
                    var newHeight = intval(dimensions.height);
                    var top = intval(dimensions.top);
                    var left = intval(dimensions.left);

                    this.datax.cropWidth = newWidth * 100 / originalWidth;
                    this.datax.cropHeight = newHeight * 100 / originalHeight;
                    this.datax.cropTop = top * 100 / originalHeight;
                    this.datax.cropLeft = left * 100 / originalWidth;
                    this.datax.cropPercent = true;

                    this.resizer = null;
                    this.editWindow.close();

                    this.updateImage();
                }.bind(this)
            }],
            html: '

'
        });

        this.editWindow.on("afterrender", function ( ){
            this.editWindowInterval = window.setInterval(function () {
                var el = Ext.get("selectorImage");
                var imageWidth = el.getWidth();
                var imageHeight = el.getHeight();

                if(el) {
                    if(el.getWidth() > 10) {
                        clearInterval(this.editWindowInterval);
                        this.editWindow.setSize(imageWidth + 14, imageHeight + 32 + 27);
                        Ext.get("selectorImage").remove();

                        this.resizer = new Ext.Resizable('selector', {
                            pinned:true,
                            minWidth:50,
                            minHeight: 50,
                            preserveRatio: false,
                            dynamic:true,
                            handles: 'all',
                            draggable:true,
                            width: 100,
                            height: 100
                        });

                        if(this.datax.cropPercent) {
                            Ext.get("selector").applyStyles({
                                width: (imageWidth * (this.datax.cropWidth / 100)) + "px",
                                height: (imageHeight * (this.datax.cropHeight / 100)) + "px",
                                top: (imageHeight * (this.datax.cropTop / 100)) + "px",
                                left: (imageWidth * (this.datax.cropLeft / 100)) + "px",
                            });
                        }
                    }
                }
            }.bind(this), 500);

        }.bind(this));

        this.editWindow.show();
    },

    getValue: function () {

        // alt alt value
        this.datax.alt = this.altInput.getValue();

        return this.datax;
    },

    getType: function () {
        return "image";
    }
});<|endoftext|>


<commit_before>/* jshint node:true */
"use strict";

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),

    watch: {
      sass: {
        files: ["scss/**/*.{scss,sass}"],
        tasks: ["sass:dev"]
      },
      js: {
        files: ["js/vendor/**/*.js", "js/**/*.js", "js-app/**/*.js", "tests/**/*.js"],
        tasks: ["jshint:all", "uglify:dev"]
      },
      livereload: {
        files: ["*.html", "assets/**/*.{js,json}", "assets/images/**/*.{png,jpg,jpeg,gif,webp,svg}"],
        options: {
          livereload: true
        }
      }
    },

    sass: {
      compile: {
        files: {
          "tmp/neue.css": "scss/neue.scss",
          "assets/neue.dev.css": "scss/neue.dev.scss",
          "assets/ie.css": "scss/ie.scss"
        },
        options: {
          sourceComments: "normal"
        }
      }
    },

    cssmin: {
      minify: {
        options: {
          report: 'gzip'
        },
        files: {
          "assets/neue.css": ["tmp/neue.css"],
          "assets/neue.dev.css": ["assets/neue.dev.css"],
          "assets/ie.css": ["assets/ie.css"]
        }
      }
    },

    jshint: {
      options: {
        force: true,
        jshintrc: true,
        reporter: require("jshint-stylish")
      },
      all: ["js/**/*.js", "js-app/**/*.js", "!js/vendor/**/*.js", "tests/**/*.js", "!tests/wraith/**/*.js", "!tests/lib/**/*.js"]
    },

    qunit: {
      all: ["tests/*.html"]
    },

    uglify: {
      prod: {
        options: {
          report: "gzip"
        },
        files: {
          "assets/neue.js": ["js/vendor/*.js", "js/**/*.js", "!js/_*.js"],
          "assets/app.js": ["js-app/**/*.js", "!js-app/_*.js"]
        }
      },
      dev: {
        options: {
          mangle: false,
          compress: false,
          beautify: true
        },
        files: {
          "assets/neue.js": ["js/vendor/*.js", "js/**/*.js", "!js/_*.js"],
          "assets/app.js": ["js-app/**/*.js", "!js-app/_*.js"]
        }
      }
    },

    docco: {
      docs: {
        src: ["js/*.js"],
        options: {
          output: "assets/docs"
        }
      }
    },

    bump: {
      options: {
        pushTo: "origin"
      }
    },

    shell: {
      wraith: {
        command: 'cd tests/wraith && rake && open tests/wraith/ds/gallery.html',
        options: {
          stdout: true
        }
      },
      scsslint: {
        command: 'scss-lint scss/ --config .scss-lint.yaml',
        options: {
          stdout: true
        }
      }
    }

  });

  // dev build tasks
  grunt.registerTask("default", ["build", "watch"]);
  grunt.registerTask("test", ["test:css", "test:js"]);
  grunt.registerTask("test:css", ["shell:wraith"]);
  grunt.registerTask("test:js", ["qunit"]);

  grunt.registerTask("build", ["sass:compile", "jshint:all", "shell:scsslint", "uglify:dev"]);

  // run this before pushing code to master – minifies css/js
  grunt.registerTask("prod", ["sass:compile", "cssmin:minify", "shell:scsslint", "jshint:all", "uglify:prod", "qunit", "docco"]);

  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks("grunt-contrib-jshint");
  grunt.loadNpmTasks("grunt-contrib-qunit");
  grunt.loadNpmTasks("grunt-contrib-uglify");
  grunt.loadNpmTasks("grunt-contrib-watch");
  grunt.loadNpmTasks("grunt-docco2");
  grunt.loadNpmTasks("grunt-bump");
  grunt.loadNpmTasks('grunt-shell');
};
<commit_msg>Fixed grunt watch task for compiling CSS.<commit_after>/* jshint node:true */
"use strict";

module.exports = function(grunt) {
  grunt.initConfig({
    pkg: grunt.file.readJSON("package.json"),

    watch: {
      sass: {
        files: ["scss/**/*.{scss,sass}"],
        tasks: ["sass:compile"]
      },
      js: {
        files: ["js/vendor/**/*.js", "js/**/*.js", "js-app/**/*.js", "tests/**/*.js"],
        tasks: ["jshint:all", "uglify:dev"]
      },
      livereload: {
        files: ["*.html", "assets/**/*.{js,json}", "assets/images/**/*.{png,jpg,jpeg,gif,webp,svg}"],
        options: {
          livereload: true
        }
      }
    },

    sass: {
      compile: {
        files: {
          "tmp/neue.css": "scss/neue.scss",
          "assets/neue.dev.css": "scss/neue.dev.scss",
          "assets/ie.css": "scss/ie.scss"
        },
        options: {
          sourceComments: "normal"
        }
      }
    },

    cssmin: {
      minify: {
        options: {
          report: 'gzip'
        },
        files: {
          "assets/neue.css": ["tmp/neue.css"],
          "assets/neue.dev.css": ["assets/neue.dev.css"],
          "assets/ie.css": ["assets/ie.css"]
        }
      }
    },

    jshint: {
      options: {
        force: true,
        jshintrc: true,
        reporter: require("jshint-stylish")
      },
      all: ["js/**/*.js", "js-app/**/*.js", "!js/vendor/**/*.js", "tests/**/*.js", "!tests/wraith/**/*.js", "!tests/lib/**/*.js"]
    },

    qunit: {
      all: ["tests/*.html"]
    },

    uglify: {
      prod: {
        options: {
          report: "gzip"
        },
        files: {
          "assets/neue.js": ["js/vendor/*.js", "js/**/*.js", "!js/_*.js"],
          "assets/app.js": ["js-app/**/*.js", "!js-app/_*.js"]
        }
      },
      dev: {
        options: {
          mangle: false,
          compress: false,
          beautify: true
        },
        files: {
          "assets/neue.js": ["js/vendor/*.js", "js/**/*.js", "!js/_*.js"],
          "assets/app.js": ["js-app/**/*.js", "!js-app/_*.js"]
        }
      }
    },

    docco: {
      docs: {
        src: ["js/*.js"],
        options: {
          output: "assets/docs"
        }
      }
    },

    bump: {
      options: {
        pushTo: "origin"
      }
    },

    shell: {
      wraith: {
        command: 'cd tests/wraith && rake && open tests/wraith/ds/gallery.html',
        options: {
          stdout: true
        }
      },
      scsslint: {
        command: 'scss-lint scss/ --config .scss-lint.yaml',
        options: {
          stdout: true
        }
      }
    }

  });

  // dev build tasks
  grunt.registerTask("default", ["build", "watch"]);
  grunt.registerTask("test", ["test:css", "test:js"]);
  grunt.registerTask("test:css", ["shell:wraith"]);
  grunt.registerTask("test:js", ["qunit"]);

  grunt.registerTask("build", ["sass:compile", "jshint:all", "shell:scsslint", "uglify:dev"]);

  // run this before pushing code to master – minifies css/js
  grunt.registerTask("prod", ["sass:compile", "cssmin:minify", "shell:scsslint", "jshint:all", "uglify:prod", "qunit", "docco"]);

  grunt.loadNpmTasks('grunt-sass');
  grunt.loadNpmTasks('grunt-contrib-cssmin');
  grunt.loadNpmTasks("grunt-contrib-jshint");
  grunt.loadNpmTasks("grunt-contrib-qunit");
  grunt.loadNpmTasks("grunt-contrib-uglify");
  grunt.loadNpmTasks("grunt-contrib-watch");
  grunt.loadNpmTasks("grunt-docco2");
  grunt.loadNpmTasks("grunt-bump");
  grunt.loadNpmTasks('grunt-shell');
};
<|endoftext|>


<commit_before>describe("Cosmos.Router", function() {

  var jsdom = require('jsdom');

  // jsdom creates a fresh new window object for every test case and React needs
  // to be required *after* the window and document globals are available. The
  // var references however must be declared globally in order to be accessible
  // in test cases as well.
  var React,
      utils,
      Cosmos;

  beforeEach(function() {
    global.window = jsdom.jsdom().createWindow('');
    global.document = global.window.document;
    global.navigator = global.window.navigator;
    // We're mocking the URL query string of the window
    global.window.location = {search: '?component=List&data=users.json'};

    React = require('react/addons');
    utils = React.addons.TestUtils;
    Cosmos = require('../build/cosmos.js');

    // Ignore out of scope methods
    spyOn(Cosmos.Router.prototype, '_bindPopStateEvent');
    spyOn(Cosmos.Router.prototype, '_replaceHistoryState');
    spyOn(Cosmos.Router.prototype, '_pushHistoryState');
    spyOn(Cosmos.url, 'isPushStateSupported').and.returnValue(true);
  });

  describe("new instance", function() {

      beforeEach(function() {
        spyOn(Cosmos, 'render');
      });

      it("should default to URL query string", function() {
        var router = new Cosmos.Router();

        expect(router.options.props).toEqual(Cosmos.url.getParams());
      });

      it("shouldn't use default props when props aren't empty", function() {
        var router = new Cosmos.Router({defaultProps: {
          component: 'DefaultComponent'
        }});

        expect(router.options.props).toEqual(Cosmos.url.getParams());
      });

      it("should default to document.body as container", function() {
        var router = new Cosmos.Router();

        expect(router.options.container).toBe(document.body);
      });

      it("should save a reference to the DOM container", function() {
        var container = React.DOM.span();
            router = new Cosmos.Router({container: ''});

        expect(router.container).toEqual('');
      });
  });

  describe("should render new Components", function() {

    beforeEach(function() {
      spyOn(Cosmos, 'render');
    });

    it("with constructor props and container", function() {
      var router = new Cosmos.Router({
        props: {component: 'List', dataUrl: 'users.json'},
        container: ''
      });

      expect(Cosmos.render.calls.count()).toEqual(1);
      expect(Cosmos.render.calls.mostRecent().args[0]).toEqual({
        component: 'List', dataUrl: 'users.json'
      });
      expect(Cosmos.render.calls.mostRecent().args[1]).toEqual('');
    });

    it("with props extracted from query string on .goTo", function() {
      var router = new Cosmos.Router({});
      router.goTo('?component=List&dataUrl=users.json');

      expect(Cosmos.render.calls.count()).toEqual(2);
      expect(Cosmos.render.calls.mostRecent().args[0]).toEqual({
        component: 'List',
        dataUrl: 'users.json'
      });
    });

    it("with props from event state on PopState event", function() {
      var router = new Cosmos.Router({});
      router._onPopState({
        state: {
          component: 'List',
          dataUrl: 'users.json'
        }
      });

      expect(Cosmos.render.calls.count()).toEqual(2);
      expect(Cosmos.render.calls.mostRecent().args[0]).toEqual({
        component: 'List',
        dataUrl: 'users.json'
      });
    });
  });

  it("should cache latest snapshot of previous Component", function() {
    var ComponentClass = React.createClass({
          mixins: [Cosmos.mixins.PersistState],
          render: function() {
            return React.DOM.span();
          }
        }),
        props = {component: 'List', dataUrl: 'users.json'},
        componentElement = React.createElement(ComponentClass, props)
        componentInstance = utils.renderIntoDocument(componentElement);

    // We just want a valid instance to work with, the Router props won't be
    // taken into consideration
    spyOn(Cosmos, 'render').and.callFake(function(props) {
      return componentInstance;
    });

    var router = new Cosmos.Router();
    // We alter the current instance while it's bound to the current history
    // entry
    componentInstance.setProps({dataUrl: null, someNumber: 555});
    componentInstance.setState({amIState: true});

    // Before routing to a new Component configuration, the previous one
    // shouldn't been updated with our changes
    router.goTo('?component=User&dataUrl=user.json');

    expect(router._replaceHistoryState.calls.count()).toEqual(1);
    expect(router._replaceHistoryState.calls.mostRecent().args[0]).toEqual({
      component: 'List',
      dataUrl: null,
      someNumber: 555,
      state: {
        amIState: true
      }
    });
  });
});
<commit_msg>Mock external module instead #86<commit_after>describe("Cosmos.Router", function() {

  var jsdom = require('jsdom');

  // jsdom creates a fresh new window object for every test case and React needs
  // to be required *after* the window and document globals are available. The
  // var references however must be declared globally in order to be accessible
  // in test cases as well.
  var React,
      utils,
      Cosmos;

  beforeEach(function() {
    global.window = jsdom.jsdom().createWindow('');
    global.document = global.window.document;
    global.navigator = global.window.navigator;

    React = require('react/addons');
    utils = React.addons.TestUtils;
    Cosmos = require('../build/cosmos.js');

    // Ignore external methods
    spyOn(Cosmos.Router.prototype, '_bindPopStateEvent');
    spyOn(Cosmos.Router.prototype, '_replaceHistoryState');
    spyOn(Cosmos.Router.prototype, '_pushHistoryState');
    spyOn(Cosmos.url, 'isPushStateSupported').and.returnValue(true);

    // The Cosmos.url lib is already tested in isolation
    spyOn(Cosmos.url, 'getParams').and.returnValue({
      component: 'List',
      data: 'users.json'
    });
  });

  describe("new instance", function() {

      beforeEach(function() {
        spyOn(Cosmos, 'render');
      });

      it("should default to URL query string", function() {
        var router = new Cosmos.Router();

        expect(router.options.props).toEqual(Cosmos.url.getParams());
      });

      it("shouldn't use default props when props aren't empty", function() {
        var router = new Cosmos.Router({defaultProps: {
          component: 'DefaultComponent'
        }});

        expect(router.options.props).toEqual(Cosmos.url.getParams());
      });

      it("should default to document.body as container", function() {
        var router = new Cosmos.Router();

        expect(router.options.container).toBe(document.body);
      });

      it("should save a reference to the DOM container", function() {
        var container = React.DOM.span();
            router = new Cosmos.Router({container: ''});

        expect(router.container).toEqual('');
      });
  });

  describe("should render new Components", function() {

    beforeEach(function() {
      spyOn(Cosmos, 'render');
    });

    it("with constructor props and container", function() {
      var router = new Cosmos.Router({
        props: {component: 'List', dataUrl: 'users.json'},
        container: ''
      });

      expect(Cosmos.render.calls.count()).toEqual(1);
      expect(Cosmos.render.calls.mostRecent().args[0]).toEqual({
        component: 'List', dataUrl: 'users.json'
      });
      expect(Cosmos.render.calls.mostRecent().args[1]).toEqual('');
    });

    it("with props extracted from query string on .goTo", function() {
      var router = new Cosmos.Router({});
      router.goTo('?component=List&dataUrl=users.json');

      expect(Cosmos.render.calls.count()).toEqual(2);
      expect(Cosmos.render.calls.mostRecent().args[0]).toEqual({
        component: 'List',
        dataUrl: 'users.json'
      });
    });

    it("with props from event state on PopState event", function() {
      var router = new Cosmos.Router({});
      router._onPopState({
        state: {
          component: 'List',
          dataUrl: 'users.json'
        }
      });

      expect(Cosmos.render.calls.count()).toEqual(2);
      expect(Cosmos.render.calls.mostRecent().args[0]).toEqual({
        component: 'List',
        dataUrl: 'users.json'
      });
    });
  });

  it("should cache latest snapshot of previous Component", function() {
    var ComponentClass = React.createClass({
          mixins: [Cosmos.mixins.PersistState],
          render: function() {
            return React.DOM.span();
          }
        }),
        props = {component: 'List', dataUrl: 'users.json'},
        componentElement = React.createElement(ComponentClass, props)
        componentInstance = utils.renderIntoDocument(componentElement);

    // We just want a valid instance to work with, the Router props won't be
    // taken into consideration
    spyOn(Cosmos, 'render').and.callFake(function(props) {
      return componentInstance;
    });

    var router = new Cosmos.Router();
    // We alter the current instance while it's bound to the current history
    // entry
    componentInstance.setProps({dataUrl: null, someNumber: 555});
    componentInstance.setState({amIState: true});

    // Before routing to a new Component configuration, the previous one
    // shouldn't been updated with our changes
    router.goTo('?component=User&dataUrl=user.json');

    expect(router._replaceHistoryState.calls.count()).toEqual(1);
    expect(router._replaceHistoryState.calls.mostRecent().args[0]).toEqual({
      component: 'List',
      dataUrl: null,
      someNumber: 555,
      state: {
        amIState: true
      }
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2010 elements.at New Media Solutions GmbH (http://www.elements.at)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.object.tags.wysiwyg");
pimcore.object.tags.wysiwyg = Class.create(pimcore.object.tags.abstract, {

    type: "wysiwyg",

    initialize: function (data, fieldConfig) {
        this.data = "";
        if (data) {
            this.data = data;
        }
        this.fieldConfig = fieldConfig;

        if (intval(this.fieldConfig.width) < 1) {
            this.fieldConfig.width = 400;
        }
        if (intval(this.fieldConfig.height) < 1) {
            this.fieldConfig.height = 300;
        }

        this.editableDivId = "object_wysiwyg_" + uniqid();
        this.previewIframeId = "object_wysiwyg_iframe_" + uniqid();

    },

    getGridColumnEditor: function(field) {
        var editorConfig = {};

        if (field.config) {
            if (field.config.width) {
                if (intval(field.config.width) > 10) {
                    editorConfig.width = field.config.width;
                }
            }
        }

        if(field.layout.noteditable) {
            return null;
        }
        // WYSIWYG
        if (field.type == "wysiwyg") {
            return new Ext.form.HtmlEditor({
                width: 500,
                height: 300
            });
        }
    },

    getGridColumnFilter: function(field) {
        return {type: 'string', dataIndex: field.key};
    },

    getLayoutEdit: function () {
        this.getLayout();
        this.disableEditing = false;
        this.component.on("afterrender", this.getPreview.bind(this));
        return this.component;
    },

    getLayout: function () {
        var pConf = {
            title: this.fieldConfig.title,
            width: this.fieldConfig.width,
            html: '

',
            cls: "object_field"
        };

        this.component = new Ext.Panel(pConf);
    },

    getPreview: function() {

        var iframe = document.createElement("iframe");
        iframe.setAttribute("frameborder", "0");
        iframe.setAttribute("id", this.previewIframeId);
        iframe.onload = function () {
            var document = Ext.get(this.previewIframeId).dom.contentWindow.document;
            var iframeContent = this.data;
            iframeContent += '<link href="/pimcore/static/js/lib/ckeditor/contents.css" rel="stylesheet" type="text/css" />';
            document.body.innerHTML = iframeContent;
            document.body.setAttribute("style", "height: 80%; cursor: pointer;");

            if(this.disableEditing == false) {
                Ext.get(document.body).on("click", this.initCkEditor.bind(this));
            }

        }.bind(this);

        Ext.get(this.editableDivId).dom.innerHTML = "";
        Ext.get(this.editableDivId).dom.appendChild(iframe);

        // set dimensions of iframe
        if (this.fieldConfig.height) {
            Ext.get(this.previewIframeId).setStyle({
                height: this.fieldConfig.height + "px"
            });
        }
        if (this.fieldConfig.width) {
            Ext.get(this.previewIframeId).setStyle({
                width: this.fieldConfig.width + "px"
            });
        }
    },

    getLayoutShow: function () {
        this.getLayout();
        this.disableEditing = true;
        this.component.on("afterrender", this.getPreview.bind(this));
        return this.component;
    },

    initCkEditor: function () {

        var toolbar_Full = [
            ["close_object",'Cut','Copy','Paste','PasteText','PasteFromWord','-', 'SpellChecker'],
            ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
            '/',
            ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
            ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
            ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
            ['Link','Unlink','Anchor'],
            ['Image','Flash','Table','HorizontalRule','SpecialChar','PageBreak'],
            '/',
            ['Styles','Format','Font','FontSize'],
            ['TextColor','BGColor'],
            ['Maximize', 'ShowBlocks','Source']
        ];

        var eConfig = {
            uiColor: "#f2f2f2",
            toolbar: toolbar_Full,
            width: this.fieldConfig.width,
            height: this.fieldConfig.height,
            resize_enabled: false
        };

        eConfig.extraPlugins = "close_object";

        if (intval(this.fieldConfig.width) > 1) {
            eConfig.width = this.fieldConfig.width;
        }
        if (intval(this.fieldConfig.height) > 1) {
            eConfig.height = this.fieldConfig.height;
        }

        Ext.get(this.editableDivId).update(this.data);
        this.ckeditor = CKEDITOR.replace(this.editableDivId, eConfig);
        this.ckeditor.pimcore_tag_instance = this;
    },

    mask: function () {
        try {
            var pan = this.component.el;

            if (pan) {
                pan.setStyle({
                    position: "relative"
                });

                var maskEl = pan.createChild({
                    tag: "div",
                    id: Ext.id()
                })

                maskEl = Ext.get(maskEl.id);

                maskEl.addClass("pimcore_wysiwyg_mask");
                maskEl.setStyle({
                    top: 0,
                    left: 0,
                    zIndex: 10000,
                    width: pan.getWidth() + "px",
                    height: pan.getHeight() + "px",
                    position: "absolute"
                });

                this.maskEl = maskEl.dom;

                // add drop zone
                var dd = new Ext.dd.DropZone(this.maskEl, {
                    ddGroup: "element",

                    getTargetFromEvent: function(e) {
                        return this.getEl();
                    },

                    onNodeOver : function(target, dd, e, data) {
                        return Ext.dd.DropZone.prototype.dropAllowed;
                    },

                    onNodeDrop : this.onNodeDrop.bind(this)
                });
            }
        }
        catch (e) {
            console.log(e);
        }
    },

    unmask: function () {
        if (this.maskEl) {
            Ext.get(this.maskEl).remove();
        }
    },

    onNodeDrop: function (target, dd, e, data) {

        if (!this.ckeditor) {
            return;
        }

        var wrappedText = data.node.attributes.text;
        var textIsSelected = false;

        try {
            var selection = this.ckeditor.getSelection();
            var bookmarks = selection.createBookmarks();
            var range = selection.getRanges()[ 0 ];
            var fragment = range.clone().cloneContents();

            selection.selectBookmarks(bookmarks);
            var retval = "";
            var childList = fragment.getChildren();
            var childCount = childList.count();

            for (var i = 0; i < childCount; i++) {
                var child = childList.getItem(i);
                retval += ( child.getOuterHtml ?
                        child.getOuterHtml() : child.getText() );
            }

            if (retval.length > 0) {
                wrappedText = retval;
                textIsSelected = true;
            }
        }
        catch (e) {
        }

        // remove existing links out of the wrapped text
        wrappedText = wrappedText.replace(/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, function ($0, $1) {
            if($1.toLowerCase() == "a") {
                return "";
            }
            return $0;
        });

        var id = data.node.attributes.id;
        var uri = data.node.attributes.path;

        if (data.node.attributes.elementType == "asset") {
            if (data.node.attributes.type == "image" && textIsSelected == false) {
                // only use thumbnails
                var defaultWidth = 400;
                uri = "/admin/asset/get-image-thumbnail/id/" + id + "/width/" + defaultWidth + "/aspectratio/true";
                this.ckeditor.insertHtml('');
                return true;
            }
            else {
                this.ckeditor.insertHtml('' + wrappedText + '');
                return true;
            }
        }

        if (data.node.attributes.type == "page") {
            this.ckeditor.insertHtml('' + wrappedText + '');
            return true;
        }

    },

    getValue: function () {

        var data = this.data;
        try {
            if (this.ckeditor) {
                data = this.ckeditor.getData();
            }
        }
        catch (e) {
        }

        this.data = data;

        return this.data;
    },

    getName: function () {
        return this.fieldConfig.name;
    },

    isDirty: function() {
        if(!this.isRendered()) {
            return false;
        }

        if(this.ckeditor) {
            return this.ckeditor.checkDirty();
        }
        return false;
//        return this.ckeditor.IsDirty();
    }
});

// add close button plugin
var ckeditor_close_objectplugin_button ='close_object';
CKEDITOR.plugins.add(ckeditor_close_objectplugin_button,{
    init:function(editor){
        editor.addCommand(ckeditor_close_objectplugin_button, {
            exec:function(editor){
                window.setTimeout(function () {
                    try {
                        this.data = this.ckeditor.getData();
                        this.ckeditor.destroy();
                        this.getPreview();
                    } catch (e) {
                        console.log(e);
                    }
                }.bind(editor.pimcore_tag_instance), 10);
            }
        });
        editor.ui.addButton("close_object",{
            label:t('close'),
            icon: "/pimcore/static/img/icon/cross.png",
            command: ckeditor_close_objectplugin_button
        });
    }
});
<commit_msg>PIMCORE-972 - object wysiwyg html preview: fixed bug<commit_after>/**
 * Pimcore
 *
 * LICENSE
 *
 * This source file is subject to the new BSD license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.pimcore.org/license
 *
 * @copyright  Copyright (c) 2009-2010 elements.at New Media Solutions GmbH (http://www.elements.at)
 * @license    http://www.pimcore.org/license     New BSD License
 */

pimcore.registerNS("pimcore.object.tags.wysiwyg");
pimcore.object.tags.wysiwyg = Class.create(pimcore.object.tags.abstract, {

    type: "wysiwyg",

    initialize: function (data, fieldConfig) {
        this.data = "";
        if (data) {
            this.data = data;
        }
        this.fieldConfig = fieldConfig;

        if (intval(this.fieldConfig.width) < 1) {
            this.fieldConfig.width = 400;
        }
        if (intval(this.fieldConfig.height) < 1) {
            this.fieldConfig.height = 300;
        }

        this.editableDivId = "object_wysiwyg_" + uniqid();
        this.previewIframeId = "object_wysiwyg_iframe_" + uniqid();

    },

    getGridColumnEditor: function(field) {
        var editorConfig = {};

        if (field.config) {
            if (field.config.width) {
                if (intval(field.config.width) > 10) {
                    editorConfig.width = field.config.width;
                }
            }
        }

        if(field.layout.noteditable) {
            return null;
        }
        // WYSIWYG
        if (field.type == "wysiwyg") {
            return new Ext.form.HtmlEditor({
                width: 500,
                height: 300
            });
        }
    },

    getGridColumnFilter: function(field) {
        return {type: 'string', dataIndex: field.key};
    },

    getLayoutEdit: function () {
        this.getLayout();
        this.disableEditing = false;
        this.component.on("afterrender", this.getPreview.bind(this));
        return this.component;
    },

    getLayout: function () {
        var pConf = {
            title: this.fieldConfig.title,
            width: this.fieldConfig.width,
            html: '

',
            cls: "object_field"
        };

        this.component = new Ext.Panel(pConf);
    },

    getPreview: function() {

        var iframe = document.createElement("iframe");
        iframe.setAttribute("frameborder", "0");
        iframe.setAttribute("id", this.previewIframeId);
        iframe.onload = function () {
            var document = Ext.get(this.previewIframeId).dom.contentWindow.document;
            var iframeContent = this.data;
            iframeContent += '<link href="/pimcore/static/js/lib/ckeditor/contents.css" rel="stylesheet" type="text/css" />';
            document.body.innerHTML = iframeContent;
            document.body.setAttribute("style", "height: 80%; cursor: pointer;");

            if(this.disableEditing == false) {
                Ext.get(document.body).on("click", this.initCkEditor.bind(this));
            }

        }.bind(this);

        Ext.get(this.editableDivId).dom.innerHTML = "";
        Ext.get(this.editableDivId).dom.appendChild(iframe);

        // set dimensions of iframe
        if (this.fieldConfig.height) {
            Ext.get(this.previewIframeId).setStyle({
                height: this.fieldConfig.height + "px"
            });
        }
        if (this.fieldConfig.width) {
            Ext.get(this.previewIframeId).setStyle({
                width: this.fieldConfig.width + "px"
            });
        }
    },

    getLayoutShow: function () {
        this.getLayout();
        this.disableEditing = true;
        this.component.on("afterrender", this.getPreview.bind(this));
        return this.component;
    },

    initCkEditor: function () {

        var toolbar_Full = [
            ["close_object",'Cut','Copy','Paste','PasteText','PasteFromWord','-', 'SpellChecker'],
            ['Undo','Redo','-','Find','Replace','-','SelectAll','RemoveFormat'],
            '/',
            ['Bold','Italic','Underline','Strike','-','Subscript','Superscript'],
            ['NumberedList','BulletedList','-','Outdent','Indent','Blockquote'],
            ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'],
            ['Link','Unlink','Anchor'],
            ['Image','Flash','Table','HorizontalRule','SpecialChar','PageBreak'],
            '/',
            ['Styles','Format','Font','FontSize'],
            ['TextColor','BGColor'],
            ['Maximize', 'ShowBlocks','Source']
        ];

        var eConfig = {
            uiColor: "#f2f2f2",
            toolbar: toolbar_Full,
            width: this.fieldConfig.width,
            height: this.fieldConfig.height,
            resize_enabled: false
        };

        eConfig.extraPlugins = "close_object";

        if (intval(this.fieldConfig.width) > 1) {
            eConfig.width = this.fieldConfig.width;
        }
        if (intval(this.fieldConfig.height) > 1) {
            eConfig.height = this.fieldConfig.height;
        }

        Ext.get(this.editableDivId).update(this.data);
        this.ckeditor = CKEDITOR.replace(this.editableDivId, eConfig);
        this.ckeditor.pimcore_tag_instance = this;
    },

    mask: function () {
        try {
            var pan = this.component.el;

            if (pan) {
                pan.setStyle({
                    position: "relative"
                });

                var maskEl = pan.createChild({
                    tag: "div",
                    id: Ext.id()
                })

                maskEl = Ext.get(maskEl.id);

                maskEl.addClass("pimcore_wysiwyg_mask");
                maskEl.setStyle({
                    top: 0,
                    left: 0,
                    zIndex: 10000,
                    width: pan.getWidth() + "px",
                    height: pan.getHeight() + "px",
                    position: "absolute"
                });

                this.maskEl = maskEl.dom;

                // add drop zone
                var dd = new Ext.dd.DropZone(this.maskEl, {
                    ddGroup: "element",

                    getTargetFromEvent: function(e) {
                        return this.getEl();
                    },

                    onNodeOver : function(target, dd, e, data) {
                        return Ext.dd.DropZone.prototype.dropAllowed;
                    },

                    onNodeDrop : this.onNodeDrop.bind(this)
                });
            }
        }
        catch (e) {
            console.log(e);
        }
    },

    unmask: function () {
        if (this.maskEl) {
            Ext.get(this.maskEl).remove();
        }
    },

    onNodeDrop: function (target, dd, e, data) {

        if (!this.ckeditor) {
            return;
        }

        var wrappedText = data.node.attributes.text;
        var textIsSelected = false;

        try {
            var selection = this.ckeditor.getSelection();
            var bookmarks = selection.createBookmarks();
            var range = selection.getRanges()[ 0 ];
            var fragment = range.clone().cloneContents();

            selection.selectBookmarks(bookmarks);
            var retval = "";
            var childList = fragment.getChildren();
            var childCount = childList.count();

            for (var i = 0; i < childCount; i++) {
                var child = childList.getItem(i);
                retval += ( child.getOuterHtml ?
                        child.getOuterHtml() : child.getText() );
            }

            if (retval.length > 0) {
                wrappedText = retval;
                textIsSelected = true;
            }
        }
        catch (e) {
        }

        // remove existing links out of the wrapped text
        wrappedText = wrappedText.replace(/<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, function ($0, $1) {
            if($1.toLowerCase() == "a") {
                return "";
            }
            return $0;
        });

        var id = data.node.attributes.id;
        var uri = data.node.attributes.path;

        if (data.node.attributes.elementType == "asset") {
            if (data.node.attributes.type == "image" && textIsSelected == false) {
                // only use thumbnails
                var defaultWidth = 400;
                uri = "/admin/asset/get-image-thumbnail/id/" + id + "/width/" + defaultWidth + "/aspectratio/true";
                this.ckeditor.insertHtml('');
                return true;
            }
            else {
                this.ckeditor.insertHtml('' + wrappedText + '');
                return true;
            }
        }

        if (data.node.attributes.type == "page") {
            this.ckeditor.insertHtml('' + wrappedText + '');
            return true;
        }

    },

    getValue: function () {

        var data = this.data;
        try {
            if (this.ckeditor) {
                data = this.ckeditor.getData();
            }
        }
        catch (e) {
        }

        this.data = data;

        return this.data;
    },

    getName: function () {
        return this.fieldConfig.name;
    },

    isDirty: function() {
        if(!this.isRendered()) {
            return false;
        }

        if(this.dirty) {
            return this.dirty;
        }

        if(this.ckeditor) {
            return this.ckeditor.checkDirty();
        }
        return false;
//        return this.ckeditor.IsDirty();
    }
});

// add close button plugin
var ckeditor_close_objectplugin_button ='close_object';
CKEDITOR.plugins.add(ckeditor_close_objectplugin_button,{
    init:function(editor){
        editor.addCommand(ckeditor_close_objectplugin_button, {
            exec:function(editor){
                window.setTimeout(function () {
                    try {
                        this.data = this.ckeditor.getData();
                        this.ckeditor.destroy();
                        this.ckeditor = null;
                        this.dirty = true;

                        this.getPreview();
                    } catch (e) {
                        console.log(e);
                    }
                }.bind(editor.pimcore_tag_instance), 10);
            }
        });
        editor.ui.addButton("close_object",{
            label:t('close'),
            icon: "/pimcore/static/img/icon/cross.png",
            command: ckeditor_close_objectplugin_button
        });
    }
});
<|endoftext|>


<commit_before>/*jshint strict:false */

module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-bumpup');
    grunt.loadNpmTasks('grunt-tagrelease');
    grunt.loadNpmTasks('grunt-shell');
    grunt.loadNpmTasks('grunt-contrib-qunit');
    grunt.loadNpmTasks('grunt-contrib-compress');

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        uglify: {
            options: {
                banner: '/*! <%= pkg.title %> v<%= pkg.version %> | Copyright (C) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> | <%= pkg.homepage %> | Released under the MIT License */\n',
                report: 'gzip'
            },
            dist: {
                files: {
                    'dist/<%= pkg.name %>.js': ['src/*.js']
                }
            }
        },

        compress: {
            main: {
                options: {
                    archive: 'dist/<%= pkg.name %>.v<%= pkg.version %>.zip'
                },
                files: [
                    {
                        src: ['LICENSE', 'README.md'],
                        dest: '<%= pkg.name %>/',
                        filter: 'isFile'
                    },
                    {
                        expand: true,
                        cwd: 'dist/',
                        src: ['<%= pkg.name %>.js'],
                        dest: '<%= pkg.name %>/',
                        filter: 'isFile'
                    },
                    {
                        expand: true,
                        cwd: 'src/',
                        src: ['*.js'],
                        dest: '<%= pkg.name %>/src/',
                        filter: 'isFile'
                    }
                ]
            }
        },

        jshint: {
            files: ['Gruntfile.js', 'src/*.js'],
            options: {
                bitwise: true,
                camelcase: true,
                curly: true,
                eqeqeq: true,
                es3: true,
                forin: true,
                freeze: true,
                immed: true,
                indent: 4,
                latedef: true,
                newcap: true,
                noarg: true,
                noempty: true,
                nonew: true,
                quotmark: 'single',
                undef: true,
                unused: true,
                strict: true,
                trailing: true,
                browser: true,
                globals: {
                    jQuery: true,
                    Zepto: true,
                    define: true,
                    module: true
                }
            }
        },

        bumpup: {
            files: ['package.json', 'bower.json', 'dense.jquery.json']
        },

        tagrelease: {
            file: 'package.json',
            commit:  true,
            message: 'Marks v%version%.',
            prefix:  '',
            annotate: true
        },

        shell: {
            jsdoc: {
                command: 'mkdir -p dist && ./node_modules/jsdoc/jsdoc src/*.js -t templates/haruki -d console -q format=json > dist/docs.json'
            },

            // Publishes the release on GitHub pages.

            publish: {
                command: [
                    'echo "Storing required resources in dist..."',
                    'mkdir -pv dist',
                    'cp -f package.json dist/package.json',
                    'echo "Cleaning working tree..."',
                    'rm -Rf content',
                    'rm -Rf download',
                    'rm -Rf tmp',
                    'echo "Stashing current changes..."',
                    'git stash',
                    'echo "Checking out gh-pages..."',
                    'git checkout gh-pages',
                    'echo "Creating base directories..."',
                    'mkdir -p content',
                    'mkdir -p download',
                    'mkdir -p tmp',
                    'echo "Updating documentation..."',
                    'touch dist/docs.json',
                    'cp -f dist/docs.json content/docs.json',
                    'echo "Updating package.json..."',
                    'cp -f dist/package.json content/package.json',
                    'echo "Updating downloads..."',
                    'cp -rf download/ tmp/',
                    'cp -rf dist/*.zip download/',
                    'cp -rf tmp/ download/',
                    'echo "Staging..."',
                    'git add download/*.zip',
                    'git add content/*.json',
                    'echo "Committing changes..."',
                    'git diff-index --quiet HEAD || git commit -m "Update dist."',
                    'echo "Restoring previous branch..."',
                    'git checkout -',
                    'echo "Removing trash..."',
                    'rm -Rf dist',
                    'rm -Rf content',
                    'rm -Rf download',
                    'rm -Rf tmp',
                    'echo "Restoring working tree..."',
                    'git stash pop'
                ].join('&&'),
                options: {
                    stdout: true
                }
            }
        },

        qunit: {
            options: {
                timeout: 60000
            },
            all: ['test/*.html']
        }
    });

    grunt.registerTask('updatePackage', function () {
        grunt.config.set('pkg', grunt.file.readJSON('package.json'));
    });

    grunt.registerTask('test', ['jshint', 'qunit']);
    grunt.registerTask('build', ['uglify']);
    grunt.registerTask('default', ['test', 'build']);

    grunt.registerTask('jsdoc', function ()
    {
        grunt.task.run('shell:jsdoc');
    });

    grunt.registerTask('publish', function ()
    {
        grunt.task.run('jsdoc');
        grunt.task.run('compress');
        grunt.task.run('shell:publish');
    });

    grunt.registerTask('travis', ['jshint', 'qunit', 'build']);

    grunt.registerTask('release', function (type)
    {
        if (!type) {
            type = 'patch';
        }

        grunt.task.run('test');
        grunt.task.run('bumpup:' + type);
        grunt.task.run('updatePackage');
        grunt.task.run('build');
        grunt.task.run('tagrelease');
        grunt.registerTask('publish');
    });
};
<commit_msg>Some release task clean up.<commit_after>/*jshint strict:false */

module.exports = function (grunt) {
    grunt.loadNpmTasks('grunt-contrib-uglify');
    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-bumpup');
    grunt.loadNpmTasks('grunt-tagrelease');
    grunt.loadNpmTasks('grunt-shell');
    grunt.loadNpmTasks('grunt-contrib-qunit');
    grunt.loadNpmTasks('grunt-contrib-compress');

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        uglify: {
            options: {
                banner: '/*! <%= pkg.title %> v<%= pkg.version %> | Copyright (C) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> | <%= pkg.homepage %> | Released under the MIT License */\n',
                report: 'gzip'
            },
            dist: {
                files: {
                    'dist/<%= pkg.name %>.js': ['src/*.js']
                }
            }
        },

        compress: {
            main: {
                options: {
                    archive: 'dist/<%= pkg.name %>.v<%= pkg.version %>.zip'
                },
                files: [
                    {
                        src: ['LICENSE', 'README.md'],
                        dest: '<%= pkg.name %>/',
                        filter: 'isFile'
                    },
                    {
                        expand: true,
                        cwd: 'dist/',
                        src: ['<%= pkg.name %>.js'],
                        dest: '<%= pkg.name %>/',
                        filter: 'isFile'
                    },
                    {
                        expand: true,
                        cwd: 'src/',
                        src: ['*.js'],
                        dest: '<%= pkg.name %>/src/',
                        filter: 'isFile'
                    }
                ]
            }
        },

        jshint: {
            files: ['Gruntfile.js', 'src/*.js'],
            options: {
                bitwise: true,
                camelcase: true,
                curly: true,
                eqeqeq: true,
                es3: true,
                forin: true,
                freeze: true,
                immed: true,
                indent: 4,
                latedef: true,
                newcap: true,
                noarg: true,
                noempty: true,
                nonew: true,
                quotmark: 'single',
                undef: true,
                unused: true,
                strict: true,
                trailing: true,
                browser: true,
                globals: {
                    jQuery: true,
                    Zepto: true,
                    define: true,
                    module: true
                }
            }
        },

        bumpup: {
            files: ['package.json', 'bower.json', 'dense.jquery.json']
        },

        tagrelease: {
            file: 'package.json',
            commit:  true,
            message: 'Marks v%version%.',
            prefix:  '',
            annotate: true
        },

        shell: {
            jsdoc: {
                command: 'mkdir -p dist && ./node_modules/jsdoc/jsdoc src/*.js -t templates/haruki -d console -q format=json > dist/docs.json'
            },

            // Publishes the release on GitHub pages.

            publish: {
                command: [
                    'echo "Storing required resources in dist..."',
                    'mkdir -pv dist',
                    'cp -f package.json dist/package.json',
                    'echo "Cleaning working tree..."',
                    'rm -Rf content',
                    'rm -Rf tmp',
                    'echo "Stashing current changes..."',
                    'git stash',
                    'echo "Checking out gh-pages..."',
                    'git checkout gh-pages',
                    'echo "Creating base directories..."',
                    'mkdir -p content',
                    'mkdir -p tmp',
                    'echo "Updating documentation..."',
                    'touch dist/docs.json',
                    'cp -f dist/docs.json content/docs.json',
                    'echo "Updating package.json..."',
                    'cp -f dist/package.json content/package.json',
                    'echo "Staging..."',
                    'git add content/*.json',
                    'echo "Committing changes..."',
                    'git diff-index --quiet HEAD || git commit -m "Update dist."',
                    'echo "Restoring previous branch..."',
                    'git checkout -',
                    'echo "Removing trash..."',
                    'rm -Rf dist',
                    'rm -Rf content',
                    'rm -Rf download',
                    'rm -Rf tmp',
                    'echo "Restoring working tree..."',
                    'git stash pop'
                ].join('&&'),
                options: {
                    stdout: true
                }
            }
        },

        qunit: {
            options: {
                timeout: 60000
            },
            all: ['test/*.html']
        }
    });

    grunt.registerTask('updatePackage', function () {
        grunt.config.set('pkg', grunt.file.readJSON('package.json'));
    });

    grunt.registerTask('test', ['jshint', 'qunit']);
    grunt.registerTask('build', ['uglify']);
    grunt.registerTask('default', ['test', 'build']);

    grunt.registerTask('jsdoc', function ()
    {
        grunt.task.run('shell:jsdoc');
    });

    grunt.registerTask('publish', function ()
    {
        grunt.task.run('jsdoc');
        grunt.task.run('compress');
        grunt.task.run('shell:publish');
    });

    grunt.registerTask('travis', ['jshint', 'qunit', 'build']);

    grunt.registerTask('release', function (type)
    {
        if (!type) {
            type = 'patch';
        }

        grunt.task.run('test');
        grunt.task.run('bumpup:' + type);
        grunt.task.run('updatePackage');
        grunt.task.run('build');
        grunt.task.run('tagrelease');
        grunt.registerTask('publish');
    });
};
<|endoftext|>


<commit_before>module.exports = function(grunt) {
	'use strict';

	// Project configuration
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),

		sass: {
			main: {
				options: {
					outputStyle: 'compressed',
				},
				files: {
					'dist/ladda.min.css': [ 'css/ladda-themed.scss' ],
					'dist/ladda-themeless.min.css': [ 'css/ladda.scss' ]
				}
			}
		},

		jshint: {
			options: {
				// enforcing
				esversion: 6,
				curly: true,
				eqeqeq: true,
				freeze: true,
				futurehostile: true,
				globals: {
					module: false,
					console: false,
					define: false
				},
				latedef: "nofunc",
				maxparams: 3,
				noarg: true,
				nocomma: true,
				nonbsp: true,
				nonew: true,
				undef: true,
				unused: true,
				// environments
				browser: true,
				node: true,
			},
			files: [ 'Gruntfile.js', 'js/ladda.js' ]
		},

		connect: {
			server: {
				options: {
					port: 8000,
					base: '.',
				}
			}
		},

		watch: {
			main: {
				files: [ 'Gruntfile.js', 'js/ladda.js' ],
				tasks: 'js',
				options: {
					livereload: true,
				},
			},
			theme: {
				files: [ 'css/ladda.scss' ],
				tasks: 'css',
				options: {
					livereload: true,
				},
			}
		}

	});

	// Dependencies
	grunt.loadNpmTasks( 'grunt-contrib-jshint' );
	grunt.loadNpmTasks( 'grunt-contrib-watch' );
	grunt.loadNpmTasks( 'grunt-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-connect' );

	// Default task
	grunt.registerTask( 'default', [ 'js', 'css' ] );

	// Theme task
	grunt.registerTask( 'js', [ 'jshint' ] );
	grunt.registerTask( 'css', [ 'sass' ] );

	// Serve presentation locally
	grunt.registerTask( 'serve', [ 'connect', 'watch' ] );
};
<commit_msg>Watch all Sass files for changes<commit_after>module.exports = function(grunt) {
	'use strict';

	// Project configuration
	grunt.initConfig({
		pkg: grunt.file.readJSON('package.json'),

		sass: {
			main: {
				options: {
					outputStyle: 'compressed',
				},
				files: {
					'dist/ladda.min.css': [ 'css/ladda-themed.scss' ],
					'dist/ladda-themeless.min.css': [ 'css/ladda.scss' ]
				}
			}
		},

		jshint: {
			options: {
				// enforcing
				esversion: 6,
				curly: true,
				eqeqeq: true,
				freeze: true,
				futurehostile: true,
				globals: {
					module: false,
					console: false,
					define: false
				},
				latedef: "nofunc",
				maxparams: 3,
				noarg: true,
				nocomma: true,
				nonbsp: true,
				nonew: true,
				undef: true,
				unused: true,
				// environments
				browser: true,
				node: true,
			},
			files: [ 'Gruntfile.js', 'js/ladda.js' ]
		},

		connect: {
			server: {
				options: {
					port: 8000,
					base: '.',
				}
			}
		},

		watch: {
			main: {
				files: [ 'Gruntfile.js', 'js/ladda.js' ],
				tasks: 'js',
				options: {
					livereload: true,
				},
			},
			theme: {
				files: [ 'css/*.scss' ],
				tasks: 'css',
				options: {
					livereload: true,
				},
			}
		}

	});

	// Dependencies
	grunt.loadNpmTasks( 'grunt-contrib-jshint' );
	grunt.loadNpmTasks( 'grunt-contrib-watch' );
	grunt.loadNpmTasks( 'grunt-sass' );
	grunt.loadNpmTasks( 'grunt-contrib-connect' );

	// Default task
	grunt.registerTask( 'default', [ 'js', 'css' ] );

	// Theme task
	grunt.registerTask( 'js', [ 'jshint' ] );
	grunt.registerTask( 'css', [ 'sass' ] );

	// Serve presentation locally
	grunt.registerTask( 'serve', [ 'connect', 'watch' ] );
};
<|endoftext|>


<commit_before>var marked = require('marked'),
  md5 = require('MD5');

module.exports = function(grunt) {
'use strict';

//------------------------------------------------------------------------------
// Snowflake CSS
// TODO: this should become it's own module and published on npm
//------------------------------------------------------------------------------

function keys(o) {
  var a = [];
  for (var i in o) {
    if (o.hasOwnProperty(i) !== true) continue;
    a.push(i);
  }
  return a;
}

function arrToObj(arr) {
  var o = {};
  for (var i = 0; i < arr.length; i++) {
    o[ arr[i] ] = null;
  }
  return o;
}

function difference(arr1, arr2) {
  var o1 = arrToObj(arr1);
  var o2 = arrToObj(arr2);
  var delta = [];

  for (var i in o1) {
    if (o1.hasOwnProperty(i) !== true) continue;

    if (o2.hasOwnProperty(i) !== true) {
      delta.push(i)
    }
  }

  for (var i in o2) {
    if (o2.hasOwnProperty(i) !== true) continue;

    if (o1.hasOwnProperty(i) !== true) {
      delta.push(i)
    }
  }

  return delta.sort();
}

// Snowflake class names must contain at least one letter and one number
function hasNumbersAndLetters(str) {
  if (str.search(/\d/) === -1) {
    return false;
  }

  if (str.search(/[a-z]/) === -1) {
    return false;
  }

  return true;
}

// returns an array of unique Snowflake classes from a file
function extractSnowflakeClasses(filename, pattern) {
  if (! pattern) {
    pattern = /([a-z0-9]+-){1,}([abcdef0-9]){5}/g;
  }

  var fileContents = grunt.file.read(filename);
  var classes = {};

  var matches = fileContents.match(pattern);

  if (matches) {
    for (var i = 0; i < matches.length; i++) {
      var c = matches[i];

      if (hasNumbersAndLetters(c) === true) {
        classes[c] = null;
      }
    }
  }

  return keys(classes);
}

function snowflakeCount() {
  var cssClasses = extractSnowflakeClasses("public/css/main.min.css"),
    jsServer = extractSnowflakeClasses("app.js"),
    jsClient = extractSnowflakeClasses('public/js/client.min.js'),
    jsClasses = jsServer.concat(jsClient);

  console.log(cssClasses.length + " class names found in css/main.min.css");
  console.log(jsClasses.length + " class names found in JS files");

  console.log("Classes found in one file but not the other:");
  console.log( difference(jsClasses, cssClasses) );
}

//------------------------------------------------------------------------------
// Doc Files
//------------------------------------------------------------------------------

function extractNamespace(fullName) {
  fullName = fullName + "";
  var slash = fullName.indexOf("/");
  return fullName.substr(0, slash);
}

function extractName(fullName) {
  fullName = fullName + "";
  var slash = (fullName + "").indexOf("/");
  return fullName.substr(slash + 1);
}

function isSectionLine(line) {
  return (line.search(/^=====/) !== -1);
}

function convertArraySection(s) {
  if (s === "") {
    return [];
  }

  var arr1 = s.split("\n"),
    arr2 = [];

  for (var i = 0; i < arr1.length; i++) {
    if (arr1[i] === "") continue;

    arr2.push(arr1[i]);
  }

  return arr2;
}

function transformFn(fn) {
  // extract namespace and function name from the full name
  fn["full-name"] = fn["function"];
  fn["namespace"] = extractNamespace(fn["function"]);
  fn["name"] = extractName(fn["function"]);
  delete fn["function"];

  // parse description as Markdown
  fn["description-html"] = marked(fn["description"]);
  delete fn["description"];

  // convert some sections into arrays
  fn.signature = convertArraySection(fn.signature);

  if (fn.hasOwnProperty("related") === true) {
    fn.related = convertArraySection(fn.related);
  }

  return fn;
}

function parseDocFile(fileContent) {
  var contentArr = fileContent.split("\n"),
    currentSection = false,
    fn = {};

  for (var i = 0; i < contentArr.length; i++) {
    var line = contentArr[i];

    if (isSectionLine(line) === true) {
      currentSection = line.replace(/^=====/, "").trim().toLowerCase();
      fn[currentSection] = "";
      continue;
    }

    if (currentSection === false) continue;

    fn[currentSection] += line + "\n";
  }

  // trim everything
  for (var i in fn) {
    if (fn.hasOwnProperty(i) !== true) continue;

    fn[i] = fn[i].trim();
  }

  return fn;
}

// quick check that the format is correct
function validFn(fn) {
  return fn.hasOwnProperty("function") &&
         fn.hasOwnProperty("signature") &&
         fn.hasOwnProperty("description");
}

function buildDocs() {
  var docs = {};

  grunt.file.recurse("docs", function(abspath) {
    // skip non .cljsdoc files
    if (abspath.search(/\.cljsdoc$/) === -1) return;

    var fileContent = grunt.file.read(abspath),
      fn = parseDocFile(fileContent);

    // quick sanity check that fn is a good format
    if (validFn(fn) !== true) return;

    // clean and transform the data
    fn = transformFn(fn);

    docs[fn["full-name"]] = fn;
  });

  grunt.file.write("public/docs.json", JSON.stringify(docs, null, 2));
}

//------------------------------------------------------------------------------
// Grunt Config
//------------------------------------------------------------------------------

grunt.initConfig({

  clean: {
    options: {
      force: true
    },

    // remove all the files in the 00-publish folder
    pre: ['00-publish']
  },

  copy: {
    cheatsheet: {
      files: [
        { src: 'public/cheatsheet/index.html', dest: '00-publish/cheatsheet/index.html' },
        { src: 'public/css/main.min.css', dest: '00-publish/css/main.min.css' },
        { src: 'public/fonts/*', dest: '00-publish/fonts/', expand: true, flatten: true },
        { src: 'public/img/*', dest: '00-publish/img/', expand: true, flatten: true },
        { src: 'public/js/cheatsheet.min.js', dest: '00-publish/js/cheatsheet.min.js' },
        { src: 'public/js/libs/jquery-2.1.1.min.js', dest: '00-publish/js/libs/jquery-2.1.1.min.js' },
        { src: 'public/favicon.png', dest: '00-publish/favicon.png' }
      ]
    }
  },

  less: {
    options: {
      compress: true
    },

    watch: {
      files: {
        'public/css/main.min.css': 'less/main.less'
      }
    }
  },

  watch: {
    options: {
      atBegin: true
    },

    less: {
      files: "less/*.less",
      tasks: "less:watch"
    },

    docs: {
      files: "docs/*.cljsdoc",
      tasks: "build-docs"
    }
  }

});

function hashCheatsheetFiles() {
  var cssFile = grunt.file.read('00-publish/css/main.min.css'),
    cssHash = md5(cssFile).substr(0, 8),
    jsFile = grunt.file.read('00-publish/js/cheatsheet.min.js'),
    jsHash = md5(jsFile).substr(0, 8),
    htmlFile = grunt.file.read('00-publish/cheatsheet/index.html');

  // write the new files
  grunt.file.write('00-publish/css/main.min.' + cssHash + '.css', cssFile);
  grunt.file.write('00-publish/js/cheatsheet.min.' + jsHash + '.js', jsFile);

  // delete the old files
  grunt.file.delete('00-publish/css/main.min.css');
  grunt.file.delete('00-publish/js/cheatsheet.min.js');

  // update the HTML file
  grunt.file.write('00-publish/cheatsheet/index.html',
    htmlFile.replace('main.min.css', 'main.min.' + cssHash + '.css')
    .replace('cheatsheet.min.js', 'cheatsheet.min.' + jsHash + '.js'));
}

// load tasks from npm
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');

grunt.registerTask('build-docs', buildDocs);
grunt.registerTask('hash-cheatsheet', hashCheatsheetFiles);

grunt.registerTask('build-cheatsheet', [
  'clean:pre',
  'less',
  'copy:cheatsheet',
  'hash-cheatsheet'
]);

grunt.registerTask('snowflake', snowflakeCount);
grunt.registerTask('default', 'less');

// end module.exports
};<commit_msg>clean up Gruntfile<commit_after>var marked = require('marked'),
  md5 = require('MD5');

module.exports = function(grunt) {
'use strict';

//------------------------------------------------------------------------------
// Snowflake CSS
// TODO: this should become it's own module and published on npm
//------------------------------------------------------------------------------

function keys(o) {
  var a = [];
  for (var i in o) {
    if (o.hasOwnProperty(i) !== true) continue;
    a.push(i);
  }
  return a;
}

function arrToObj(arr) {
  var o = {};
  for (var i = 0; i < arr.length; i++) {
    o[ arr[i] ] = null;
  }
  return o;
}

function difference(arr1, arr2) {
  var o1 = arrToObj(arr1);
  var o2 = arrToObj(arr2);
  var delta = [];

  for (var i in o1) {
    if (o1.hasOwnProperty(i) !== true) continue;

    if (o2.hasOwnProperty(i) !== true) {
      delta.push(i)
    }
  }

  for (var i in o2) {
    if (o2.hasOwnProperty(i) !== true) continue;

    if (o1.hasOwnProperty(i) !== true) {
      delta.push(i)
    }
  }

  return delta.sort();
}

// Snowflake class names must contain at least one letter and one number
function hasNumbersAndLetters(str) {
  if (str.search(/\d/) === -1) {
    return false;
  }

  if (str.search(/[a-z]/) === -1) {
    return false;
  }

  return true;
}

// returns an array of unique Snowflake classes from a file
function extractSnowflakeClasses(filename, pattern) {
  if (! pattern) {
    pattern = /([a-z0-9]+-){1,}([abcdef0-9]){5}/g;
  }

  var fileContents = grunt.file.read(filename);
  var classes = {};

  var matches = fileContents.match(pattern);

  if (matches) {
    for (var i = 0; i < matches.length; i++) {
      var c = matches[i];

      if (hasNumbersAndLetters(c) === true) {
        classes[c] = null;
      }
    }
  }

  return keys(classes);
}

function snowflakeCount() {
  var cssClasses = extractSnowflakeClasses("public/css/main.min.css"),
    jsServer = extractSnowflakeClasses("app.js"),
    jsClient = extractSnowflakeClasses('public/js/client.min.js'),
    jsClasses = jsServer.concat(jsClient);

  console.log(cssClasses.length + " class names found in css/main.min.css");
  console.log(jsClasses.length + " class names found in JS files");

  console.log("Classes found in one file but not the other:");
  console.log( difference(jsClasses, cssClasses) );
}

//------------------------------------------------------------------------------
// Doc Files
//------------------------------------------------------------------------------

function extractNamespace(fullName) {
  fullName = fullName + "";
  var slash = fullName.indexOf("/");
  return fullName.substr(0, slash);
}

function extractName(fullName) {
  fullName = fullName + "";
  var slash = (fullName + "").indexOf("/");
  return fullName.substr(slash + 1);
}

function isSectionLine(line) {
  return line.search(/^=====/) !== -1;
}

// converts each non-empty line of a section into an array
function convertSectionIntoArray(s) {
  // NOTE: this condtion should never happen
  if (s === "") return [];

  var arr1 = s.split("\n"),
    arr2 = [];

  // trim each line and remove empty lines
  for (var i = 0; i < arr1.length; i++) {
    var line = arr1[i].trim();
    if (line === "") continue;

    arr2.push(line);
  }

  return arr2;
}

function transformObjToDocs(fn) {
  // extract namespace and function name from the full name
  fn["full-name"] = fn["function"];
  fn["namespace"] = extractNamespace(fn["function"]);
  fn["name"] = extractName(fn["function"]);
  delete fn["function"];

  // parse description as Markdown
  fn["description-html"] = marked(fn["description"]);
  delete fn["description"];

  // convert some sections into arrays
  fn.signature = convertSectionIntoArray(fn.signature);

  if (fn.hasOwnProperty("related") === true) {
    fn.related = convertSectionIntoArray(fn.related);
  }

  return fn;
}

function parseDocFileIntoObject(fileContent) {
  var contentArr = fileContent.split("\n"),
    currentSection = false,
    obj = {};

  for (var i = 0; i < contentArr.length; i++) {
    var line = contentArr[i];

    if (isSectionLine(line) === true) {
      currentSection = line.replace(/^=====/, "").trim().toLowerCase();
      obj[currentSection] = "";
      continue;
    }

    if (currentSection === false) continue;

    obj[currentSection] += line + "\n";
  }

  // trim everything and delete empty sections
  var obj2 = {};
  for (var i in obj) {
    if (obj.hasOwnProperty(i) !== true) continue;

    obj[i] = obj[i].trim();
    if (obj[i] !== "") {
      obj2[i] = obj[i];
    }
  }

  return obj2;
}

// quick check that the file has everything we are expecting
function validDocObj(obj) {
  return obj.hasOwnProperty("function") &&
         obj.hasOwnProperty("signature") &&
         obj.hasOwnProperty("description");
}

function buildDocs() {
  var docs = {};

  grunt.file.recurse("docs", function(abspath) {
    // skip non .cljsdoc files
    if (abspath.search(/\.cljsdoc$/) === -1) return;

    var fileContent = grunt.file.read(abspath),
      obj = parseDocFileIntoObject(fileContent);

    // quick sanity check that the file is in a good format
    if (validDocObj(obj) !== true) return;

    // clean and transform the data
    obj = transformObjToDocs(obj);

    docs[obj["full-name"]] = obj;
  });

  grunt.file.write("public/docs.json", JSON.stringify(docs, null, 2));
}

//------------------------------------------------------------------------------
// Grunt Config
//------------------------------------------------------------------------------

grunt.initConfig({

  clean: {
    options: {
      force: true
    },

    // remove all the files in the 00-publish folder
    pre: ['00-publish']
  },

  copy: {
    cheatsheet: {
      files: [
        { src: 'public/cheatsheet/index.html', dest: '00-publish/cheatsheet/index.html' },
        { src: 'public/css/main.min.css', dest: '00-publish/css/main.min.css' },
        { src: 'public/fonts/*', dest: '00-publish/fonts/', expand: true, flatten: true },
        { src: 'public/img/*', dest: '00-publish/img/', expand: true, flatten: true },
        { src: 'public/js/cheatsheet.min.js', dest: '00-publish/js/cheatsheet.min.js' },
        { src: 'public/js/libs/jquery-2.1.1.min.js', dest: '00-publish/js/libs/jquery-2.1.1.min.js' },
        { src: 'public/favicon.png', dest: '00-publish/favicon.png' }
      ]
    }
  },

  less: {
    options: {
      compress: true
    },

    watch: {
      files: {
        'public/css/main.min.css': 'less/main.less'
      }
    }
  },

  watch: {
    options: {
      atBegin: true
    },

    less: {
      files: "less/*.less",
      tasks: "less:watch"
    },

    docs: {
      files: "docs/*.cljsdoc",
      tasks: "build-docs"
    }
  }

});

function hashCheatsheetFiles() {
  var cssFile = grunt.file.read('00-publish/css/main.min.css'),
    cssHash = md5(cssFile).substr(0, 8),
    jsFile = grunt.file.read('00-publish/js/cheatsheet.min.js'),
    jsHash = md5(jsFile).substr(0, 8),
    htmlFile = grunt.file.read('00-publish/cheatsheet/index.html');

  // write the new files
  grunt.file.write('00-publish/css/main.min.' + cssHash + '.css', cssFile);
  grunt.file.write('00-publish/js/cheatsheet.min.' + jsHash + '.js', jsFile);

  // delete the old files
  grunt.file.delete('00-publish/css/main.min.css');
  grunt.file.delete('00-publish/js/cheatsheet.min.js');

  // update the HTML file
  grunt.file.write('00-publish/cheatsheet/index.html',
    htmlFile.replace('main.min.css', 'main.min.' + cssHash + '.css')
    .replace('cheatsheet.min.js', 'cheatsheet.min.' + jsHash + '.js'));
}

// load tasks from npm
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');

grunt.registerTask('build-docs', buildDocs);
grunt.registerTask('hash-cheatsheet', hashCheatsheetFiles);

grunt.registerTask('build-cheatsheet', [
  'clean:pre',
  'less',
  'copy:cheatsheet',
  'hash-cheatsheet'
]);

grunt.registerTask('snowflake', snowflakeCount);
grunt.registerTask('default', 'less');

// end module.exports
};<|endoftext|>