<commit_before>module.exports = function(grunt) {
    grunt.initConfig({
		copyPackageTo: "\\\\telerik.com\\Resources\\BlackDragon\\Builds\\appbuilder-sublime-package",

        movePackageTo: process.env["JOB_NAME"] ? "\\\\telerik.com\\Resources\\BlackDragon\\Builds\\appbuilder-sublime-package" : "build",
        jobName: process.env["JOB_NAME"] || "local",
        buildNumber: process.env["BUILD_NUMBER"] || "non-ci",
        destinationFolder: process.env["JOB_NAME"] ? "<%= movePackageTo %>\\<%= jobName %>\\<%= grunt.template.today('yyyy-mm-dd') %> #<%= buildNumber %>" : "<%= movePackageTo %>",

        compress: {
            main: {
                options: {
                    archive: "<%= destinationFolder %>\\Telerik AppBuilder.zip"
                },
                files: [
                    { src: ["**/*.{py,pyd,so}", "*.{sublime-keymap,sublime-menu,sublime-settings}", "LICENSE"] }
                ]
            }
        },

		copy: {
			package_to_qa_drop_folder: {
				src: "<%= destinationFolder %>\\Telerik AppBuilder.zip",
				dest: "<%= copyPackageTo %>\\<%= jobName %>\\Telerik AppBuilder.zip"
			}
		},

        clean: {
            src: ["**/*.pyc"]
        }
    });

    grunt.loadNpmTasks("grunt-contrib-compress");
    grunt.loadNpmTasks("grunt-contrib-clean");
    grunt.registerTask("default", "compress:main");
	grunt.registerTask("default", ["compress:main", "copy:package_to_qa_drop_folder" ]);
}<commit_msg>Fix Build<commit_after>module.exports = function(grunt) {
    grunt.initConfig({
		copyPackageTo: "\\\\telerik.com\\Resources\\BlackDragon\\Builds\\appbuilder-sublime-package",

        movePackageTo: process.env["JOB_NAME"] ? "\\\\telerik.com\\Resources\\BlackDragon\\Builds\\appbuilder-sublime-package" : "build",
        jobName: process.env["JOB_NAME"] || "local",
        buildNumber: process.env["BUILD_NUMBER"] || "non-ci",
        destinationFolder: process.env["JOB_NAME"] ? "<%= movePackageTo %>\\<%= jobName %>\\<%= grunt.template.today('yyyy-mm-dd') %> #<%= buildNumber %>" : "<%= movePackageTo %>",

        compress: {
            main: {
                options: {
                    archive: "<%= destinationFolder %>\\Telerik AppBuilder.zip"
                },
                files: [
                    { src: ["**/*.{py,pyd,so}", "*.{sublime-keymap,sublime-menu,sublime-settings}", "LICENSE"] }
                ]
            }
        },

		copy: {
			package_to_qa_drop_folder: {
				src: "<%= destinationFolder %>\\Telerik AppBuilder.zip",
				dest: "<%= copyPackageTo %>\\<%= jobName %>\\Telerik AppBuilder.zip"
			}
		},

        clean: {
            src: ["**/*.pyc"]
        }
    });

    grunt.loadNpmTasks("grunt-contrib-compress");
    grunt.loadNpmTasks("grunt-contrib-clean");
	grunt.loadNpmTasks("grunt-contrib-copy");
	grunt.registerTask("default", ["compress:main", "copy:package_to_qa_drop_folder" ]);
}<|endoftext|>


<commit_before>// Test the test runner

var assert = require('assert');
var chai = require('chai');
var expect = chai.expect;
var should = chai.should;
var _ = require('underscore');

// always-true-mocha.js provides a template for other tests and can be used to debug the mocha test runner.

    describe('always true', function() {
        it('is always true', function() {
            expect(false).to.equal(true);
        });
    });
<commit_msg>Revert "Revert "Fix failing test""<commit_after>// Test the test runner

var assert = require('assert');
var chai = require('chai');
var expect = chai.expect;
var should = chai.should;
var _ = require('underscore');

// always-true-mocha.js provides a template for other tests and can be used to debug the mocha test runner.

    describe('always true', function() {
        it('is always true', function() {
            expect(true).to.equal(true);
        });
    });
<|endoftext|>


<commit_before>import preferences from './preferences.js';
import focalStorage from 'src/external/focalStorage.js'
import {debounce} from "utils"
import scriptManager from  "src/client/script-manager.js";
import Connection from 'src/components/halo/Connection.js'
import moment from 'src/external/moment.js'
import ViewNav from 'src/client/viewnav.js'

export function isCurrentlyCloning() {
    return sessionStorage["lively.persistenceCurrentlyCloning"] === 'true';
}

export default class Persistence {

  constructor() {
    this.saveDelay = (() => {
        this.saveLivelyContent();
        this.showMutationIndicator().style.backgroundColor = "rgba(10,10,10,0.3)"
    })::debounce(3000) // save the world 3seconds after a change
  }

  // work around non static module global state
  static get current() {
    if (!window.lively4persistence) {
      window.lively4persistence = new Persistence()
    }
    return window.lively4persistence
  }
  static set current(obj) {
    window.lively4persistence = obj
  }

  static enable() {
    this.disable()
    this.current = new Persistence()
    this.current.start()
  }

  static disable() {
    if (this.current) {
      this.current.stop()
    }
  }

  start() {
    for(let ea of Persistence.backupIntervals()) {
      this.ensureLivelyContentBackup(undefined, ea)

    }
    this.observeHTMLChanges()
  }

  stop() {
    if (this.mutationObserver) this.mutationObserver.disconnect()
  }

  urlToKey(urlString) {
    urlString = "" + urlString
    return "livelyWindows_"+urlString.replace(/^https?:\/\//,"").replace(/[^A-Za-z0-9]/,"_")
  }

  defaultTarget() {
    return document.body
  }

  defaultURL() {
    return (document.location.protocol + "//" + document.location.hostname + document.location.pathname)
  }

  async getLivelyContentForURL(url) {
    url = url || this.defaultURL()
    var content = await focalStorage.getItem(this.urlToKey(url.toString()))
    if (!content) {
      try {
        var elementSource = await lively.files.loadFile(lively4url + "/src/parts/initial-content.html")
        content = lively.html.parseHTML(elementSource)[0].innerHTML
      } catch(e) {
        lively.error("Could not load default desktop conent for lively", e)
      }
    }
    return content
  }

  setLivelyContentForURL(url, source) {
    url = url || this.defaultURL()
    return focalStorage.setItem(this.urlToKey(url.toString()), source)
  }

  async loadLivelyContentForURL(url, target) {
    var source = await this.getLivelyContentForURL(url)
    return this.loadLivelyContent(source, target)
  }

  async loadLivelyContent(source, target) {
    target = target || this.defaultTarget()
    var div = document.createElement("div")
    div.innerHTML = source
    var topLevel = Array.from(div.querySelectorAll(":scope > *"))
    var objs = Array.from(div.childNodes)

    // somehow zIndex gets lost...
    var zIndexMap = new Map()
    topLevel.forEach(ea => {
       zIndexMap.set(ea, ea.style.zIndex)
    })

    objs.map(ea => {
      if(ea.classList && ea.classList.contains('lively-preferences')) {
        target.querySelectorAll('div.lively-preferences').forEach(ele => ele.remove());
      }
      target.appendChild(ea);
    });
    await lively.components.loadUnresolved(target, true, "loadLivelyContentForURL", true)

    // restore zIndex in an Async way... it seems focus is responsible for it #Hack
    lively.sleep(0).then(() => {
      topLevel.forEach(ea => {
        ea.style.zIndex = zIndexMap.get(ea)
      })
    })

    objs.map(ea => { Persistence.initLivelyObject(ea)})
    return
  }

  static initLivelyObject(obj) {
    if (!obj) return;
    try {
      scriptManager.findLively4Script(obj, false);
      if (obj.livelyLoad) obj.livelyLoad()
      Connection.deserializeFromObjectIfNeeded(obj)
      if (!obj.querySelectorAll) return;
      obj.querySelectorAll("*").forEach(ea => {
        if (ea.livelyLoad) ea.livelyLoad()
        Connection.deserializeFromObjectIfNeeded(ea)
      })
    } catch(e) {
      lively.showError(e)
    }
  }

  async ensureLivelyContentBackup(url=this.defaultURL(), interval="yesterday", force) {
    var key = this.urlToKey(url.toString())
    const datePrefix = `backup_${interval}_date_`
    const sourcePrefix = `backup_${interval}_source_`

    var dateString = await focalStorage.getItem(datePrefix+ key)
    var lastBackupDate = moment(dateString)
    var today = moment()
    if (force || !dateString || lastBackupDate.diff(today, "minutes") > this.minutesForBackupInterval(interval)) {
      var source = await focalStorage.getItem(key) // get current source ... @onsetsu or should we use current world?

      await focalStorage.setItem(datePrefix + key, today.format("YYYY-MM-DD HH:MM:SS"))
      await focalStorage.setItem(sourcePrefix + key, source)
      lively.notify(`Ensured ${interval} backup!`)
    } else {
      lively.notify(`Last ${interval} backup is not old enough`)
    }
  }

  async loadLivelyContentBackup(url=this.defaultURL(), interval="yesterday", target=this.defaultTarget()) {
    var key = this.urlToKey(url.toString())
    const sourcePrefix = `backup_${interval}_source_`
    var source = await focalStorage.getItem(sourcePrefix + key)
    if (source) {
      this.loadLivelyContent(source, target)
      lively.notify(`load ${interval} backup!`)
    } else {
      lively.notify(`could not load ${interval} backup!`)
    }
  }

  async storeLivelyContentForURL(url, target) {
    target = target || this.defaultTarget()
    this.isPersisting = true
    var source = lively.html.getGlobalSource(target)
    this.setLivelyContentForURL(url, source)
    setTimeout(() => {
      this.isPersisting = false
    }, 0) // the mutation events triggered are still pending here...
    return source
  }

  async saveLivelyContent() {
    // console.log("[peristence] save after " +
    //   (Date.now() - this.lastSaved) +"ms")
    this.lastSaved = Date.now()
    await this.storeLivelyContentForURL()
    // console.log("[peristence] saved lively content into focalStorage " +
    //   (Date.now() - this.lastSaved) +"ms")
  }

  hasDoNotPersistTag(node,) {
    var donotperist =  node.attributes &&
      node.attributes.hasOwnProperty('data-lively4-donotpersist');
    if (donotperist) return true
    if (!node.parentElement) return false
    return this.hasDoNotPersistTag(node.parentElement)
  }

  isBlacklisted(mutation) {
    if (mutation.target.tagName == "BODY") return true
    if (mutation.target.tagName == "LIVELY-MENU") return true
    if (mutation.target.tagName == "LIVELY-SELECTION") return true
    if (mutation.target.tagName == "LIVELY-HAND") return true
    if (mutation.target.tagName == "LIVELY-HALO") return true
    if (mutation.target.tagName == "LIVELY-NOTIFICATION-LIST") return true
    if (mutation.target.id == "mutationIndicator") return true
    if (mutation.target.getAttribute && mutation.target.getAttribute("data-is-meta")) return true
    if (this.hasDoNotPersistTag(mutation.target)) return true
    return false
  }

  showMutationIndicator() {
    var indicator = document.body.querySelector("#mutationIndicator")
    if (!indicator)  {
      var div = document.createElement("div")
      div.id = "mutationIndicator"
      document.body.appendChild(div)
      div.style.position = "fixed"
      div.style.right = "0px"
      div.style.top = "0px"
      div.style.width = "10px"
      div.style.height = "10px"
      div.style.backgroundColor = "blue"
      div.style.pointerEvents = "none"
      div.style.zIndex = 2000

      indicator = div
    }
    return indicator
  }

  onMutation(mutations, observer) {
    if (this.isPersisting) {
      // console.log("ignore mutation ", mutations)
      return // we mutate while persisting
    }

    mutations.filter(ea => !this.isBlacklisted(ea)).forEach(record => {
      this.showMutationIndicator().style.backgroundColor = "rgba(200,0,0,0.5)"
      this.saveDelay()
    })
  }

  observeHTMLChanges(target) {
    target = target || this.defaultTarget()
    if (this.mutationObserver) this.mutationObserver.disconnect()
    this.mutationObserver = new MutationObserver((mutations, observer) => {
        this.onMutation(mutations, observer)
    });
    this.mutationObserver.observe(target, {
      childList: true,
      subtree: true,
      characterData: true,
      attributes: true});
  }

  minutesForBackupInterval(interval) {
    // #TODO refactor... this duplication does not look nice
    if (interval === "yesterday") {
      return 1 * 24 * 60
    }
    if (interval === "hour") {
      return  1 * 60
    }
    if (interval === "twohours") {
      return  2 * 60
    }
    if (interval === "week") {
      return  7 * 24 * 60
    }
    throw new Error(interval + " not supported")
  }

  static backupIntervals() {
    return ["hour", "twohours", "yesterday", "week"]
  }

  static restoreBackupContextMenuItems() {
    return this.backupIntervals().map(ea => {
      return this.restoreBackupContextMenuItem(ea)
    })
  }

  static restoreBackupContextMenuItem(interval) {
    return [interval, async (evt) => {
          var target =

          var scrollPane =
{target}

          ViewNav.enable(target)
          var ui =
{scrollPane}

                <button click={() => {

                      var contents = Array.from(target.childNodes)
                      document.body.innerHTML = ""
                      for(let ea of contents) {
                        document.body.appendChild(ea)
                      }

                      lively.notify("would restore " + contents.length + " elements" )

                  }}>restore</button>

          var win = await (<lively-window title={"Restore " + interval}>{ui}</lively-window>)
          document.body.appendChild(win)
          lively.setGlobalPosition(win, lively.getPosition(evt))
          lively.persistence.current.loadLivelyContentBackup(undefined, interval, target)
        }
      ]
  }

}
<commit_msg>added reminder for refactroring<commit_after>import preferences from './preferences.js';
import focalStorage from 'src/external/focalStorage.js'
import {debounce} from "utils"
import scriptManager from  "src/client/script-manager.js";
import Connection from 'src/components/halo/Connection.js'
import moment from 'src/external/moment.js'
import ViewNav from 'src/client/viewnav.js'

export function isCurrentlyCloning() {
    return sessionStorage["lively.persistenceCurrentlyCloning"] === 'true';
}

export default class Persistence {

  constructor() {
    this.saveDelay = (() => {
        this.saveLivelyContent();
        this.showMutationIndicator().style.backgroundColor = "rgba(10,10,10,0.3)"
    })::debounce(3000) // save the world 3seconds after a change
  }

  // work around non static module global state
  static get current() {
    if (!window.lively4persistence) {
      window.lively4persistence = new Persistence()
    }
    return window.lively4persistence
  }
  static set current(obj) {
    window.lively4persistence = obj
  }

  static enable() {
    this.disable()
    this.current = new Persistence()
    this.current.start()
  }

  static disable() {
    if (this.current) {
      this.current.stop()
    }
  }

  start() {
    for(let ea of Persistence.backupIntervals()) {
      this.ensureLivelyContentBackup(undefined, ea)

    }
    this.observeHTMLChanges()
  }

  stop() {
    if (this.mutationObserver) this.mutationObserver.disconnect()
  }

  urlToKey(urlString) {
    urlString = "" + urlString
    return "livelyWindows_"+urlString.replace(/^https?:\/\//,"").replace(/[^A-Za-z0-9]/,"_")
  }

  defaultTarget() {
    return document.body
  }

  defaultURL() {
    return (document.location.protocol + "//" + document.location.hostname + document.location.pathname)
  }

  async getLivelyContentForURL(url) {
    url = url || this.defaultURL()
    var content = await focalStorage.getItem(this.urlToKey(url.toString()))
    if (!content) {
      try {
        var elementSource = await lively.files.loadFile(lively4url + "/src/parts/initial-content.html")
        content = lively.html.parseHTML(elementSource)[0].innerHTML
      } catch(e) {
        lively.error("Could not load default desktop conent for lively", e)
      }
    }
    return content
  }

  setLivelyContentForURL(url, source) {
    url = url || this.defaultURL()
    return focalStorage.setItem(this.urlToKey(url.toString()), source)
  }

  async loadLivelyContentForURL(url, target) {
    var source = await this.getLivelyContentForURL(url)
    return this.loadLivelyContent(source, target)
  }

  async loadLivelyContent(source, target) {
    target = target || this.defaultTarget()
    var div = document.createElement("div")
    div.innerHTML = source
    var topLevel = Array.from(div.querySelectorAll(":scope > *"))
    var objs = Array.from(div.childNodes)

    // somehow zIndex gets lost...
    var zIndexMap = new Map()
    topLevel.forEach(ea => {
       zIndexMap.set(ea, ea.style.zIndex)
    })

    objs.map(ea => {
      if(ea.classList && ea.classList.contains('lively-preferences')) {
        target.querySelectorAll('div.lively-preferences').forEach(ele => ele.remove());
      }
      target.appendChild(ea);
    });
    await lively.components.loadUnresolved(target, true, "loadLivelyContentForURL", true)

    // restore zIndex in an Async way... it seems focus is responsible for it #Hack
    lively.sleep(0).then(() => {
      topLevel.forEach(ea => {
        ea.style.zIndex = zIndexMap.get(ea)
      })
    })

    objs.map(ea => { Persistence.initLivelyObject(ea)})
    return
  }

  static initLivelyObject(obj) {
    if (!obj) return;
    try {
      scriptManager.findLively4Script(obj, false);
      if (obj.livelyLoad) obj.livelyLoad()
      Connection.deserializeFromObjectIfNeeded(obj)
      if (!obj.querySelectorAll) return;
      obj.querySelectorAll("*").forEach(ea => {
        if (ea.livelyLoad) ea.livelyLoad()
        Connection.deserializeFromObjectIfNeeded(ea)
      })
    } catch(e) {
      lively.showError(e)
    }
  }

  async ensureLivelyContentBackup(url=this.defaultURL(), interval="yesterday", force) {
    var key = this.urlToKey(url.toString())
    const datePrefix = `backup_${interval}_date_`
    const sourcePrefix = `backup_${interval}_source_`

    var dateString = await focalStorage.getItem(datePrefix+ key)
    var lastBackupDate = moment(dateString)
    var today = moment()
    if (force || !dateString || lastBackupDate.diff(today, "minutes") > this.minutesForBackupInterval(interval)) {
      var source = await focalStorage.getItem(key) // get current source ... @onsetsu or should we use current world?

      await focalStorage.setItem(datePrefix + key, today.format("YYYY-MM-DD HH:MM:SS"))
      await focalStorage.setItem(sourcePrefix + key, source)
      lively.notify(`Ensured ${interval} backup!`)
    } else {
      lively.notify(`Last ${interval} backup is not old enough`)
    }
  }

  async loadLivelyContentBackup(url=this.defaultURL(), interval="yesterday", target=this.defaultTarget()) {
    var key = this.urlToKey(url.toString())
    const sourcePrefix = `backup_${interval}_source_`
    var source = await focalStorage.getItem(sourcePrefix + key)
    if (source) {
      this.loadLivelyContent(source, target)
      lively.notify(`load ${interval} backup!`)
    } else {
      lively.notify(`could not load ${interval} backup!`)
    }
  }

  async storeLivelyContentForURL(url, target) {
    target = target || this.defaultTarget()
    this.isPersisting = true
    var source = lively.html.getGlobalSource(target)
    this.setLivelyContentForURL(url, source)
    setTimeout(() => {
      this.isPersisting = false
    }, 0) // the mutation events triggered are still pending here...
    return source
  }

  async saveLivelyContent() {
    // console.log("[peristence] save after " +
    //   (Date.now() - this.lastSaved) +"ms")
    this.lastSaved = Date.now()
    await this.storeLivelyContentForURL()
    // console.log("[peristence] saved lively content into focalStorage " +
    //   (Date.now() - this.lastSaved) +"ms")
  }

  hasDoNotPersistTag(node,) {
    var donotperist =  node.attributes &&
      node.attributes.hasOwnProperty('data-lively4-donotpersist');
    if (donotperist) return true
    if (!node.parentElement) return false
    return this.hasDoNotPersistTag(node.parentElement)
  }

  isBlacklisted(mutation) {
    if (mutation.target.tagName == "BODY") return true
    if (mutation.target.tagName == "LIVELY-MENU") return true
    if (mutation.target.tagName == "LIVELY-SELECTION") return true
    if (mutation.target.tagName == "LIVELY-HAND") return true
    if (mutation.target.tagName == "LIVELY-HALO") return true
    if (mutation.target.tagName == "LIVELY-NOTIFICATION-LIST") return true
    if (mutation.target.id == "mutationIndicator") return true
    if (mutation.target.getAttribute && mutation.target.getAttribute("data-is-meta")) return true
    if (this.hasDoNotPersistTag(mutation.target)) return true
    return false
  }

  showMutationIndicator() {
    var indicator = document.body.querySelector("#mutationIndicator")
    if (!indicator)  {
      var div = document.createElement("div")
      div.id = "mutationIndicator"
      document.body.appendChild(div)
      div.style.position = "fixed"
      div.style.right = "0px"
      div.style.top = "0px"
      div.style.width = "10px"
      div.style.height = "10px"
      div.style.backgroundColor = "blue"
      div.style.pointerEvents = "none"
      div.style.zIndex = 2000

      indicator = div
    }
    return indicator
  }

  onMutation(mutations, observer) {
    if (this.isPersisting) {
      // console.log("ignore mutation ", mutations)
      return // we mutate while persisting
    }

    mutations.filter(ea => !this.isBlacklisted(ea)).forEach(record => {
      this.showMutationIndicator().style.backgroundColor = "rgba(200,0,0,0.5)"
      this.saveDelay()
    })
  }

  observeHTMLChanges(target) {
    target = target || this.defaultTarget()
    if (this.mutationObserver) this.mutationObserver.disconnect()
    this.mutationObserver = new MutationObserver((mutations, observer) => {
        this.onMutation(mutations, observer)
    });
    this.mutationObserver.observe(target, {
      childList: true,
      subtree: true,
      characterData: true,
      attributes: true});
  }

  minutesForBackupInterval(interval) {
    // #TODO refactor... this duplication does not look nice
    if (interval === "yesterday") {
      return 1 * 24 * 60
    }
    if (interval === "hour") {
      return  1 * 60
    }
    if (interval === "twohours") {
      return  2 * 60
    }
    if (interval === "week") {
      return  7 * 24 * 60
    }
    throw new Error(interval + " not supported")
  }

  static backupIntervals() {
    return ["hour", "twohours", "yesterday", "week"]
  }

  static restoreBackupContextMenuItems() {
    return this.backupIntervals().map(ea => {
      return this.restoreBackupContextMenuItem(ea)
    })
  }

  static restoreBackupContextMenuItem(interval) {
    return [interval, async (evt) => {
          // #TODO refactor into component so that behavior is persistent
          var target =

          var scrollPane =
{target}

          ViewNav.enable(target)
          var ui =
{scrollPane}

                <button click={() => {

                      var contents = Array.from(target.childNodes)
                      document.body.innerHTML = ""
                      for(let ea of contents) {
                        document.body.appendChild(ea)
                      }

                      lively.notify("would restore " + contents.length + " elements" )

                  }}>restore</button>

          var win = await (<lively-window title={"Restore " + interval}>{ui}</lively-window>)
          document.body.appendChild(win)
          lively.setGlobalPosition(win, lively.getPosition(evt))
          lively.persistence.current.loadLivelyContentBackup(undefined, interval, target)
        }
      ]
  }

}
<|endoftext|>


<commit_before>'use strict';

module.exports = function (grunt) {

    // Time how long tasks take. Can help when optimizing build times
    require('time-grunt')(grunt);

    // Automatically load required grunt tasks
    require('jit-grunt')(grunt, {
        useminPrepare: 'grunt-usemin',
        s3: 'grunt-aws',
        replace: 'grunt-text-replace'
    });

    // configurable paths
    var config = {
        app: 'app',
        dist: 'dist'
    };

    grunt.initConfig({

        // Project settings
        config: config,
        credentials: grunt.file.readJSON('credentials.json'),
        package: grunt.file.readJSON('package.json'),

        // Watches files for changes and runs tasks based on the changed files
        watch: {
            gruntfile: {
                files: ['Gruntfile.js'],
                tasks: ['wiredep']
            },
            bower: {
                files: ['bower.json'],
                tasks: ['wiredep']
            },
            assemble: {
                files: [
                    '<%= config.app %>/data/{,*/}*.{yml,json}',
                    '<%= config.app %>/{layouts,pages,partials}/{,**/}*.hbs'
                ],
                tasks: [
                    'assemble',
                    'wiredep'
                ]
            },
            js: {
                files: [
                    '<%= config.app %>/scripts/{,*/}*.js'
                ],
                tasks: ['jshint'],
                options: {
                    spawn: false,
                },
            },
            sass: {
                files: ['<%= config.app %>/styles/{,*/}*.scss'],
                tasks: [
                    'sass:server',
                    'autoprefixer'
                ]
            },
            webfont: {
                files: [
                    '<%= config.app %>/media/icons/*.svg'
                ],
                tasks: ['webfont']
            },
        },

        browserSync: {
            options: {
                port: 0,
                notify: false,
                background: true
            },
            livereload: {
                options: {
                    files: [
                        '.tmp/{,*/}*.html',
                        '.tmp/styles/{,*/}*.css',
                        '<%= config.app %>/scripts/{,*/}*.js',
                        '<%= config.app %>/media/{,**/}*',
                        '!<%= config.app %>/media/icons/*'
                    ],
                    server: {
                        baseDir: ['.tmp', config.app],
                        routes: {
                            '/bower_components': './bower_components'
                        }
                    }
                }
            },
            dist: {
                options: {
                    background: false,
                    server: '<%= config.dist %>'
                }
            }
        },

        // Empties folders to start fresh
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '.sass-cache',
                        '<%= config.dist %>/*',
                        '!<%= config.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },

        // Generate pages
        assemble: {
            options: {
                root: '.tmp',
                layout: 'default.hbs',
                layoutdir: '<%= config.app %>/layouts',
                assets: '<%= config.app %>',
                data: '<%= config.app %>/data/*.{json,yml}',
                partials: '<%= config.app %>/partials/{,*/}*.hbs',
                plugins: ['grunt-assemble-sitemap'],
                sitemap: {
                    relativedest: '.tmp'
                }
            },
            pages: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/pages',
                    dest: '.tmp',
                    src: '{,**/}*.hbs'
                }]
            }
        },

        // Compile Sass
        sass: {
            options: {
                sourceMap: true,
                includePaths: [
                    '<%= config.app %>',
                    'bower_components'
                ]
            },
            server: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>',
                    src: ['{,*/}*.scss'],
                    dest: '.tmp',
                    ext: '.css'
                }]
            }
        },

        // Add vendor prefixed styles
        autoprefixer: {
            options: {
                browsers: ['> 1%', 'last 2 versions', 'Firefox ESR', 'Opera 12.1'],
                map: {
                    prev: '.tmp/styles/'
                }
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/',
                    src: '{,*/}*.css',
                    dest: '.tmp/'
                }]
            }
        },

        // Generate custom icon font
        webfont: {
            icons: {
                src: '<%= config.app %>/media/icons/*.svg',
                dest: '.tmp/media/fonts',
                destCss: '.tmp/',
                options: {
                    engine: 'node',
                    hashes: false,
                    stylesheet: 'css',
                    relativeFontPath: '/media/fonts',
                    templateOptions: {
                        classPrefix: 'icon-',
                    }
                }
            }
        },

        // Reads HTML for usemin blocks to enable smart builds that automatically
        // concat, minify and revision files. Creates configurations in memory so
        // additional tasks can operate on them
        useminPrepare: {
            options: {
                dest: '<%= config.dist %>'
            },
            html: '.tmp/index.html'
        },

        // Performs rewrites based on rev and the useminPrepare configuration
        usemin: {
            options: {
                assetsDirs: ['<%= config.dist %>']
            },
            html: ['<%= config.dist %>/{,**/}*.html'],
            css: ['<%= config.dist %>/{,*/}*.css']
        },

        // The following *-min tasks produce minified files in the dist folder
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/media',
                    src: '{,**/}*.{png,jpg,jpeg}',
                    dest: '<%= config.dist %>/media'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/media',
                    src: '{,**/}*.svg',
                    dest: '<%= config.dist %>/media'
                }]
            }
        },
        htmlmin: {
            dist: {
                options: {
                    collapseBooleanAttributes: true,
                    collapseWhitespace: true,
                    conservativeCollapse: true,
                    removeAttributeQuotes: true,
                    removeCommentsFromCDATA: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true,
                    removeRedundantAttributes: false,
                    useShortDoctype: true
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.dist %>',
                    src: '{,**/}*.html',
                    dest: '<%= config.dist %>'
                }]
            }
        },

        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= config.app %>',
                    dest: '<%= config.dist %>',
                    src: [
                        '.htaccess',
                        '*.txt',
                        'media/{,**/}*.{webp,gif,ico}',
                        'media/fonts/*'
                    ]
                },{
                    expand: true,
                    dot: true,
                    cwd: '<%= config.app %>',
                    dest: '<%= config.dist %>',
                    flatten: true,
                    src: [
                        'media/favicon.ico'
                    ]
                },{
                    expand: true,
                    dot: true,
                    cwd: '.tmp',
                    dest: '<%= config.dist %>',
                    src: [
                        'media/fonts/*',
                        'robots.txt',
                        'sitemap.xml'
                    ]
                },{
                    expand: true,
                    dot: true,
                    cwd: '.tmp',
                    dest: '<%= config.dist %>',
                    src: [
                        '{,**/}*.html',
                        '!icons.html'
                    ]
                },{
                    expand: true,
                    dot: true,
                    cwd: 'bower_components',
                    dest: '<%= config.dist %>/bower_components',
                    src: [
                        'bootstrap-sass-official/assets/fonts/bootstrap/*'
                    ]
                }]
            }
        },

        // Automatically inject Bower components into the HTML file
        wiredep: {
            app: {
                src: '.tmp/{,**/}*.html',
                ignorePath: '../',
                exclude: []
            }
        },

        // Make sure code styles are up to par and there are no obvious mistakes
        jshint: {
            options: {
                jshintrc: '.jshintrc',
                force: true,
                reporter: require('jshint-stylish')
            },
            all: [
                'Gruntfile.js',
                '<%= config.app %>/scripts/{,*/}*.js'
            ]
        },

        // Pick an unused port for livereload
        portPick: {
            browserSync: {
                options: {
                    port: 9000
                },
                targets: ['browserSync.options.port']
            }
        },

        // Renames files for browser caching purposes
        filerev: {
            dist: {
                src: [
                    '<%= config.dist %>/scripts/{,*/}*.js',
                    '<%= config.dist %>/styles/{,*/}*.css',
                    '<%= config.dist %>/{media,bower_components}/{,**/}*.{png,jpg,jpeg,gif,webp,svg,eot,ttf,woff,woff2}'
                ]
            }
        },

        // Run some tasks in parallel to speed up build process
        concurrent: {
            server: [
                'webfont',
                'sass:server',
            ],
            dist: [
                'webfont',
                'sass',
                'imagemin',
                'svgmin'
            ]
        },

        // Deploy to AWS S3
        s3: {
            options: {
                accessKeyId: '<%= credentials.aws.accessKeyId %>',
                secretAccessKey: '<%= credentials.aws.secretAccessKey %>',
                signatureVersion: 'v4',
                headers: {
                    CacheControl: 'public, no-cache'
                }
            },
            dist: {
                options: {
                    bucket: 'yw-test-bucket',
                },
                cwd: '<%= config.dist %>',
                src: '**'
            }
        },

        // Increment version number, create and push tag
        bump: {
            options: {
                files: [
                    'package.json',
                    'bower.json'
                ],
                commitFiles: '<%= bump.options.files %>',
                pushTo: 'origin'
            }
        },

        // Text replacements
        replace: {
            openGraphTag: {
                src: [
                    '<%= config.dist %>/{,**/}*.html',
                ],
                overwrite: true,
                replacements: [{
                    from: /<meta property=og:(image|url) content="?(\/[^"?>]+)"?/g,
                    to: '<meta property=og:$1 content=<%= package.homepage %>$2'
                }]
            },
            relCanonical: {
                src: [
                    '<%= config.dist %>/{,**/}*.html',
                ],
                overwrite: true,
                replacements: [{
                    from: /<link rel=canonical href="?(\/[^"?>]+)"?/g,
                    to: '<link rel=canonical href=<%= package.homepage %>$1'
                }]
            },
            sitemap: {
                src: [
                    '<%= config.dist %>/sitemap.xml',
                ],
                overwrite: true,
                replacements: [
                    {
                        from: 'index.html',
                        to: ''
                    }, {
                        from: /\s+<changefreq>[a-z]+<\/changefreq>/g,
                        to: ''
                    }, {
                        from: /\s+<priority>[0-9\.]+<\/priority>/g,
                        to: ''
                    }

                ]
            }
        },

    });

    grunt.registerTask('serve', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'portPick:browserSync', 'browserSync:dist']);
        }

        grunt.task.run([
            'clean:server',
            'assemble',
            'wiredep',
            'portPick',
            'concurrent:server',
            'autoprefixer',
            'browserSync:livereload',
            'watch'
        ]);
    });

    grunt.registerTask('test', [
        'jshint'
    ]);

    grunt.registerTask('build', [
        'clean:dist',
        'assemble',
        'wiredep',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'concat',
        'cssmin',
        'uglify',
        'copy:dist',
        'filerev',
        'usemin',
        'htmlmin'
    ]);

    grunt.registerTask('deploy', [
        'build',
        'replace',
        's3'
    ]);

    grunt.registerTask('default', [
        'jshint',
        'build'
    ]);
};
<commit_msg>Adding `layoutext` option to assemble<commit_after>'use strict';

module.exports = function (grunt) {

    // Time how long tasks take. Can help when optimizing build times
    require('time-grunt')(grunt);

    // Automatically load required grunt tasks
    require('jit-grunt')(grunt, {
        useminPrepare: 'grunt-usemin',
        s3: 'grunt-aws',
        replace: 'grunt-text-replace'
    });

    // configurable paths
    var config = {
        app: 'app',
        dist: 'dist'
    };

    grunt.initConfig({

        // Project settings
        config: config,
        credentials: grunt.file.readJSON('credentials.json'),
        package: grunt.file.readJSON('package.json'),

        // Watches files for changes and runs tasks based on the changed files
        watch: {
            gruntfile: {
                files: ['Gruntfile.js'],
                tasks: ['wiredep']
            },
            bower: {
                files: ['bower.json'],
                tasks: ['wiredep']
            },
            assemble: {
                files: [
                    '<%= config.app %>/data/{,*/}*.{yml,json}',
                    '<%= config.app %>/{layouts,pages,partials}/{,**/}*.hbs'
                ],
                tasks: [
                    'assemble',
                    'wiredep'
                ]
            },
            js: {
                files: [
                    '<%= config.app %>/scripts/{,*/}*.js'
                ],
                tasks: ['jshint'],
                options: {
                    spawn: false,
                },
            },
            sass: {
                files: ['<%= config.app %>/styles/{,*/}*.scss'],
                tasks: [
                    'sass:server',
                    'autoprefixer'
                ]
            },
            webfont: {
                files: [
                    '<%= config.app %>/media/icons/*.svg'
                ],
                tasks: ['webfont']
            },
        },

        browserSync: {
            options: {
                port: 0,
                notify: false,
                background: true
            },
            livereload: {
                options: {
                    files: [
                        '.tmp/{,*/}*.html',
                        '.tmp/styles/{,*/}*.css',
                        '<%= config.app %>/scripts/{,*/}*.js',
                        '<%= config.app %>/media/{,**/}*',
                        '!<%= config.app %>/media/icons/*'
                    ],
                    server: {
                        baseDir: ['.tmp', config.app],
                        routes: {
                            '/bower_components': './bower_components'
                        }
                    }
                }
            },
            dist: {
                options: {
                    background: false,
                    server: '<%= config.dist %>'
                }
            }
        },

        // Empties folders to start fresh
        clean: {
            dist: {
                files: [{
                    dot: true,
                    src: [
                        '.tmp',
                        '.sass-cache',
                        '<%= config.dist %>/*',
                        '!<%= config.dist %>/.git*'
                    ]
                }]
            },
            server: '.tmp'
        },

        // Generate pages
        assemble: {
            options: {
                root: '.tmp',
                layout: 'default',
                layoutext: '.hbs',
                layoutdir: '<%= config.app %>/layouts',
                assets: '<%= config.app %>',
                data: '<%= config.app %>/data/*.{json,yml}',
                partials: '<%= config.app %>/partials/{,*/}*.hbs',
                plugins: ['grunt-assemble-sitemap'],
                sitemap: {
                    relativedest: '.tmp'
                }
            },
            pages: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/pages',
                    dest: '.tmp',
                    src: '{,**/}*.hbs'
                }]
            }
        },

        // Compile Sass
        sass: {
            options: {
                sourceMap: true,
                includePaths: [
                    '<%= config.app %>',
                    'bower_components'
                ]
            },
            server: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>',
                    src: ['{,*/}*.scss'],
                    dest: '.tmp',
                    ext: '.css'
                }]
            }
        },

        // Add vendor prefixed styles
        autoprefixer: {
            options: {
                browsers: ['> 1%', 'last 2 versions', 'Firefox ESR', 'Opera 12.1'],
                map: {
                    prev: '.tmp/styles/'
                }
            },
            dist: {
                files: [{
                    expand: true,
                    cwd: '.tmp/',
                    src: '{,*/}*.css',
                    dest: '.tmp/'
                }]
            }
        },

        // Generate custom icon font
        webfont: {
            icons: {
                src: '<%= config.app %>/media/icons/*.svg',
                dest: '.tmp/media/fonts',
                destCss: '.tmp/',
                options: {
                    engine: 'node',
                    hashes: false,
                    stylesheet: 'css',
                    relativeFontPath: '/media/fonts',
                    templateOptions: {
                        classPrefix: 'icon-',
                    }
                }
            }
        },

        // Reads HTML for usemin blocks to enable smart builds that automatically
        // concat, minify and revision files. Creates configurations in memory so
        // additional tasks can operate on them
        useminPrepare: {
            options: {
                dest: '<%= config.dist %>'
            },
            html: '.tmp/index.html'
        },

        // Performs rewrites based on rev and the useminPrepare configuration
        usemin: {
            options: {
                assetsDirs: ['<%= config.dist %>']
            },
            html: ['<%= config.dist %>/{,**/}*.html'],
            css: ['<%= config.dist %>/{,*/}*.css']
        },

        // The following *-min tasks produce minified files in the dist folder
        imagemin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/media',
                    src: '{,**/}*.{png,jpg,jpeg}',
                    dest: '<%= config.dist %>/media'
                }]
            }
        },
        svgmin: {
            dist: {
                files: [{
                    expand: true,
                    cwd: '<%= config.app %>/media',
                    src: '{,**/}*.svg',
                    dest: '<%= config.dist %>/media'
                }]
            }
        },
        htmlmin: {
            dist: {
                options: {
                    collapseBooleanAttributes: true,
                    collapseWhitespace: true,
                    conservativeCollapse: true,
                    removeAttributeQuotes: true,
                    removeCommentsFromCDATA: true,
                    removeEmptyAttributes: true,
                    removeOptionalTags: true,
                    removeRedundantAttributes: false,
                    useShortDoctype: true
                },
                files: [{
                    expand: true,
                    cwd: '<%= config.dist %>',
                    src: '{,**/}*.html',
                    dest: '<%= config.dist %>'
                }]
            }
        },

        // Put files not handled in other tasks here
        copy: {
            dist: {
                files: [{
                    expand: true,
                    dot: true,
                    cwd: '<%= config.app %>',
                    dest: '<%= config.dist %>',
                    src: [
                        '.htaccess',
                        '*.txt',
                        'media/{,**/}*.{webp,gif,ico}',
                        'media/fonts/*'
                    ]
                },{
                    expand: true,
                    dot: true,
                    cwd: '<%= config.app %>',
                    dest: '<%= config.dist %>',
                    flatten: true,
                    src: [
                        'media/favicon.ico'
                    ]
                },{
                    expand: true,
                    dot: true,
                    cwd: '.tmp',
                    dest: '<%= config.dist %>',
                    src: [
                        'media/fonts/*',
                        'robots.txt',
                        'sitemap.xml'
                    ]
                },{
                    expand: true,
                    dot: true,
                    cwd: '.tmp',
                    dest: '<%= config.dist %>',
                    src: [
                        '{,**/}*.html',
                        '!icons.html'
                    ]
                },{
                    expand: true,
                    dot: true,
                    cwd: 'bower_components',
                    dest: '<%= config.dist %>/bower_components',
                    src: [
                        'bootstrap-sass-official/assets/fonts/bootstrap/*'
                    ]
                }]
            }
        },

        // Automatically inject Bower components into the HTML file
        wiredep: {
            app: {
                src: '.tmp/{,**/}*.html',
                ignorePath: '../',
                exclude: []
            }
        },

        // Make sure code styles are up to par and there are no obvious mistakes
        jshint: {
            options: {
                jshintrc: '.jshintrc',
                force: true,
                reporter: require('jshint-stylish')
            },
            all: [
                'Gruntfile.js',
                '<%= config.app %>/scripts/{,*/}*.js'
            ]
        },

        // Pick an unused port for livereload
        portPick: {
            browserSync: {
                options: {
                    port: 9000
                },
                targets: ['browserSync.options.port']
            }
        },

        // Renames files for browser caching purposes
        filerev: {
            dist: {
                src: [
                    '<%= config.dist %>/scripts/{,*/}*.js',
                    '<%= config.dist %>/styles/{,*/}*.css',
                    '<%= config.dist %>/{media,bower_components}/{,**/}*.{png,jpg,jpeg,gif,webp,svg,eot,ttf,woff,woff2}'
                ]
            }
        },

        // Run some tasks in parallel to speed up build process
        concurrent: {
            server: [
                'webfont',
                'sass:server',
            ],
            dist: [
                'webfont',
                'sass',
                'imagemin',
                'svgmin'
            ]
        },

        // Deploy to AWS S3
        s3: {
            options: {
                accessKeyId: '<%= credentials.aws.accessKeyId %>',
                secretAccessKey: '<%= credentials.aws.secretAccessKey %>',
                signatureVersion: 'v4',
                headers: {
                    CacheControl: 'public, no-cache'
                }
            },
            dist: {
                options: {
                    bucket: 'yw-test-bucket',
                },
                cwd: '<%= config.dist %>',
                src: '**'
            }
        },

        // Increment version number, create and push tag
        bump: {
            options: {
                files: [
                    'package.json',
                    'bower.json'
                ],
                commitFiles: '<%= bump.options.files %>',
                pushTo: 'origin'
            }
        },

        // Text replacements
        replace: {
            openGraphTag: {
                src: [
                    '<%= config.dist %>/{,**/}*.html',
                ],
                overwrite: true,
                replacements: [{
                    from: /<meta property=og:(image|url) content="?(\/[^"?>]+)"?/g,
                    to: '<meta property=og:$1 content=<%= package.homepage %>$2'
                }]
            },
            relCanonical: {
                src: [
                    '<%= config.dist %>/{,**/}*.html',
                ],
                overwrite: true,
                replacements: [{
                    from: /<link rel=canonical href="?(\/[^"?>]+)"?/g,
                    to: '<link rel=canonical href=<%= package.homepage %>$1'
                }]
            },
            sitemap: {
                src: [
                    '<%= config.dist %>/sitemap.xml',
                ],
                overwrite: true,
                replacements: [
                    {
                        from: 'index.html',
                        to: ''
                    }, {
                        from: /\s+<changefreq>[a-z]+<\/changefreq>/g,
                        to: ''
                    }, {
                        from: /\s+<priority>[0-9\.]+<\/priority>/g,
                        to: ''
                    }

                ]
            }
        },

    });

    grunt.registerTask('serve', function (target) {
        if (target === 'dist') {
            return grunt.task.run(['build', 'portPick:browserSync', 'browserSync:dist']);
        }

        grunt.task.run([
            'clean:server',
            'assemble',
            'wiredep',
            'portPick',
            'concurrent:server',
            'autoprefixer',
            'browserSync:livereload',
            'watch'
        ]);
    });

    grunt.registerTask('test', [
        'jshint'
    ]);

    grunt.registerTask('build', [
        'clean:dist',
        'assemble',
        'wiredep',
        'useminPrepare',
        'concurrent:dist',
        'autoprefixer',
        'concat',
        'cssmin',
        'uglify',
        'copy:dist',
        'filerev',
        'usemin',
        'htmlmin'
    ]);

    grunt.registerTask('deploy', [
        'build',
        'replace',
        's3'
    ]);

    grunt.registerTask('default', [
        'jshint',
        'build'
    ]);
};
<|endoftext|>


<commit_before>/*
  backgrid
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
  Licensed under the MIT license.
*/

// jshint globalstrict:true, node:true

"use strict";

module.exports = function (grunt) {

  grunt.initConfig({

    pkg: grunt.file.readJSON("package.json"),

    clean: {
      options: {
        force: true
      },
      lib: [
        "lib/*.js",
        "lib/*.css"
      ],
      api: [
        "api/**/*"
      ],
      default: [
        "api/**/*",
        "lib/*.js",
        "lib/*.css"
      ]
    },

    concat: {
      backgrid: {
        options: {
          banner: '/*!\n  <%= pkg.name %> <%= pkg.version %>\n' +
            '  <%= pkg.repository.url %>\n\n' +
            '  Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
            '  Licensed under the MIT license.\n' +
            '*/\n\n' +
            '(function (root, factory) {\n\n' +
            '  if (typeof define === "function" && define.amd) {\n' +
            '    // AMD (+ global for extensions)\n' +
            '    define(["underscore", "backbone"], function (_, Backbone) {\n' +
            '      return (root.Backgrid = factory(_, Backbone));\n' +
            '    });\n' +
            '  } else if (typeof exports === "object") {\n' +
            '    // CommonJS\n' +
            '    module.exports = factory(require("underscore"), require("backbone"));\n' +
            '  } else {\n' +
            '    // Browser\n' +
            '    root.Backgrid = factory(root._, root.Backbone);\n' +
            '  }' +
            '}(this, function (_, Backbone) {\n\n  "use strict";\n\n',
          footer: '  return Backgrid;\n' +
            '}));'
        },
        src: [
          "src/preamble.js",
          "src/formatter.js",
          "src/cell.js",
          "src/column.js",
          "src/row.js",
          "src/header.js",
          "src/body.js",
          "src/footer.js",
          "src/grid.js"
        ],
        dest: "lib/backgrid.js"
      }
    },

    connect: {
      server: {
        options: {
          keepalive: true
        }
      }
    },

    jasmine: {
      test: {
        version: "1.3.1",
        src: [
          "lib/backgrid.js",
        ],
        options: {
          specs: [
            "test/preamble.js",
            "test/column.js",
            "test/formatter.js",
            "test/cell.js",
            "test/row.js",
            "test/body.js",
            "test/header.js",
            "test/footer.js",
            "test/grid.js"
          ],
          template: require("grunt-template-jasmine-istanbul"),
          templateOptions: {
            coverage: "test/coverage/coverage.json",
            report: {
              type: "html",
              options: {
                dir: "test/coverage"
              }
            }
          },
          helpers: "vendor/js/jasmine-html.js",
          vendor: [
            "test/vendor/js/jquery.js",
            "test/vendor/js/underscore.js",
            "test/vendor/js/backbone.js",
            "test/vendor/js/backbone-pageable.js"
          ]
        }
      }
    },

    jsduck: {
      main: {
        src: ["src"],
        dest: "api",
        options: {
          "external": ["Backbone.Model,Backbone.Collection,Backbone.View"],
          "title": "Backgrid.js",
          "no-source": true,
          "categories": "categories.json",
          "warnings": "-no_doc",
          "pretty-json": true,
          "body-html": '<script type="text/javascript">\n' +
            '  var _gaq = _gaq || [];\n' +
            '  _gaq.push(["_setAccount", "UA-36403214-1"]);\n' +
            '  _gaq.push(["_setDomainName", "backgridjs.com"]);\n' +
            '  _gaq.push(["_setAllowLinker", true]);\n' +
            '  _gaq.push(["_trackPageview"]);\n' +
            '  (function() {\n' +
            '    var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;\n' +
            '    ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";\n' +
            '    var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);\n' +
            '  })();\n' +
          '</script>'
        }
      }
    },

    recess: {
      csslint: {
        options: {
          compile: true
        },
        files: {
          "lib/backgrid.css": ["src/backgrid.css"]
        }
      },
      default: {
        options: {
          compress: true
        },
        files: {
          "lib/backgrid.min.css": ["src/backgrid.css"]
        }
      }
    },

    uglify: {
      options: {
        mangle: true,
        compress: true,
        preserveComments: "some"
      },
      default: {
        files: {
          "lib/backgrid.min.js": ["./lib/backgrid.js"]
        }
      }
    },

    watch: {
      default: {
        files: ["src/**/*.*"],
        tasks: ["dist"]
      }
    }
  });

  grunt.loadNpmTasks("grunt-contrib-clean");
  grunt.loadNpmTasks("grunt-contrib-concat");
  grunt.loadNpmTasks("grunt-contrib-uglify");
  grunt.loadNpmTasks("grunt-contrib-watch");
  grunt.loadNpmTasks("grunt-recess");
  grunt.loadNpmTasks("grunt-jsduck");
  grunt.loadNpmTasks("grunt-contrib-jasmine");
  grunt.loadNpmTasks("grunt-contrib-connect");

  grunt.registerTask("doc", ["clean:api", "jsduck"]);
  grunt.registerTask("dist", ["concat", "uglify", "recess"]);
  grunt.registerTask("test", ["concat", "jasmine"]);
  grunt.registerTask("default", ["clean", "doc", "dist", "jasmine"]);
};
<commit_msg>fix gruntfile uglify:compress to {}<commit_after>/*
  backgrid
  http://github.com/wyuenho/backgrid

  Copyright (c) 2013 Jimmy Yuen Ho Wong and contributors
  Licensed under the MIT license.
*/

// jshint globalstrict:true, node:true

"use strict";

module.exports = function (grunt) {

  grunt.initConfig({

    pkg: grunt.file.readJSON("package.json"),

    clean: {
      options: {
        force: true
      },
      lib: [
        "lib/*.js",
        "lib/*.css"
      ],
      api: [
        "api/**/*"
      ],
      default: [
        "api/**/*",
        "lib/*.js",
        "lib/*.css"
      ]
    },

    concat: {
      backgrid: {
        options: {
          banner: '/*!\n  <%= pkg.name %> <%= pkg.version %>\n' +
            '  <%= pkg.repository.url %>\n\n' +
            '  Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>\n' +
            '  Licensed under the MIT license.\n' +
            '*/\n\n' +
            '(function (root, factory) {\n\n' +
            '  if (typeof define === "function" && define.amd) {\n' +
            '    // AMD (+ global for extensions)\n' +
            '    define(["underscore", "backbone"], function (_, Backbone) {\n' +
            '      return (root.Backgrid = factory(_, Backbone));\n' +
            '    });\n' +
            '  } else if (typeof exports === "object") {\n' +
            '    // CommonJS\n' +
            '    module.exports = factory(require("underscore"), require("backbone"));\n' +
            '  } else {\n' +
            '    // Browser\n' +
            '    root.Backgrid = factory(root._, root.Backbone);\n' +
            '  }' +
            '}(this, function (_, Backbone) {\n\n  "use strict";\n\n',
          footer: '  return Backgrid;\n' +
            '}));'
        },
        src: [
          "src/preamble.js",
          "src/formatter.js",
          "src/cell.js",
          "src/column.js",
          "src/row.js",
          "src/header.js",
          "src/body.js",
          "src/footer.js",
          "src/grid.js"
        ],
        dest: "lib/backgrid.js"
      }
    },

    connect: {
      server: {
        options: {
          keepalive: true
        }
      }
    },

    jasmine: {
      test: {
        version: "1.3.1",
        src: [
          "lib/backgrid.js",
        ],
        options: {
          specs: [
            "test/preamble.js",
            "test/column.js",
            "test/formatter.js",
            "test/cell.js",
            "test/row.js",
            "test/body.js",
            "test/header.js",
            "test/footer.js",
            "test/grid.js"
          ],
          template: require("grunt-template-jasmine-istanbul"),
          templateOptions: {
            coverage: "test/coverage/coverage.json",
            report: {
              type: "html",
              options: {
                dir: "test/coverage"
              }
            }
          },
          helpers: "vendor/js/jasmine-html.js",
          vendor: [
            "test/vendor/js/jquery.js",
            "test/vendor/js/underscore.js",
            "test/vendor/js/backbone.js",
            "test/vendor/js/backbone-pageable.js"
          ]
        }
      }
    },

    jsduck: {
      main: {
        src: ["src"],
        dest: "api",
        options: {
          "external": ["Backbone.Model,Backbone.Collection,Backbone.View"],
          "title": "Backgrid.js",
          "no-source": true,
          "categories": "categories.json",
          "warnings": "-no_doc",
          "pretty-json": true,
          "body-html": '<script type="text/javascript">\n' +
            '  var _gaq = _gaq || [];\n' +
            '  _gaq.push(["_setAccount", "UA-36403214-1"]);\n' +
            '  _gaq.push(["_setDomainName", "backgridjs.com"]);\n' +
            '  _gaq.push(["_setAllowLinker", true]);\n' +
            '  _gaq.push(["_trackPageview"]);\n' +
            '  (function() {\n' +
            '    var ga = document.createElement("script"); ga.type = "text/javascript"; ga.async = true;\n' +
            '    ga.src = ("https:" == document.location.protocol ? "https://ssl" : "http://www") + ".google-analytics.com/ga.js";\n' +
            '    var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(ga, s);\n' +
            '  })();\n' +
          '</script>'
        }
      }
    },

    recess: {
      csslint: {
        options: {
          compile: true
        },
        files: {
          "lib/backgrid.css": ["src/backgrid.css"]
        }
      },
      default: {
        options: {
          compress: true
        },
        files: {
          "lib/backgrid.min.css": ["src/backgrid.css"]
        }
      }
    },

    uglify: {
      options: {
        mangle: true,
        compress: {},
        preserveComments: "some"
      },
      default: {
        files: {
          "lib/backgrid.min.js": ["./lib/backgrid.js"]
        }
      }
    },

    watch: {
      default: {
        files: ["src/**/*.*"],
        tasks: ["dist"]
      }
    }
  });

  grunt.loadNpmTasks("grunt-contrib-clean");
  grunt.loadNpmTasks("grunt-contrib-concat");
  grunt.loadNpmTasks("grunt-contrib-uglify");
  grunt.loadNpmTasks("grunt-contrib-watch");
  grunt.loadNpmTasks("grunt-recess");
  grunt.loadNpmTasks("grunt-jsduck");
  grunt.loadNpmTasks("grunt-contrib-jasmine");
  grunt.loadNpmTasks("grunt-contrib-connect");

  grunt.registerTask("doc", ["clean:api", "jsduck"]);
  grunt.registerTask("dist", ["concat", "uglify", "recess"]);
  grunt.registerTask("test", ["concat", "jasmine"]);
  grunt.registerTask("default", ["clean", "doc", "dist", "jasmine"]);
};
<|endoftext|>


<commit_before>module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        karma: {
            options: {
                files: [
                    'test/support/test-runner.js',
                    {pattern: 'src/**/*.js', included: false},
                    {pattern: 'test/**/*.js', included: false},
                    {pattern: 'bower_components/**/*.js', included: false}
                ],
                frameworks: ['requirejs', 'jasmine']
            },
            unit: {
                browsers: ['PhantomJS'],
                singleRun: true
            },
            debug: {
                browsers: ['Chrome']
            }
        }
    });

    grunt.loadNpmTasks('grunt-karma');

    grunt.registerTask('default', ['karma:unit']);
};<commit_msg>Add grunt test task for build<commit_after>module.exports = function (grunt) {
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        karma: {
            options: {
                files: [
                    'test/support/test-runner.js',
                    {pattern: 'src/**/*.js', included: false},
                    {pattern: 'test/**/*.js', included: false},
                    {pattern: 'bower_components/**/*.js', included: false}
                ],
                frameworks: ['requirejs', 'jasmine']
            },
            unit: {
                browsers: ['PhantomJS'],
                singleRun: true
            },
            debug: {
                browsers: ['Chrome']
            }
        }
    });

    grunt.loadNpmTasks('grunt-karma');

    grunt.registerTask('test', ['karma:unit']);
    grunt.registerTask('default', ['test']);
};<|endoftext|>


<commit_before>describe('Legend', function() {
    var chart;
    var lineSeries;

    var div = document.createElement('div');
    div.id  = 'testChart';

    var createChartElement = function(){

        document.body.appendChild(div);
    };

    var removeChartElement = function(){
        document.body.removeChild(div);
    };

    beforeEach(function() {

        createChartElement();

        chart = new insight.Chart('test', '#testChart')
            .width(550)
            .height(450);

        var x = new insight.Axis('ValueAxis', insight.Scales.Linear)
            .tickLabelOrientation('lr');

        var y = new insight.Axis('KeyAxis', insight.Scales.Linear)
            .tickLabelOrientation('lr')
            .shouldShowGridlines(true);

        chart.addXAxis(x);
        chart.addYAxis(y);

        var data = new insight.DataSet([
            {"key": 1, "value": 1},
            {"key": 2, "value": 2},
            {"key": 3, "value": 3}
        ]);

        lineSeries = new insight.LineSeries('line', data, x, y);

        chart.draw();

        removeChartElement();
    });

    it('legend size is 0,0 when no series on the chart', function() {

        //Given:
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        expect(chart.legendBox.attr('width')).toEqual('0');
        expect(chart.legendBox.attr('height')).toEqual('0');
        expect(chart.legendItems.attr('width')).toEqual('0');
        expect(chart.legendItems.attr('height')).toEqual('0');
    });

    it('legend size closely wraps the series boxes on the chart', function() {

        //Given:
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        expect(chart.legendBox.attr('width')).toEqual('45');
        expect(chart.legendBox.attr('height')).toEqual('60');
        expect(chart.legendItems.attr('width')).toEqual('45');
        expect(chart.legendItems.attr('height')).toEqual('60');
    });

    it('legend items position series blobs', function() {

        //Given:
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        var allRects = chart.legendItems.selectAll('rect')[0];
        var allPositions = allRects.map(function(item) {
            return [item["attributes"]["x"].value,
                item["attributes"]["y"].value,
                item["attributes"]["width"].value,
                item["attributes"]["height"].value];
        });

        var expectedPositions = [['5','5','10','10'],
                                 ['5','25','10','10'],
                                 ['5','45','10','10']];
        expect(allPositions).toEqual(expectedPositions);
    });

    it('legend items position series names', function() {

        //Given:
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        var allRects = chart.legendItems.selectAll('text')[0];
        var allPositions = allRects.map(function(item) {
            return [item["attributes"]["x"].value,
                item["attributes"]["y"].value,
                item["attributes"]["width"].value,
                item["attributes"]["height"].value];
        });

        var expectedPositions = [['20','14','20','20'],
                                 ['20','34','20','20'],
                                 ['20','54','20','20']];
        expect(allPositions).toEqual(expectedPositions);
    });

    it('legend blobs contain series colours', function() {

        //Given:
        lineSeries.color = d3.functor(d3.rgb(128, 0, 128));
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        var allTextElements = chart.legendItems.selectAll('rect')[0];
        var allTexts = allTextElements.map(function(item) {
            return d3.rgb(item["style"]["fill"]);
        });

        expect(allTexts).toEqual([d3.rgb(128, 0, 128), d3.rgb(128, 0, 128), d3.rgb(128, 0, 128)]);
    });

    it('legend items contain series names', function() {

        //Given:
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        var allTextElements = chart.legendItems.selectAll('text')[0];
        var allTexts = allTextElements.map(function(item) {
            return item.textContent;
        });

        expect(allTexts).toEqual(["line", "line", "line"]);
    });
});<commit_msg>Fixed failing legend tests in SpecRunner<commit_after>describe('Legend', function() {

    var chart,
        lineSeries,
        div;

    function setupDiv() {
        div = document.createElement('div');
        div.id  = 'testChart';
        document.body.appendChild(div);
    }

    function destroyDiv() {
        document.body.removeChild(div);
        div = null;
    }

    function mockTextMeasurer() {

        // the fakeMeasurer object will stand in for the canvas.getContext('2d') return value
        var fakeMeasurer = {
            measureText: function (text) {
                return { width: text.length };
            }
        };

        // replace the chart's measureCanvas object with a fake that will return our fakeMeasurer
        chart.measureCanvas = {
            getContext: function() { return fakeMeasurer; }
        };

    }

    beforeEach(function() {

        setupDiv();

        chart = new insight.Chart('test', '#testChart')
            .width(550)
            .height(450);

        var x = new insight.Axis('ValueAxis', insight.Scales.Linear)
            .tickLabelOrientation('lr');

        var y = new insight.Axis('KeyAxis', insight.Scales.Linear)
            .tickLabelOrientation('lr')
            .shouldShowGridlines(true);

        chart.addXAxis(x);
        chart.addYAxis(y);

        var data = new insight.DataSet([
            {"key": 1, "value": 1},
            {"key": 2, "value": 2},
            {"key": 3, "value": 3}
        ]);

        lineSeries = new insight.LineSeries('line', data, x, y);

        mockTextMeasurer();

        chart.draw();

    });

    afterEach(function() {

        destroyDiv();

    })

    it('legend size is 0,0 when no series on the chart', function() {

        //Given:
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        expect(chart.legendBox.attr('width')).toEqual('0');
        expect(chart.legendBox.attr('height')).toEqual('0');
        expect(chart.legendItems.attr('width')).toEqual('0');
        expect(chart.legendItems.attr('height')).toEqual('0');
    });

    it('legend size closely wraps the series boxes on the chart', function() {

        //Given:
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:

        // expected width from the fake text measurer plus a hard-coded value
        var expectedWidth = (lineSeries.name.length + 25) + '';

        expect(chart.legendBox.attr('width')).toEqual(expectedWidth);
        expect(chart.legendBox.attr('height')).toEqual('60');
        expect(chart.legendItems.attr('width')).toEqual(expectedWidth);
        expect(chart.legendItems.attr('height')).toEqual('60');
    });

    it('legend items position series blobs', function() {

        //Given:
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        var allRects = chart.legendItems.selectAll('rect')[0];
        var allPositions = allRects.map(function(item) {
            return [item["attributes"]["x"].value,
                item["attributes"]["y"].value,
                item["attributes"]["width"].value,
                item["attributes"]["height"].value];
        });

        var expectedPositions = [['5','5','10','10'],
                                 ['5','25','10','10'],
                                 ['5','45','10','10']];
        expect(allPositions).toEqual(expectedPositions);
    });

    it('legend items position series names', function() {

        //Given:
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        var allRects = chart.legendItems.selectAll('text')[0];
        var allPositions = allRects.map(function(item) {
            return [
                item["attributes"]["x"].value,
                item["attributes"]["y"].value,
                item["attributes"]["width"].value,
                item["attributes"]["height"].value
            ];
        });

        // expected width from the fake text measurer
        var expectedWidth = lineSeries.name.length + '';

        var expectedPositions = [['20','14',expectedWidth,'20'],
                                 ['20','34',expectedWidth,'20'],
                                 ['20','54',expectedWidth,'20']];
        expect(allPositions).toEqual(expectedPositions);
    });

    it('legend blobs contain series colours', function() {

        //Given:
        lineSeries.color = d3.functor(d3.rgb(128, 0, 128));
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        var allTextElements = chart.legendItems.selectAll('rect')[0];
        var allTexts = allTextElements.map(function(item) {
            return d3.rgb(item["style"]["fill"]);
        });

        expect(allTexts).toEqual([d3.rgb(128, 0, 128), d3.rgb(128, 0, 128), d3.rgb(128, 0, 128)]);
    });

    it('legend items contain series names', function() {

        //Given:
        chart.series([lineSeries, lineSeries, lineSeries]);
        var legend = new insight.Legend();
        chart.legend(legend);

        //When:
        legend.draw(chart);

        //Then:
        var allTextElements = chart.legendItems.selectAll('text')[0];
        var allTexts = allTextElements.map(function(item) {
            return item.textContent;
        });

        expect(allTexts).toEqual(["line", "line", "line"]);
    });
});<|endoftext|>


<commit_before>'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            all: [
                'Gruntfile.js',
                'lib/*.js',
                'test/*.js'
            ],
            options: {
              jshintrc: '.jshintrc'
            }
        },
        tape: {
          options: {
            pretty: true,
            output: 'console'
          },
          //files: ['test/**/*.js']
          files: ['test/test.js']
        },
        watch: {
            files: ['<%= jshint.files %>'],
            tasks: ['jshint']
        },
        doxx: {
          all: {
            src: 'lib',
            target: 'docs',
            options: {
              title: 'LineRate Node.js REST API module'
            }
          }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-doxx');
    grunt.loadNpmTasks('grunt-tape');

    grunt.registerTask('test', ['jshint', 'tape']);
    grunt.registerTask('default', ['jshint', 'tape', 'doxx']);

};
<commit_msg>Use doxx.template instead of default<commit_after>'use strict';

module.exports = function(grunt) {

    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),
        jshint: {
            all: [
                'Gruntfile.js',
                'lib/*.js',
                'test/*.js'
            ],
            options: {
              jshintrc: '.jshintrc'
            }
        },
        tape: {
          options: {
            pretty: true,
            output: 'console'
          },
          //files: ['test/**/*.js']
          files: ['test/test.js']
        },
        watch: {
            files: ['<%= jshint.files %>'],
            tasks: ['jshint']
        },
        doxx: {
          all: {
            src: 'lib',
            target: 'docs',
            options: {
              title: 'LineRate Node.js REST API module',
              template: 'doxx.jade'
            }
          }
        }
    });

    grunt.loadNpmTasks('grunt-contrib-jshint');
    grunt.loadNpmTasks('grunt-contrib-watch');
    grunt.loadNpmTasks('grunt-doxx');
    grunt.loadNpmTasks('grunt-tape');

    grunt.registerTask('test', ['jshint', 'tape']);
    grunt.registerTask('default', ['jshint', 'tape', 'doxx']);

};
<|endoftext|>


<commit_before>import {isFunction, isUndefined} from '../helper/types.js';
import {result, assign} from '../helper/objects.js';

const DEFAULT_DATA = {
    enumerable: true,
    immutable: false,
    descriptorValue: true
};

/**
 * The property builder.
 * Its goal is to provide a way to define a property.
 */
export class PropertyBuilder {

    /**
     * @param {!string} propName the name of the property
     */
    constructor(propName) {
        /**
         * @ignore
         */
        this.data = assign({propName, listeners: []}, DEFAULT_DATA);
    }

    /**
     * To make an immutable property.
     * @returns {PropertyBuilder} the builder
     */
    immutable() {
        this.data.immutable = true;
        return this;
    }

    /**
     * To hide the property name when using Object.keys().
     * @returns {PropertyBuilder} the builder
     */
    hidden() {
        this.data.enumerable = false;
        return this;
    }

    /**
     * To set a default value.
     * @param {*} value the default value
     * @returns {PropertyBuilder} the builder
     */
    value(value) {
        this.data.value = value;
        return this;
    }

    /**
     * To set a getter function.
     * @param {function(el: HTMLElement): *} fn the getter function
     * @returns {PropertyBuilder} the builder
     */
    getter(fn) {
        this.data.descriptorValue = false;
        this.data.getter = fn;
        return this;
    }

    /**
     * To set a setter function.
     * @param {function(el: HTMLElement, value: *)} fn the setter function
     * @returns {PropertyBuilder} the builder
     */
    setter(fn) {
        this.data.descriptorValue = false;
        this.data.setter = fn;
        return this;
    }

    /**
     * To be notified when the property is updated.
     * @param {function(el: HTMLElement, oldVal: *, newVal: *)} listener the listener function
     * @returns {PropertyBuilder} the builder
     */
    listen(listener) {
        this.data.listeners.push(listener);
        return this;
    }

    /**
     * Logic of the builder.
     * @param {!ElementBuilder.context.proto} proto the prototype
     * @param {!ElementBuilder.on} on the method on
     */
    build(proto, on) {
        let data = this.data,
            defaultValue = result(this.data, 'value'),
            descriptor = {
                enumerable: this.data.enumerable
            };

        if (this.data.immutable) {
            descriptor.configurable = false;
            descriptor.writable = false;
        } else if (isFunction(this.data.getter) || isFunction(this.data.setter)) {
            descriptor.configurable = false;
            descriptor.get = function () {
                if (data.getter) {
                    return data.getter.call(this, this);
                }
            };
            descriptor.set = function (value) {
                if (data.setter) {
                    return data.setter.call(this, this, value);
                }
            };
        } else {
            descriptor.configurable = true;
            descriptor.writable = true;
        }

        if (data.listeners.length > 0) {
            descriptor.configurable = false;
            delete descriptor.writable;
            data.descriptorValue = false;
            let _propName = '_' + data.propName;
            if (!descriptor.get) {
                descriptor.get = function () {
                    return this[_propName];
                };
            }
            descriptor.set = function (newVal) {
                let oldVal = this[_propName];
                if (data.setter) {
                    data.setter.call(this, this, newVal);
                } else {
                    this[_propName] = newVal;
                }
                data.listeners.forEach(listener => {
                    listener.call(this, this, oldVal, newVal);
                });
            };
        }

        if (data.descriptorValue) {
            descriptor.value = defaultValue;
        }

        Object.defineProperty(proto, this.data.propName, descriptor);

        on('after:createdCallback').invoke(el => {
            if (!data.descriptorValue && !isUndefined(defaultValue)) {
                el[data.propName] = defaultValue;
            }
        });
    }

}

/**
 * Get a new property builder.
 * @param {!string} propName the name of the property
 * @returns {PropertyBuilder} the property builder
 */
export function property(propName) {
    return new PropertyBuilder(propName);
}
<commit_msg>prefix internal property with __<commit_after>import {isFunction, isUndefined} from '../helper/types.js';
import {result, assign} from '../helper/objects.js';

const DEFAULT_DATA = {
    enumerable: true,
    immutable: false,
    descriptorValue: true
};

/**
 * The property builder.
 * Its goal is to provide a way to define a property.
 */
export class PropertyBuilder {

    /**
     * @param {!string} propName the name of the property
     */
    constructor(propName) {
        /**
         * @ignore
         */
        this.data = assign({propName, listeners: []}, DEFAULT_DATA);
    }

    /**
     * To make an immutable property.
     * @returns {PropertyBuilder} the builder
     */
    immutable() {
        this.data.immutable = true;
        return this;
    }

    /**
     * To hide the property name when using Object.keys().
     * @returns {PropertyBuilder} the builder
     */
    hidden() {
        this.data.enumerable = false;
        return this;
    }

    /**
     * To set a default value.
     * @param {*} value the default value
     * @returns {PropertyBuilder} the builder
     */
    value(value) {
        this.data.value = value;
        return this;
    }

    /**
     * To set a getter function.
     * @param {function(el: HTMLElement): *} fn the getter function
     * @returns {PropertyBuilder} the builder
     */
    getter(fn) {
        this.data.descriptorValue = false;
        this.data.getter = fn;
        return this;
    }

    /**
     * To set a setter function.
     * @param {function(el: HTMLElement, value: *)} fn the setter function
     * @returns {PropertyBuilder} the builder
     */
    setter(fn) {
        this.data.descriptorValue = false;
        this.data.setter = fn;
        return this;
    }

    /**
     * To be notified when the property is updated.
     * @param {function(el: HTMLElement, oldVal: *, newVal: *)} listener the listener function
     * @returns {PropertyBuilder} the builder
     */
    listen(listener) {
        this.data.listeners.push(listener);
        return this;
    }

    /**
     * Logic of the builder.
     * @param {!ElementBuilder.context.proto} proto the prototype
     * @param {!ElementBuilder.on} on the method on
     */
    build(proto, on) {
        let data = this.data,
            defaultValue = result(this.data, 'value'),
            descriptor = {
                enumerable: this.data.enumerable
            };

        if (this.data.immutable) {
            descriptor.configurable = false;
            descriptor.writable = false;
        } else if (isFunction(this.data.getter) || isFunction(this.data.setter)) {
            descriptor.configurable = false;
            descriptor.get = function () {
                if (data.getter) {
                    return data.getter.call(this, this);
                }
            };
            descriptor.set = function (value) {
                if (data.setter) {
                    return data.setter.call(this, this, value);
                }
            };
        } else {
            descriptor.configurable = true;
            descriptor.writable = true;
        }

        if (data.listeners.length > 0) {
            descriptor.configurable = false;
            delete descriptor.writable;
            data.descriptorValue = false;
            let _propName = '__' + data.propName + 'LastSetValue';
            if (!descriptor.get) {
                descriptor.get = function () {
                    return this[_propName];
                };
            }
            descriptor.set = function (newVal) {
                let oldVal = this[_propName];
                this[_propName] = newVal;
                if (data.setter) {
                    data.setter.call(this, this, newVal);
                }
                data.listeners.forEach(listener => {
                    listener.call(this, this, oldVal, newVal);
                });
            };
        }

        if (data.descriptorValue) {
            descriptor.value = defaultValue;
        }

        Object.defineProperty(proto, this.data.propName, descriptor);

        on('after:createdCallback').invoke(el => {
            if (!data.descriptorValue && !isUndefined(defaultValue)) {
                el[data.propName] = defaultValue;
            }
        });
    }

}

/**
 * Get a new property builder.
 * @param {!string} propName the name of the property
 * @returns {PropertyBuilder} the property builder
 */
export function property(propName) {
    return new PropertyBuilder(propName);
}
<|endoftext|>


<commit_before>'use strict';

module.exports = function (grunt) {

  grunt.initConfig({

    jshint: {

      files: [

        'Gruntfile.js',
        'src/**/*.js',
        'package.json',
        '.jshintrc',
        'test/**/*.js'

      ],

      options: {
        jshintrc: '.jshintrc'
      }

    },

    jsdoc: {

        dist : {
            src: ['src/**/*.js'],

            options: {
                destination: 'docs'
            }
        }

    },

    karma: {
        unit: {
            configFile: 'karma.unit.conf.js'
        },

        continuous1: {
            configFile: 'karma.continuous.conf.js',
            browsers: ['SL_Chrome', 'SL_Firefox', 'PhantomJS']
        },

        continuous2: {
            configFile: 'karma.continuous.conf.js',
            browsers: ['SL_Safari', 'SL_IE_8']
        },

        continuous3: {
            configFile: 'karma.continuous.conf.js',
            browsers: ['SL_IE_9', 'SL_IE_10']
        }
    }

  });

  grunt.loadNpmTasks('grunt-contrib-jshint');

  grunt.loadNpmTasks('grunt-karma');

  grunt.loadNpmTasks('grunt-jsdoc');

  grunt.registerTask('travis', ['jshint', 'karma:continuous1', 'karma:continuous2', 'karma:continuous3']);

};<commit_msg>Just Starting with chrome and<commit_after>'use strict';

module.exports = function (grunt) {

  grunt.initConfig({

    jshint: {

      files: [

        'Gruntfile.js',
        'src/**/*.js',
        'package.json',
        '.jshintrc',
        'test/**/*.js'

      ],

      options: {
        jshintrc: '.jshintrc'
      }

    },

    jsdoc: {

        dist : {
            src: ['src/**/*.js'],

            options: {
                destination: 'docs'
            }
        }

    },

    karma: {
        unit: {
            configFile: 'karma.unit.conf.js'
        },

        continuous: {
            configFile: 'karma.continuous.conf.js',
            browsers: ['SL_Chrome', 'SL_Firefox', 'PhantomJS']
        }
//
//        continuous: {
//            configFile: 'karma.continuous.conf.js',
//            browsers: ['SL_Safari', 'SL_IE_8']
//        },
//
//        continuous3: {
//            configFile: 'karma.continuous.conf.js',
//            browsers: ['SL_IE_9', 'SL_IE_10']
//        }
    }

  });

  grunt.loadNpmTasks('grunt-contrib-jshint');

  grunt.loadNpmTasks('grunt-karma');

  grunt.loadNpmTasks('grunt-jsdoc');

  grunt.registerTask('travis', ['jshint', 'karma:continuous']);

};<|endoftext|>


<commit_before>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import shallowEqualFuzzy from 'shallow-equal-fuzzy';
import $ from 'jquery';
import 'select2';

export default class Select2 extends Component {
  static propTypes = {
    defaultValue: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.array,
      PropTypes.string,
    ]),
    value: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.array,
      PropTypes.string,
    ]),
    data: PropTypes.array,
    events: PropTypes.array,
    options: PropTypes.object,
    multiple: PropTypes.bool,
    onOpen: PropTypes.func,
    onClose: PropTypes.func,
    onSelect: PropTypes.func,
    onChange: PropTypes.func,
    onUnselect: PropTypes.func,
  };

  static defaultProps = {
    data: [],
    events: [
      ['change', 'onChange'],
      ['select2:open', 'onOpen'],
      ['select2:close', 'onClose'],
      ['select2:select', 'onSelect'],
      ['select2:unselect', 'onUnselect'],
    ],
    options: {},
    multiple: false,
  };

  constructor(props) {
    super(props);
    this.el = null;
  }

  componentDidMount() {
    this.initSelect2();
  }

  componentWillReceiveProps(nextProps) {
    if (this.el && nextProps.value !== this.props.value) {
      this.setValue(nextProps.value);
    }
  }

  componentDidUpdate(prevProps) {
    if (!shallowEqualFuzzy(prevProps.data, this.props.data)) {
      this.destroySelect2(false);
      this.initSelect2(false);
    }
  }

  componentWillUnmount() {
    this.destroySelect2();
  }

  setValue(value) {
    const elVal = this.props.multiple ? this.el.val() || [] : this.el.val();
    if (!shallowEqualFuzzy(elVal, value)) {
      this.el.val(value).trigger('change');
    }
  }

  initSelect2(withCallbacks = true) {
    if (this.el) { return; }
    const { defaultValue, value, options, events } = this.props;

    this.el = $(ReactDOM.findDOMNode(this));
    this.el.select2(options);

    if (withCallbacks) {
      events.forEach(event => {
        if (typeof this.props[event[1]] !== 'undefined') {
          this.el.on(event[0], this.props[event[1]]);
        }
      });
    }

    if (typeof defaultValue === 'undefined' && typeof value !== 'undefined') {
      this.setValue(value);
    }
  }

  destroySelect2(withCallbacks = true) {
    if (!this.el) { return; }

    if (withCallbacks) {
      this.props.events.forEach(event => {
        if (typeof this.props[event[1]] !== 'undefined') {
          this.el.off(event[0], this.props[event[1]]);
        }
      });
    }

    this.el.select2('destroy');
    this.el = null;
  }

  isObject(value) {
    const type = typeof value;
    return type === 'function' || (value && type === 'object') || false;
  }

  makeOption(item, k) {
    if (this.isObject(item)) {
      const { id, text, ...itemParams } = item;
      return (<option key={`option-${k}`} value={id} {...itemParams}>{text}</option>);
    }

    return (<option key={`option-${k}`} value={item}>{item}</option>);
  }

  render() {
    const { data, value, ...params } = this.props;
    return (
      <select {...params}>
        {data.map((item, k) => {
          if (this.isObject(item) && this.isObject(item.children)) {
            const { children, text, ...itemParams } = item;
            return (
              <optgroup key={`optgroup-${k}`} label={text} {...itemParams}>
                {children.map((child, k2) => this.makeOption(child, `${k}-${k2}`))}
              </optgroup>
            );
          }
          return this.makeOption(item, k);
        })}
      </select>
    );
  }
}
<commit_msg>Fix js style<commit_after>import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import shallowEqualFuzzy from 'shallow-equal-fuzzy';
import $ from 'jquery';
import 'select2';

export default class Select2 extends Component {
  static propTypes = {
    defaultValue: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.array,
      PropTypes.string,
    ]),
    value: PropTypes.oneOfType([
      PropTypes.number,
      PropTypes.array,
      PropTypes.string,
    ]),
    data: PropTypes.array,
    events: PropTypes.array,
    options: PropTypes.object,
    multiple: PropTypes.bool,
    onOpen: PropTypes.func,
    onClose: PropTypes.func,
    onSelect: PropTypes.func,
    onChange: PropTypes.func,
    onUnselect: PropTypes.func,
  };

  static defaultProps = {
    data: [],
    events: [
      ['change', 'onChange'],
      ['select2:open', 'onOpen'],
      ['select2:close', 'onClose'],
      ['select2:select', 'onSelect'],
      ['select2:unselect', 'onUnselect'],
    ],
    options: {},
    multiple: false,
  };

  constructor(props) {
    super(props);
    this.el = null;
  }

  componentDidMount() {
    this.initSelect2();
  }

  componentWillReceiveProps(nextProps) {
    if (this.el && nextProps.value !== this.props.value) {
      this.setValue(nextProps.value);
    }
  }

  componentDidUpdate(prevProps) {
    if (!shallowEqualFuzzy(prevProps.data, this.props.data)) {
      this.destroySelect2(false);
      this.initSelect2(false);
    }
  }

  componentWillUnmount() {
    this.destroySelect2();
  }

  setValue(value) {
    const elVal = this.props.multiple ? this.el.val() || [] : this.el.val();
    if (!shallowEqualFuzzy(elVal, value)) {
      this.el.val(value).trigger('change');
    }
  }

  initSelect2(withCallbacks = true) {
    if (this.el) { return; }
    const { defaultValue, value, options, events } = this.props;

    this.el = $(ReactDOM.findDOMNode(this));
    this.el.select2(options);

    if (withCallbacks) {
      events.forEach(event => {
        if (typeof this.props[event[1]] !== 'undefined') {
          this.el.on(event[0], this.props[event[1]]);
        }
      });
    }

    if (typeof defaultValue === 'undefined' && typeof value !== 'undefined') {
      this.setValue(value);
    }
  }

  destroySelect2(withCallbacks = true) {
    if (!this.el) { return; }

    if (withCallbacks) {
      this.props.events.forEach(event => {
        if (typeof this.props[event[1]] !== 'undefined') {
          this.el.off(event[0], this.props[event[1]]);
        }
      });
    }

    this.el.select2('destroy');
    this.el = null;
  }

  isObject(value) {
    const type = typeof value;
    return type === 'function' || (value && type === 'object') || false;
  }

  makeOption(item, k) {
    if (this.isObject(item)) {
      const { id, text, ...itemParams } = item;
      return (<option key={`option-${k}`} value={id} {...itemParams}>{text}</option>);
    }

    return (<option key={`option-${k}`} value={item}>{item}</option>);
  }

  render() {
    const { data, value, ...params } = this.props;
    return (
      <select {...params}>
        {data.map((item, k) => {
          if (this.isObject(item) && this.isObject(item.children)) {
            const { children, text, ...itemParams } = item;
            return (
              <optgroup key={`optgroup-${k}`} label={text} {...itemParams}>
                {children.map((child, k2) => this.makeOption(child, `${k}-${k2}`))}
              </optgroup>
            );
          }
          return this.makeOption(item, k);
        })}
      </select>
    );
  }
}
<|endoftext|>