<commit_before>import d3 from "d3";
import uuid from "node-uuid";

import Jupyter from "base/js/namespace";

import {Editor} from "./editor";

let REMOVED = "<removed>";

class Sorter {
  constructor(tree) {
    this.tree = tree;
    // TODO: put this in the tree
    this.visible = this.tree.select("visible");
    this.visible.set(false);

    this.$view = d3.select("#header")
      .append("div")
      .classed({
        nbpresent_sorter: 1,
        offscreen: 1
      });

    this.initToolbar();
    this.initDrag();

    this.$slides = this.$view.append("div")
      .classed({slides_wrap: 1});

    this.$empty = this.$slides.append("h3")
      .classed({empty: 1})
      .text("No slides yet.");

    this.slides = this.tree.select(["slides"]);
    this.selectedSlide = this.tree.select("selectedSlide");
    this.selectedRegion = this.tree.select("selectedRegion");

    this.selectedSlide.on("update", () => {
      this.draw();
      if(this.editor){
        this.editSlide(this.selectedSlide.get());
      }
    });
    this.selectedRegion.on("update", () => this.draw());
    this.slides.on("update", () => this.draw());

    this.draw();
  }

  initDrag(){
    let that = this,
      dragOrigin;

    this.drag = d3.behavior.drag()
      .on("dragstart", function(d){
        let slide = d3.select(this)
          .classed({dragging: 1});

        dragOrigin = parseFloat(slide.style("top"));
      })
      .on("drag", function(d){
        d3.select(this)
          .style({
            top: `${dragOrigin += d3.event.dy}px`,
          });
      })
      .on("dragend", function(d, i){
        let $slide = d3.select(this)
          .classed({dragging: 0});

        let top = parseFloat($slide.style("top")),
          slides = that.sortedSlides(),
          slideN = Math.floor(top / that.slideHeight()),
          after;

        if(top < that.slideHeight() || slideN < 0){
          after = null;
        }else if(slideN > slides.length || !slides[slideN]){
          after = slides.slice(-1)[0].key;
        }else{
          after = slides[slideN].key;
        }

        if(d.key !== after){
          that.unlinkSlide(d.key);
          that.selectedSlide.set(that.appendSlide(after, d.key));
        }else{
          that.draw();
        }
      });
  }

  // put in tree?
  sortedSlides(){
    let slides = d3.entries(this.slides.get());

    slides.sort(
      (a, b) => (a.value.prev === null) || (a.key === b.value.prev) ? -1 : 1
    )

    return slides;
  }

  slideHeight() {
    return 100;
  }

  draw(){
    let that = this;

    let slides = this.sortedSlides();

    //console.table(slides.map(({value}) => value));

    let $slide = this.$slides.selectAll(".slide")
      .data(slides, (d) => d.key);

    $slide.enter().append("div")
      .classed({slide: 1})
      .call(this.drag)
      .on("mousedown", function(d){
        that.selectedSlide.set(
          that.selectedSlide.get() === d.key ? null : d.key
        );
      })
      .style({
        left: "200px"
      })
      .append("svg");

    $slide.exit()
      .transition()
      .style({
        left: "200px"
      })
      .remove();

    let selectedSlide = this.selectedSlide.get();

    $slide
      .style({
        "z-index": (d, i) => i
      })
      .classed({
        active: (d) => d.key === selectedSlide
      })
      .transition()
      .delay((d, i) => i * 10)
      .style({
        left: "0px",
        top: (d, i) => `${i * this.slideHeight()}px`
      });

    let $region = $slide.select("svg")
      .selectAll(".region")
      .data((d) => d3.entries(d.value.regions))

    $region.enter()
      .append("rect")
      .classed({region: 1})
      .on("click", (d)=> this.selectedRegion.set(d.key));

    $region.exit()
      .remove();

    let selectedRegion = this.selectedRegion.get();

    $region
      .classed({
        active: (d) => d.key === selectedRegion,
        "has-content": (d) => d.value.content
      })
      .attr({
        x: (d) => d.value.x * 160,
        y: (d) => d.value.y * 90,
        width: (d) => d.value.width * 160,
        height: (d) => d.value.height * 90,
      });

    this.$empty.style({opacity: 1 * !$slide[0].length });
  }

  initToolbar(){
    this.$toolbar = this.$view.append("div")
      .classed({
        sorter_toolbar: 1,
        "btn-toolbar": 1
      });

    let $slide_actions = this.$toolbar.append("div")
      .classed({"btn-group": 1});

    $slide_actions.selectAll(".btn")
      .data([{
        icon: "plus-square-o",
        on: {click: () => this.addSlide() }
      }, {
        icon: "trash",
        on: {click: () => this.removeSlide(this.selectedSlide.get()) }
      }, {
        icon: "edit",
        on: {click: () => this.editSlide(this.selectedSlide.get()) }
      }, {
        icon: "external-link-square",
        on: {click: () => this.linkContent("source")}
      }, {
        icon: "external-link",
        on: {click: () => this.linkContent("output")}
      }])
      .enter()
      .append("a")
      .classed({btn: 1, "btn-default": 1, "btn-xs": 1})
      .call(function($btn){
        let icon = $btn.append("i")
          .classed({fa: 1, "fa-fw": 1})
          .each(function(d){
            d3.select(this).classed(`fa-${d.icon}`, 1);
          });
      })
      .each(function(d){
        let $btn = d3.select(this);
        Object.keys(d.on).map((key)=>{
          $btn.on(key, d.on[key]);
        });
      });
  }

  linkContent(part){
    let slide = this.selectedSlide.get(),
      region = this.selectedRegion.get(),
      cell = Jupyter.notebook.get_selected_cell(),
      cellId;

    if(!(slide && region && cell)){
      return;
    }

    if(!cell.metadata.slides){
      cell.metadata.slides = {id: this.nextId()};
    }

    cellId = cell.metadata.slides.id;

    this.slides.set([slide, "regions", region, "content"], {
      cell: cellId,
      part
    });
  }

  addSlide(){
    let last = this.sortedSlides().slice(-1),
      selected = this.selectedSlide.get(),
      appended = this.appendSlide(
        selected ? selected : last.length ? last[0].key : null
      );
    this.selectedSlide.set(appended);
  }

  editSlide(id){
    if(this.editor){
      if(this.editor.slide.get("id") === id){
        id = null;
      }
      this.editor.destroy();
      this.editor = null;
    }

    if(!id){
      return;
    }
    // TODO: do this with an id and big tree ref?
    this.editor = new Editor(this.slides.select(id));
  }

  nextId(){
    return uuid.v4();
  }

  unlinkSlide(id){
    let {prev} = this.slides.get(id),
      next = this.nextSlide(id);

    next && this.slides.set([next, "prev"], prev);
    this.slides.set([id, "prev"], REMOVED);
  }

  removeSlide(id){
    if(!id){
      return;
    }
    this.unlinkSlide(id);
    this.slides.unset(id);
  }

  nextSlide(id){
    let slides = this.sortedSlides(),
      next = slides.filter((d) => d.value.prev === id);

    return next.length ? next[0].key : null;
  }

  newSlide(id, prev){
    return {
      id, prev,
      regions: {
      }
    }
  }

  appendSlide(prev, id=null){
    let next = this.nextSlide(prev);

    if(!id){
      id = this.nextId();
      this.slides.set(id, this.newSlide(id, prev));
    }else{
      this.slides.set([id, "prev"], prev);
    }

    next && this.slides.set([next, "prev"], id);

    return id;
  }

  show(){
    this.visible.set(!this.visible.get());
    this.update();
  }

  update(){
    let visible = this.visible.get();
    this.$view.classed({offscreen: !visible});
    d3.select("#notebook-container")
      .style({
        width: visible ? "auto" : null,
        "margin-right": visible ? "220px" : null,
        "margin-left": visible ? "20px" : null,
      });
  }
}

export {Sorter};
<commit_msg>selecting region selects cell<commit_after>import d3 from "d3";
import uuid from "node-uuid";

import Jupyter from "base/js/namespace";

import {Editor} from "./editor";

let REMOVED = "<removed>";

class Sorter {
  constructor(tree) {
    this.tree = tree;
    // TODO: put this in the tree
    this.visible = this.tree.select("visible");
    this.visible.set(false);

    this.$view = d3.select("#header")
      .append("div")
      .classed({
        nbpresent_sorter: 1,
        offscreen: 1
      });

    this.initToolbar();
    this.initDrag();

    this.$slides = this.$view.append("div")
      .classed({slides_wrap: 1});

    this.$empty = this.$slides.append("h3")
      .classed({empty: 1})
      .text("No slides yet.");

    this.slides = this.tree.select(["slides"]);
    this.selectedSlide = this.tree.select("selectedSlide");
    this.selectedRegion = this.tree.select("selectedRegion");

    this.selectedSlide.on("update", () => {
      let slide = this.selectedSlide.get();
      this.draw();
      if(this.editor){
        this.editSlide(slide);
      }
    });
    this.selectedRegion.on("update", () => {
      let region = this.selectedRegion.get();
      if(region){
        let content = this.slides.get(
          [region[0], "regions", region[1], "content"]);
        if(content){
          let cell = Jupyter.notebook.get_cells().filter(function(cell, idx){
            if(cell.metadata.slides && cell.metadata.slides.id == content.cell){
              Jupyter.notebook.select(idx);
            };
          });
        }
      }
      this.draw();
    });
    this.slides.on("update", () => this.draw());

    this.draw();
  }

  initDrag(){
    let that = this,
      dragOrigin;

    this.drag = d3.behavior.drag()
      .on("dragstart", function(d){
        let slide = d3.select(this)
          .classed({dragging: 1});

        dragOrigin = parseFloat(slide.style("top"));
      })
      .on("drag", function(d){
        d3.select(this)
          .style({
            top: `${dragOrigin += d3.event.dy}px`,
          });
      })
      .on("dragend", function(d, i){
        let $slide = d3.select(this)
          .classed({dragging: 0});

        let top = parseFloat($slide.style("top")),
          slides = that.sortedSlides(),
          slideN = Math.floor(top / that.slideHeight()),
          after;

        if(top < that.slideHeight() || slideN < 0){
          after = null;
        }else if(slideN > slides.length || !slides[slideN]){
          after = slides.slice(-1)[0].key;
        }else{
          after = slides[slideN].key;
        }

        if(d.key !== after){
          that.unlinkSlide(d.key);
          that.selectedSlide.set(that.appendSlide(after, d.key));
        }else{
          that.draw();
        }
      });
  }

  // put in tree?
  sortedSlides(){
    let slides = d3.entries(this.slides.get());

    slides.sort(
      (a, b) => (a.value.prev === null) || (a.key === b.value.prev) ? -1 : 1
    )

    return slides;
  }

  slideHeight() {
    return 100;
  }

  draw(){
    let that = this;

    let slides = this.sortedSlides();

    //console.table(slides.map(({value}) => value));

    let $slide = this.$slides.selectAll(".slide")
      .data(slides, (d) => d.key);

    $slide.enter().append("div")
      .classed({slide: 1})
      .call(this.drag)
      .on("mousedown", function(d){
        that.selectedSlide.set(
          that.selectedSlide.get() === d.key ? null : d.key
        );
      })
      .style({
        left: "200px"
      })
      .append("svg");

    $slide.exit()
      .transition()
      .style({
        left: "200px"
      })
      .remove();

    let selectedSlide = this.selectedSlide.get();

    $slide
      .style({
        "z-index": (d, i) => i
      })
      .classed({
        active: (d) => d.key === selectedSlide
      })
      .transition()
      .delay((d, i) => i * 10)
      .style({
        left: "0px",
        top: (d, i) => `${i * this.slideHeight()}px`
      });

    let $region = $slide.select("svg")
      .selectAll(".region")
      .data((slide) => d3.entries(slide.value.regions).map((region) => {
        return {slide, region};
      }))

    $region.enter()
      .append("rect")
      .classed({region: 1})
      .on("click", (d)=> this.selectedRegion.set([d.slide.key, d.region.key]));

    $region.exit()
      .remove();

    let sRegion = this.selectedRegion.get();

    $region
      .classed({
        active: (d) => {
          return sRegion &&
            d.slide.key == sRegion[0] &&
            d.region.key === sRegion[1];
        },
        "has-content": (d) => d.region.value.content
      })
      .attr({
        x: (d) => d.region.value.x * 160,
        y: (d) => d.region.value.y * 90,
        width: (d) => d.region.value.width * 160,
        height: (d) => d.region.value.height * 90,
      });

    this.$empty.style({opacity: 1 * !$slide[0].length });
  }

  initToolbar(){
    this.$toolbar = this.$view.append("div")
      .classed({
        sorter_toolbar: 1,
        "btn-toolbar": 1
      });

    let $slide_actions = this.$toolbar.append("div")
      .classed({"btn-group": 1});

    $slide_actions.selectAll(".btn")
      .data([{
        icon: "plus-square-o",
        on: {click: () => this.addSlide() }
      }, {
        icon: "trash",
        on: {click: () => this.removeSlide(this.selectedSlide.get()) }
      }, {
        icon: "edit",
        on: {click: () => this.editSlide(this.selectedSlide.get()) }
      }, {
        icon: "external-link-square",
        on: {click: () => this.linkContent("source")}
      }, {
        icon: "external-link",
        on: {click: () => this.linkContent("output")}
      }])
      .enter()
      .append("a")
      .classed({btn: 1, "btn-default": 1, "btn-xs": 1})
      .call(function($btn){
        let icon = $btn.append("i")
          .classed({fa: 1, "fa-fw": 1})
          .each(function(d){
            d3.select(this).classed(`fa-${d.icon}`, 1);
          });
      })
      .each(function(d){
        let $btn = d3.select(this);
        Object.keys(d.on).map((key)=>{
          $btn.on(key, d.on[key]);
        });
      });
  }

  linkContent(part){
    let region = this.selectedRegion.get(),
      cell = Jupyter.notebook.get_selected_cell(),
      cellId;

    if(!(region && cell)){
      return;
    }

    if(!cell.metadata.slides){
      cell.metadata.slides = {id: this.nextId()};
    }

    cellId = cell.metadata.slides.id;

    this.slides.set([region[0], "regions", region[1], "content"], {
      cell: cellId,
      part
    });
  }

  addSlide(){
    let last = this.sortedSlides().slice(-1),
      selected = this.selectedSlide.get(),
      appended = this.appendSlide(
        selected ? selected : last.length ? last[0].key : null
      );
    this.selectedSlide.set(appended);
  }

  editSlide(id){
    if(this.editor){
      if(this.editor.slide.get("id") === id){
        id = null;
      }
      this.editor.destroy();
      this.editor = null;
    }

    if(!id){
      return;
    }
    // TODO: do this with an id and big tree ref?
    this.editor = new Editor(this.slides.select(id));
  }

  nextId(){
    return uuid.v4();
  }

  unlinkSlide(id){
    let {prev} = this.slides.get(id),
      next = this.nextSlide(id);

    next && this.slides.set([next, "prev"], prev);
    this.slides.set([id, "prev"], REMOVED);
  }

  removeSlide(id){
    if(!id){
      return;
    }
    this.unlinkSlide(id);
    this.slides.unset(id);
  }

  nextSlide(id){
    let slides = this.sortedSlides(),
      next = slides.filter((d) => d.value.prev === id);

    return next.length ? next[0].key : null;
  }

  newSlide(id, prev){
    return {
      id, prev,
      regions: {
      }
    }
  }

  appendSlide(prev, id=null){
    let next = this.nextSlide(prev);

    if(!id){
      id = this.nextId();
      this.slides.set(id, this.newSlide(id, prev));
    }else{
      this.slides.set([id, "prev"], prev);
    }

    next && this.slides.set([next, "prev"], id);

    return id;
  }

  show(){
    this.visible.set(!this.visible.get());
    this.update();
  }

  update(){
    let visible = this.visible.get();
    this.$view.classed({offscreen: !visible});
    d3.select("#notebook-container")
      .style({
        width: visible ? "auto" : null,
        "margin-right": visible ? "220px" : null,
        "margin-left": visible ? "20px" : null,
      });
  }
}

export {Sorter};
<|endoftext|>


<commit_before>'use strict';

var eslint = require('./cli');
var _ = require('lodash');
var logger = require('../log');

function createOption(arr){
  var noAlias = arr[0].match(/--\w/);
  var option = noAlias ? parseRegular(arr) : parseAlias(arr);
  var isEmpty = _.isEmpty(option);
  return isEmpty ? undefined : option;
}

function parseAlias(arr){
  var alias = arr[0];
  var option = parseRegular(_.without(arr, alias));

  if(alias){
    option.alias = alias.replace('-','');
  }
  return option;
}

function parseRegular(arr){
  if(!arr[0]){
    return;
  }
  var optionText = arr[0];
  var type = arr[1];
  var option = {};
  option.option = optionText.replace('--', '');
  option.type = type ? type : 'Boolean';

  var helpText = _.without(arr, optionText, type, '');

  var description = helpText.join(' ');
  if(description){
    option.description = description;
  }
  return option;
}

function parseHelp(helpText){
  var helpArr = helpText.split('\n');
  var newArr = [];
  var previousLine =[];
  _.each(helpArr, function(row, index){
    if(index === 0 || index === 1 || index === 2){
      return;
    } else {
      var str = row.replace(',', '');
      var arr = str.trim().split(' ');
      if(str.length > 1 && previousLine[0] !== ''){
        var option = createOption(arr);
        if(option && option.option !== 'format' && option.option !== 'help'){
          newArr.push(option);
        }
      }
      previousLine = arr;
    }
  });
  return newArr;
}

// rewrite in es6 this callback yucky stuff goes away.
module.exports = function(cllbk){
  var spawn = eslint(['--help'], {help: true}, {});
  spawn.stdout.on('data', function(msg){
    var eslintHelp = msg.toString();
    try {
      cllbk(parseHelp(eslintHelp));
    } catch(e){
      logger.log(e.stack);
      throw(e);
    }
  });
};
<commit_msg>Making code a little less fragile. Takes any line with a hyphen<commit_after>'use strict';

var eslint = require('./cli');
var _ = require('lodash');
var logger = require('../log');

function createOption(arr){
  var noAlias = arr[0].match(/--\w/);
  var option = noAlias ? parseRegular(arr) : parseAlias(arr);
  var isEmpty = _.isEmpty(option);
  return isEmpty ? undefined : option;
}

function parseAlias(arr){
  var alias = arr[0];
  var option = parseRegular(_.without(arr, alias));

  if(alias){
    option.alias = alias.replace('-','');
  }
  return option;
}

function parseRegular(arr){
  if(!arr[0]){
    return;
  }
  var optionText = arr[0];
  var type = arr[1];
  var option = {};
  option.option = optionText.replace('--', '');
  option.type = type ? type : 'Boolean';

  var helpText = _.without(arr, optionText, type, '');

  var description = helpText.join(' ');
  if(description){
    option.description = description;
  }
  return option;
}

function parseHelp(helpText){
  var helpArr = helpText.split('\n');
  var newArr = [];
  var previousLine = [];
  _.each(helpArr, function(row, index){
    if(index === 0 || index === 1 || index === 2){
      return;
    } else {
      var str = row.replace(',', '');
      var arr = str.trim().split(' ');
      if(str.indexOf('-') >= 0 && previousLine[0] !== ''){
        var option = createOption(arr);
        if(option && option.option !== 'format' && option.option !== 'help'){
          newArr.push(option);
        }
      }
      previousLine = arr;
    }
  });
  return newArr;
}

// rewrite in es6 this callback yucky stuff goes away.
module.exports = function(cllbk){
  var spawn = eslint(['--help'], {help: true}, {});
  spawn.stdout.on('data', function(msg){
    var eslintHelp = msg.toString();
    try {
      cllbk(parseHelp(eslintHelp));
    } catch(e){
      logger.log(e.stack);
      throw(e);
    }
  });
};
<|endoftext|>


<commit_before>var slug = alchemy.use('mollusc');

/**
 * The Sluggable Behaviour class
 *
 * @constructor
 * @extends       Alchemy.Behaviour
 *
 * @author        Jelle De Loecker   <jelle@develry.be>
 * @since         0.1.0
 * @version       0.3.0
 */
var Sluggable = Function.inherits('Alchemy.Behaviour', function SluggableBehaviour(model, options) {

	Behaviour.call(this, model, options);

	// Source options
	this.source = options.source;

	this.source_field = options.source_field;
	this.target_field = options.target_field;
	this.replacement = options.replacement;

	this.translatable = this.source_field.translatable;
});

/**
 * Default slug options
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.2.3
 *
 * @type     {Object}
 */
Sluggable.setProperty('default_options', {
	translatable : null,
	target       : 'slug',
	source       : ['title', 'name'],
	replacement  : '-'
});

/**
 * Get the source field
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.2.0
 *
 * @param    {String}    source
 * @param    {Schema}    schema
 */
Sluggable.setStatic(function getSource(source, schema) {

	var field,
	    i;

	source = Array.cast(source);

	for (i = 0; i < source.length; i++) {
		field = schema.get(source[i]);

		if (field) {
			break;
		}
	}

	return field;
});

/**
 * Listen to attachments to schema's
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.2.0
 *
 * @param    {Schema}    schema
 * @param    {Object}    options
 */
Sluggable.setStatic(function attached(schema, new_options) {

	var field_options,
	    options,
	    source,
	    target,
	    main;

	if (schema.model_class) {
		main = schema.model_class;
	} else {
		main = schema;
	}

	// Merge the options object
	options = Object.assign({}, this.prototype.default_options, new_options);

	// Add them back into the new_options by reference
	options = Object.assign(new_options, options);

	// Get the source field
	source = this.getSource(options.source, schema);

	// Get the target field
	target = schema.get(options.target);

	// Create the target if it doesn't exist yet
	if (!target) {

		field_options = {};

		// See if the target field we're adding needs to be translatable
		if (options.translatable || (options.translatable == null && source && source.is_translatable)) {
			field_options.translatable = true;
		}

		target = main.addField(options.target, 'String', field_options);
	} else if (target.is_translatable) {
		options.translatable = true;
	}

	// Also store the fields in the options
	options.source_field = source;
	options.target_field = target;
});

/**
 * The beforeSave
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.1.0
 * @version  1.0.6
 *
 * @param    {Object}    data      The data that is to be saved
 * @param    {Object}    options   Behaviour options
 * @param    {Boolean}   creating  Is this record being created?
 */
Sluggable.setMethod(async function beforeSave(data, options, creating) {

	var that = this,
	    has_new_value,
	    old_record,
	    new_value,
	    old_value,
	    next;

	// Get the actual record data
	if (data[that.model.name]) {
		data = data[that.model.name];
	}

	// See if there is a new value defined
	new_value = data[that.target_field.name];

	if (typeof new_value == 'undefined') {
		has_new_value = false;
	} else {
		has_new_value = true;
	}

	// Let other event callbacks wait for this one
	next = this.wait('series');

	if (!creating) {
		old_record = await this.model.findById(data._id);

		if (old_value) {
			old_value = old_record[that.target_field.name];
		}
	}

	// If we're not creating a new document,
	// and the old document already has a slug,
	// and we're not explicitly setting a new slug value,
	// then do nothing
	if (!creating && old_value && !has_new_value) {
		return next();
	}

	let new_data = {};

	// Create a new data object, so the non-changed values are available
	if (old_record) {
		Object.assign(new_data, old_record.$main, data);
	} else {
		Object.assign(new_data, data);
	}

	// Try creating a new slug
	that.createSlug(new_data, new_value, function createdSlug(err, result) {

		if (err) {
			return next(err);
		}

		if (!Object.isEmpty(result)) {
			data[that.target_field.name] = result;
		}

		next();
	});
});

/**
 * Start the slug creating process
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.1.0
 * @version  1.0.4
 *
 * @param    {Object}   record
 * @param    {String}   new_value
 * @param    {Function} callback
 */
Sluggable.setMethod(function createSlug(record, new_value, callback) {

	var that   = this,
	    source,
	    tasks,
	    key,
	    i;

	if (typeof new_value == 'function') {
		callback = new_value;
		new_value = undefined;
	}

	if (Array.isArray(this.source)) {

		for (i = 0; i < this.source.length; i++) {
			source = record[this.source[i]];

			if (source) {
				break;
			}
		}
	} else {
		source = record[this.source];
	}

	if (source && typeof source == 'object') {

		tasks = {};

		Object.each(source, function eachSource(source_title, key) {

			var has_value = !!record[that.target_field.name] && !!record[that.target_field.name][key];

			// If it already has a value, and there is no new one, skip it
			if (has_value && (!new_value || !new_value[key])) {
				return;
			}

			tasks[key] = function generateSlug(next) {

				if (new_value && new_value[key]) {
					source_title = new_value[key];
				}

				that.generateSlug(source_title, key, record._id, next);
			};
		});

		return Function.parallel(tasks, callback);
	} else {
		// Only generate a new slug if it doesn't exist yet
		if (!record[that.target_field.name]) {
			return that.generateSlug(record[that.source_field.name], false, record._id, callback);
		}
	}

	callback();
});

/**
 * Actually generate the slug from the given string,
 * and look for existing slugs in the path
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.1.0
 * @version  1.0.4
 *
 * @param    {String}   title
 * @param    {String}   path
 * @param    {ObjectId} for_record_id
 * @param    {Function} callback
 */
Sluggable.setMethod(function generateSlug(title, key, for_record_id, callback) {

	var that = this,
	    baseSlug,
	    current,
	    model,
	    count,
	    item,
	    path;

	if (typeof key == 'object') {
		callback = for_record_id;
		for_record_id = key;
		key = false;
	}

	if (typeof for_record_id == 'function') {
		callback = for_record_id;
		for_record_id = null;
	}

	if (typeof key === 'function') {
		callback = key;
		key = false;
	}

	// If no valid title is given, do nothing!
	if (!title) {
		return callback();
	}

	baseSlug = slug(title, {replacement: that.replacement || '-'}).toLowerCase();
	current = baseSlug;
	model = Model.get(this.model.name);
	count = 1;

	path = this.target_field.name;

	if (key) {
		path += '.' + key;
	}

	Function.doWhile(function doWhile(next) {

		var conditions = {};
		conditions[path] = current;

		model.find('first', {conditions: conditions}, function gotFirst(err, found_item) {

			if (err != null) {
				return next(err);
			}

			// Don't use the target record for duplicate checks
			if (found_item && for_record_id && String(for_record_id) == String(found_item._id)) {
				return next();
			}

			item = found_item;
			next();
		});
	}, function check() {

		// As long as items have been found we keep on adding numbers
		if (item) {

			count++;
			current = baseSlug + (that.replacement || '-') + count;

			return true;
		}

	}, function finished(err) {
		callback(err, current);
	});
});<commit_msg>Add `unique_modifier_fields` option to Sluggable behaviour<commit_after>var slug = alchemy.use('mollusc');

/**
 * The Sluggable Behaviour class
 *
 * @constructor
 * @extends       Alchemy.Behaviour
 *
 * @author        Jelle De Loecker   <jelle@develry.be>
 * @since         0.1.0
 * @version       0.3.0
 */
var Sluggable = Function.inherits('Alchemy.Behaviour', function SluggableBehaviour(model, options) {

	Behaviour.call(this, model, options);

	// Source options
	this.source = options.source;

	this.source_field = options.source_field;
	this.target_field = options.target_field;
	this.replacement = options.replacement;

	if (!options.unique_modifier_fields) {
		options.unique_modifier_fields = [];
	} else if (!Array.isArray(options.unique_modifier_fields)) {
		options.unique_modifier_fields = Array.cast(options.unique_modifier_fields);
	}

	this.unique_modifier_fields = options.unique_modifier_fields;

	this.translatable = this.source_field.translatable;
});

/**
 * Default slug options
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.2.3
 *
 * @type     {Object}
 */
Sluggable.setProperty('default_options', {
	translatable : null,
	target       : 'slug',
	source       : ['title', 'name'],
	replacement  : '-'
});

/**
 * Get the source field
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.2.0
 *
 * @param    {String}    source
 * @param    {Schema}    schema
 */
Sluggable.setStatic(function getSource(source, schema) {

	var field,
	    i;

	source = Array.cast(source);

	for (i = 0; i < source.length; i++) {
		field = schema.get(source[i]);

		if (field) {
			break;
		}
	}

	return field;
});

/**
 * Listen to attachments to schema's
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.2.0
 * @version  0.2.0
 *
 * @param    {Schema}    schema
 * @param    {Object}    options
 */
Sluggable.setStatic(function attached(schema, new_options) {

	var field_options,
	    options,
	    source,
	    target,
	    main;

	if (schema.model_class) {
		main = schema.model_class;
	} else {
		main = schema;
	}

	// Merge the options object
	options = Object.assign({}, this.prototype.default_options, new_options);

	// Add them back into the new_options by reference
	options = Object.assign(new_options, options);

	// Get the source field
	source = this.getSource(options.source, schema);

	// Get the target field
	target = schema.get(options.target);

	// Create the target if it doesn't exist yet
	if (!target) {

		field_options = {};

		// See if the target field we're adding needs to be translatable
		if (options.translatable || (options.translatable == null && source && source.is_translatable)) {
			field_options.translatable = true;
		}

		target = main.addField(options.target, 'String', field_options);
	} else if (target.is_translatable) {
		options.translatable = true;
	}

	// Also store the fields in the options
	options.source_field = source;
	options.target_field = target;
});

/**
 * The beforeSave
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.1.0
 * @version  1.0.6
 *
 * @param    {Object}    data      The data that is to be saved
 * @param    {Object}    options   Behaviour options
 * @param    {Boolean}   creating  Is this record being created?
 */
Sluggable.setMethod(async function beforeSave(data, options, creating) {

	var that = this,
	    has_new_value,
	    old_record,
	    new_value,
	    old_value,
	    next;

	// Get the actual record data
	if (data[that.model.name]) {
		data = data[that.model.name];
	}

	// See if there is a new value defined
	new_value = data[that.target_field.name];

	if (typeof new_value == 'undefined') {
		has_new_value = false;
	} else {
		has_new_value = true;
	}

	// Let other event callbacks wait for this one
	next = this.wait('series');

	if (!creating) {
		old_record = await this.model.findById(data._id);

		if (old_value) {
			old_value = old_record[that.target_field.name];
		}
	}

	// If we're not creating a new document,
	// and the old document already has a slug,
	// and we're not explicitly setting a new slug value,
	// then do nothing
	if (!creating && old_value && !has_new_value) {
		return next();
	}

	let new_data = {};

	// Create a new data object, so the non-changed values are available
	if (old_record) {
		Object.assign(new_data, old_record.$main, data);
	} else {
		Object.assign(new_data, data);
	}

	// Try creating a new slug
	that.createSlug(new_data, new_value, function createdSlug(err, result) {

		if (err) {
			return next(err);
		}

		if (!Object.isEmpty(result)) {
			data[that.target_field.name] = result;
		}

		next();
	});
});

/**
 * Start the slug creating process
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.1.0
 * @version  1.0.4
 *
 * @param    {Object}   record
 * @param    {String}   new_value
 * @param    {Function} callback
 */
Sluggable.setMethod(function createSlug(record, new_value, callback) {

	var that   = this,
	    source,
	    tasks,
	    key,
	    i;

	if (typeof new_value == 'function') {
		callback = new_value;
		new_value = undefined;
	}

	if (Array.isArray(this.source)) {

		for (i = 0; i < this.source.length; i++) {
			source = record[this.source[i]];

			if (source) {
				break;
			}
		}
	} else {
		source = record[this.source];
	}

	if (source && typeof source == 'object') {

		tasks = {};

		Object.each(source, function eachSource(source_title, key) {

			var has_value = !!record[that.target_field.name] && !!record[that.target_field.name][key];

			// If it already has a value, and there is no new one, skip it
			if (has_value && (!new_value || !new_value[key])) {
				return;
			}

			tasks[key] = function generateSlug(next) {

				if (new_value && new_value[key]) {
					source_title = new_value[key];
				}

				that.generateSlug(source_title, key, record, next);
			};
		});

		return Function.parallel(tasks, callback);
	} else {
		// Only generate a new slug if it doesn't exist yet
		if (!record[that.target_field.name]) {
			return that.generateSlug(record[that.source_field.name], false, record, callback);
		}
	}

	callback();
});

/**
 * Actually generate the slug from the given string,
 * and look for existing slugs in the path
 *
 * @author   Jelle De Loecker   <jelle@develry.be>
 * @since    0.1.0
 * @version  1.1.0
 *
 * @param    {String}   title
 * @param    {String}   path
 * @param    {Document} record
 * @param    {Function} callback
 */
Sluggable.setMethod(function generateSlug(title, key, record, callback) {

	var that = this,
	    for_record_id,
	    baseSlug,
	    current,
	    model,
	    count,
	    item,
	    path;

	if (typeof key == 'object') {
		callback = record;
		record = key;
		key = false;
	}

	if (typeof record == 'function') {
		callback = record;
		record = null;
	}

	if (record) {
		for_record_id = record._id;
	}

	if (typeof key === 'function') {
		callback = key;
		key = false;
	}

	// If no valid title is given, do nothing!
	if (!title) {
		return callback();
	}

	baseSlug = slug(title, {replacement: that.replacement || '-'}).toLowerCase();
	current = baseSlug;
	model = Model.get(this.model.name);
	count = 1;

	path = this.target_field.name;

	if (key) {
		path += '.' + key;
	}

	Function.doWhile(function doWhile(next) {

		var conditions = {};
		conditions[path] = current;

		if (that.unique_modifier_fields.length && record) {
			let value,
			    field,
			    i;

			for (i = 0; i < that.unique_modifier_fields.length; i++) {
				field = that.unique_modifier_fields[i];
				value = record[field];
				conditions[field] = value;
			}
		}

		model.find('first', {conditions: conditions}, function gotFirst(err, found_item) {

			if (err != null) {
				return next(err);
			}

			// Don't use the target record for duplicate checks
			if (found_item && for_record_id && String(for_record_id) == String(found_item._id)) {
				return next();
			}

			item = found_item;
			next();
		});
	}, function check() {

		// As long as items have been found we keep on adding numbers
		if (item) {

			count++;
			current = baseSlug + (that.replacement || '-') + count;

			return true;
		}

	}, function finished(err) {
		callback(err, current);
	});
});<|endoftext|>


<commit_before>/*
 * Package: filter_shield.js
 *
 * Namespace: bbop.widget.display.filter_shield
 *
 * BBOP object to produce a self-constructing/self-destructing shield
 * to support very large filter selection in the live search/search
 * pane genre.
 */

bbop.core.require('bbop', 'core');
bbop.core.require('bbop', 'logger');
//bbop.core.require('bbop', 'model');
//bbop.core.require('bbop', 'model', 'graph', 'bracket');
bbop.core.require('bbop', 'html');
bbop.core.require('bbop', 'golr', 'manager', 'jquery');
bbop.core.namespace('bbop', 'widget', 'display', 'filter_shield');

/*
 * Constructor: filter_shield
 *
 * Contructor for the bbop.widget.display.filter_shield object.
 *
 * Support for <bbop.widget.search_pane> by way of
 * <bbop.widget.display.live_search>
 *
 * Arguments:
 *  n/a
 *
 * Returns:
 *  self
 */
bbop.widget.display.filter_shield = function(){

    this._is_a = 'bbop.widget.display.filter_shield';

    var anchor = this;

    // Per-UI logger.
    var logger = new bbop.logger();
    logger.DEBUG = true;
    function ll(str){ logger.kvetch('W (filter_shield): ' + str); }

    // Variables that we'll need to keep.
    var is_open_p = false;
    var parea = new bbop.html.tag('div', {'generate_id': true});
    var pmsg = new bbop.html.tag('div', {'generate_id': true}, "Waiting...");
    var pbar = new bbop.html.tag('div', {'generate_id': true});
    parea.add_to(pmsg);
    parea.add_to(pbar);
    var div = new bbop.html.tag('div', {'generate_id': true}, parea);
    var pmsg_id = pmsg.get_id();
    var pbar_id = pbar.get_id();
    var div_id = div.get_id();
    var diargs = {
	modal: true,
	draggable: false,
	width: 800,
	height: 600,
	close:
	function(){
	    // TODO: Could maybe use .dialog('destroy') instead?
	    jQuery('#' + div_id).remove();
	}
    };

    /*
     * Function: start_wait
     *
     * Render an unpopulated modal shield with some kind of waiting
     * element. This is to act as a block for the IO if
     * desired--calling this before .draw() is not required (as
     * .draw() will call it anyways if you haven't).
     *
     * Arguments:
     *  n/a
     *
     * Returns:
     *  n/a
     */
    this.start_wait = function(){

	// Mark that we've finally opened it.
	is_open_p = true;

	// Append div to body.
	jQuery('body').append(div.to_string());

	// Pop open the dialog.
	var dia = jQuery('#' + div_id).dialog(diargs);

	// Start the progress bar in the dialog
	//var progress_val = 10;
	jQuery('#' + pbar_id).empty();
	jQuery('#' + pbar_id).progressbar({value: 10});
	// var progression_id = null;
	// function progression(){
	//     var success_p = false;
	//     if( jQuery('#' + pbar_id) ){
	// 	if( progress_val < 100 ){
	// 	    progress_val += 10;
	// 	    jQuery('#' + pbar_id).progressbar("value", progress_val);
	// 	    success_p = true;
	// 	}
	//     }
	//     if( ! success_p && progression_id ){
	// 	window.clearInterval(progression_id);
	// 	ll("waiting spinner interrupt");
	//     }
	// }
	// progression_id = window.setInterval(progression, 100);
    };

    /*
     * Function: draw
     *
     * Render a temporary modal filter shield.
     *
     * Arguments:
     *  field_name - the name (id) of the filter field to display
     *  filter_list - a list of [[filter_id, filter_count], ...]
     *  manager - the manager that we'll use for the callbacks
     *
     * Returns:
     *  n/a
     */
    this.draw = function(field_name, filter_list, manager){
	//ll(doc['id']);

	// Open the shield if it is not already open.
	if( ! is_open_p ){
	    anchor.open();
	}

	var txt = 'No filters...';
	var tbl = new bbop.html.table();
	var button_hash = {};
	var each = bbop.core.each; // conveience
	var bgen = bbop.widget.display.text_button_sim;
	each(filter_list,
 	     function(field){
		 var fname = field[0];
		 var fcount = field[1];

		 var b_plus = new bgen('+', 'Add positive filter');
		 var b_minus = new bgen('-', 'Add negative filter');
		 // var b_plus_txt = '[ + ]';
		 // var b_plus =
		 //     new bbop.html.span(b_plus_txt,
		 // 			{'generate_id': true});
		 // var b_minus_txt = '[ - ]';
		 // var b_minus =
		 //     new bbop.html.span(b_minus_txt,
		 // 			{'generate_id': true});
		 button_hash[b_plus.get_id()] =
		     [field_name, fname, fcount, '+'];
		 button_hash[b_minus.get_id()] =
		     [field_name, fname, fcount, '-'];

		 tbl.add_to([fname, '(' + fcount + ')',
			     b_plus.to_string(),
			     b_minus.to_string()]);
	     });
	txt = tbl.to_string();

	// Add text to div.
	jQuery('#' + div_id).empty();
	jQuery('#' + div_id).append(txt);

	// Okay, now introducing a function that we'll be using a
	// couple of times in our callbacks. Given a button id (from
	// a button hash) and the [field, filter, count, polarity]
	// values from the props, make a button-y thing an active
	// filter.
	function filter_select_live(button_id, create_time_button_props){
	    var in_polarity = create_time_button_props[3];

	    // Decide on the button graphical elements.
	    var b_ui_icon = 'ui-icon-plus';
	    if( in_polarity == '-' ){
		b_ui_icon = 'ui-icon-minus';
	    }
	    var b_ui_props = {
		icons: { primary: b_ui_icon},
		text: false
	    };

	    // Create the button and immediately add the event.
	    jQuery('#' + button_id).click(
		function(){
		    var tid = jQuery(this).attr('id');
		    var call_time_button_props = button_hash[tid];
		    var call_field = call_time_button_props[0];
		    var call_filter = call_time_button_props[1];
		    //var in_count = button_props[2];
		    var call_polarity = call_time_button_props[3];

		    // Change manager, fire, and close the dialog.
		    manager.add_query_filter(call_field, call_filter,
			  		     [call_polarity]);
		    manager.search();
		    jQuery('#' + div_id).remove();
		});
	}

	// Now let's go back and add the buttons, styles,
	// events, etc. in the main accordion section.
	each(button_hash, filter_select_live);

    };

};
<commit_msg>added more spinner images; spinner now semi-properly in filter shield widget; TODO: still more spinners in search_pane<commit_after>/*
 * Package: filter_shield.js
 *
 * Namespace: bbop.widget.display.filter_shield
 *
 * BBOP object to produce a self-constructing/self-destructing shield
 * to support very large filter selection in the live search/search
 * pane genre.
 */

bbop.core.require('bbop', 'core');
bbop.core.require('bbop', 'logger');
//bbop.core.require('bbop', 'model');
//bbop.core.require('bbop', 'model', 'graph', 'bracket');
bbop.core.require('bbop', 'html');
bbop.core.require('bbop', 'golr', 'manager', 'jquery');
bbop.core.namespace('bbop', 'widget', 'display', 'filter_shield');

/*
 * Constructor: filter_shield
 *
 * Contructor for the bbop.widget.display.filter_shield object.
 *
 * Support for <bbop.widget.search_pane> by way of
 * <bbop.widget.display.live_search>
 *
 * Arguments:
 *  n/a
 *
 * Returns:
 *  self
 */
bbop.widget.display.filter_shield = function(){

    this._is_a = 'bbop.widget.display.filter_shield';

    var anchor = this;

    // Per-UI logger.
    var logger = new bbop.logger();
    logger.DEBUG = true;
    function ll(str){ logger.kvetch('W (filter_shield): ' + str); }

    // Variables that we'll need to keep.
    var is_open_p = false;
    var parea = new bbop.html.tag('div', {'generate_id': true});
    var pmsg = new bbop.html.tag('div', {'generate_id': true}, "Waiting...");
    var pbar = new bbop.html.tag('div',
    				 {
    				     'generate_id': true,
    				     'style': 'padding:1em;',
    				     'class': 'ui-poll-loading-left'
    				 },
    				 "");
    //var pbar = new bbop.html.tag('div', {'generate_id': true});
    parea.add_to(pmsg);
    parea.add_to(pbar);
    var div = new bbop.html.tag('div', {'generate_id': true}, parea);
    var pmsg_id = pmsg.get_id();
    //var pbar_id = pbar.get_id();
    var div_id = div.get_id();
    var diargs = {
	modal: true,
	draggable: false,
	width: 800,
	height: 600,
	close:
	function(){
	    // TODO: Could maybe use .dialog('destroy') instead?
	    jQuery('#' + div_id).remove();
	}
    };

    /*
     * Function: start_wait
     *
     * Render an unpopulated modal shield with some kind of waiting
     * element. This is to act as a block for the IO if
     * desired--calling this before .draw() is not required (as
     * .draw() will call it anyways if you haven't).
     *
     * Arguments:
     *  n/a
     *
     * Returns:
     *  n/a
     */
    this.start_wait = function(){

	// Mark that we've finally opened it.
	is_open_p = true;

	// Append div to body.
	jQuery('body').append(div.to_string());

	// Pop open the dialog.
	var dia = jQuery('#' + div_id).dialog(diargs);

	// Start the progress bar in the dialog
	//var progress_val = 10;
	//jQuery('#' + pbar_id).empty();
	//jQuery('#' + pbar_id).progressbar({value: 10});
	// var progression_id = null;
	// function progression(){
	//     var success_p = false;
	//     if( jQuery('#' + pbar_id) ){
	// 	if( progress_val < 100 ){
	// 	    progress_val += 10;
	// 	    jQuery('#' + pbar_id).progressbar("value", progress_val);
	// 	    success_p = true;
	// 	}
	//     }
	//     if( ! success_p && progression_id ){
	// 	window.clearInterval(progression_id);
	// 	ll("waiting spinner interrupt");
	//     }
	// }
	// progression_id = window.setInterval(progression, 100);
    };

    /*
     * Function: draw
     *
     * Render a temporary modal filter shield.
     *
     * Arguments:
     *  field_name - the name (id) of the filter field to display
     *  filter_list - a list of [[filter_id, filter_count], ...]
     *  manager - the manager that we'll use for the callbacks
     *
     * Returns:
     *  n/a
     */
    this.draw = function(field_name, filter_list, manager){
	//ll(doc['id']);

	// Open the shield if it is not already open.
	if( ! is_open_p ){
	    anchor.open();
	}

	var txt = 'No filters...';
	var tbl = new bbop.html.table();
	var button_hash = {};
	var each = bbop.core.each; // conveience
	var bgen = bbop.widget.display.text_button_sim;
	each(filter_list,
 	     function(field){
		 var fname = field[0];
		 var fcount = field[1];

		 var b_plus = new bgen('+', 'Add positive filter');
		 var b_minus = new bgen('-', 'Add negative filter');
		 // var b_plus_txt = '[ + ]';
		 // var b_plus =
		 //     new bbop.html.span(b_plus_txt,
		 // 			{'generate_id': true});
		 // var b_minus_txt = '[ - ]';
		 // var b_minus =
		 //     new bbop.html.span(b_minus_txt,
		 // 			{'generate_id': true});
		 button_hash[b_plus.get_id()] =
		     [field_name, fname, fcount, '+'];
		 button_hash[b_minus.get_id()] =
		     [field_name, fname, fcount, '-'];

		 tbl.add_to([fname, '(' + fcount + ')',
			     b_plus.to_string(),
			     b_minus.to_string()]);
	     });
	txt = tbl.to_string();

	// Add text to div.
	jQuery('#' + div_id).empty();
	jQuery('#' + div_id).append(txt);

	// Okay, now introducing a function that we'll be using a
	// couple of times in our callbacks. Given a button id (from
	// a button hash) and the [field, filter, count, polarity]
	// values from the props, make a button-y thing an active
	// filter.
	function filter_select_live(button_id, create_time_button_props){
	    var in_polarity = create_time_button_props[3];

	    // Decide on the button graphical elements.
	    var b_ui_icon = 'ui-icon-plus';
	    if( in_polarity == '-' ){
		b_ui_icon = 'ui-icon-minus';
	    }
	    var b_ui_props = {
		icons: { primary: b_ui_icon},
		text: false
	    };

	    // Create the button and immediately add the event.
	    jQuery('#' + button_id).click(
		function(){
		    var tid = jQuery(this).attr('id');
		    var call_time_button_props = button_hash[tid];
		    var call_field = call_time_button_props[0];
		    var call_filter = call_time_button_props[1];
		    //var in_count = button_props[2];
		    var call_polarity = call_time_button_props[3];

		    // Change manager, fire, and close the dialog.
		    manager.add_query_filter(call_field, call_filter,
			  		     [call_polarity]);
		    manager.search();
		    jQuery('#' + div_id).remove();
		});
	}

	// Now let's go back and add the buttons, styles,
	// events, etc. in the main accordion section.
	each(button_hash, filter_select_live);

    };

};
<|endoftext|>


<commit_before>import { defaults } from 'lodash'
import { extractStyles, styleLoader } from './helpers'

export default function ({ isClient }) {
  let babelOptions = JSON.stringify(defaults(this.options.build.babel, {
    presets: ['vue-app'],
    babelrc: false,
    cacheDirectory: !!this.options.dev
  }))

  // https://github.com/vuejs/vue-loader/blob/master/docs/en/configurations
  const config = {
    postcss: this.options.build.postcss,
    loaders: {
      'js': 'babel-loader?' + babelOptions,
      'css': styleLoader.call(this, 'css'),
      'less': styleLoader.call(this, 'less', 'less-loader'),
      'sass': styleLoader.call(this, 'sass', 'sass-loader?indentedSyntax&?sourceMap'),
      'scss': styleLoader.call(this, 'sass', 'sass-loader?sourceMap'),
      'stylus': styleLoader.call(this, 'stylus', 'stylus-loader'),
      'styl': styleLoader.call(this, 'stylus', 'stylus-loader')
    },
    preserveWhitespace: false,
    extractCSS: extractStyles.call(this)
  }

  // Return the config
  return config
}
<commit_msg>remove extra ? in sass loader query<commit_after>import { defaults } from 'lodash'
import { extractStyles, styleLoader } from './helpers'

export default function ({ isClient }) {
  let babelOptions = JSON.stringify(defaults(this.options.build.babel, {
    presets: ['vue-app'],
    babelrc: false,
    cacheDirectory: !!this.options.dev
  }))

  // https://github.com/vuejs/vue-loader/blob/master/docs/en/configurations
  const config = {
    postcss: this.options.build.postcss,
    loaders: {
      'js': 'babel-loader?' + babelOptions,
      'css': styleLoader.call(this, 'css'),
      'less': styleLoader.call(this, 'less', 'less-loader'),
      'sass': styleLoader.call(this, 'sass', 'sass-loader?indentedSyntax&sourceMap'),
      'scss': styleLoader.call(this, 'sass', 'sass-loader?sourceMap'),
      'stylus': styleLoader.call(this, 'stylus', 'stylus-loader'),
      'styl': styleLoader.call(this, 'stylus', 'stylus-loader')
    },
    preserveWhitespace: false,
    extractCSS: extractStyles.call(this)
  }

  // Return the config
  return config
}
<|endoftext|>


<commit_before>'use strict';
// An SMTP Transaction

// node.js built-in modules
const util   = require('util');

// haraka npm modules
const config = require('haraka-config');
const Notes  = require('haraka-notes');
const utils  = require('haraka-utils');

// Haraka modules
const Header = require('./mailheader').Header;
const body   = require('./mailbody');
const MessageStream = require('./messagestream');

const MAX_HEADER_LINES = config.get('max_header_lines') || 1000;

class Transaction {
    constructor () {
        this.uuid = null;
        this.mail_from = null;
        this.rcpt_to = [];
        this.header_lines = [];
        this.data_lines = [];
        this.attachment_start_hooks = [];
        this.banner = null;
        this.body_filters = [];
        this.data_bytes = 0;
        this.header_pos = 0;
        this.found_hb_sep = false;
        this.body = null;
        this.parse_body = false;
        this.notes = new Notes();
        this.header = new Header();
        this.message_stream = null;
        this.discard_data = false;
        this.resetting = false;
        this.rcpt_count = {
            accept: 0,
            tempfail: 0,
            reject: 0,
        }
        this.msg_status = undefined;
        this.data_post_start = null;
        this.data_post_delay = 0;
        this.encoding = 'utf8';
        this.mime_part_count = 0;
    }

    ensure_body () {
        if (this.body) return;

        this.body = new body.Body(this.header);
        this.body.on('mime_boundary', m => this.incr_mime_count());
        this.attachment_start_hooks.forEach(h => {
            this.body.on('attachment_start', h);
        });

        if (this.banner) {
            this.body.set_banner(this.banner);
        }

        this.body_filters.forEach(o => {
            this.body.add_filter((ct, enc, buf) => {
                if ((util.isRegExp(o.ct_match) &&
                        o.ct_match.test(ct.toLowerCase())) ||
                    ct.toLowerCase()
                        .indexOf(String(o.ct_match)
                            .toLowerCase()) === 0) {
                    return o.filter(ct, enc, buf);
                }
            });
        });
    }

    add_data (line) {
        if (typeof line === 'string') { // This shouldn't ever happen.
            line = Buffer.from(line, this.encoding);
        }
        // is this the end of headers line?
        if (this.header_pos === 0 &&
            (line[0] === 0x0A || (line[0] === 0x0D && line[1] === 0x0A))) {
            this.header.parse(this.header_lines);
            this.header_pos = this.header_lines.length;
            this.found_hb_sep = true;
            if (this.parse_body) this.ensure_body();
        }
        else if (this.header_pos === 0) {
            // Build up headers
            if (this.header_lines.length < MAX_HEADER_LINES) {
                if (line[0] === 0x2E) line = line.slice(1); // Strip leading '.'
                this.header_lines.push(line.toString(this.encoding).replace(/\r\n$/, '\n'));
            }
        }
        else if (this.header_pos && this.parse_body) {
            let new_line = line;
            if (new_line[0] === 0x2E) new_line = new_line.slice(1); // Strip leading "."

            new_line = this.body.parse_more(new_line.toString(this.encoding).replace(/\r\n$/, '\n'));

            if (!new_line.length) return; // buffering for banners
        }

        if (!this.discard_data) this.message_stream.add_line(line);
    }

    end_data (cb) {
        if (!this.found_hb_sep && this.header_lines.length) {
            // Headers not parsed yet - must be a busted email
            // Strategy: Find the first line that doesn't look like a header.
            // Treat anything before that as headers, anything after as body.
            let header_pos = 0;
            for (let i = 0; i < this.header_lines.length; i++) {
                // Anything that doesn't match a header or continuation
                if (!/^(?:([^\s:]*):\s*([\s\S]*)$|[ \t])/.test(this.header_lines[i])) {
                    break;
                }
                header_pos = i;
            }
            const body_lines = this.header_lines.splice(header_pos + 1);
            this.header.parse(this.header_lines);
            this.header_pos = header_pos;
            if (this.parse_body) {
                this.ensure_body();
                for (let j = 0; j < body_lines.length; j++) {
                    this.body.parse_more(body_lines[j]);
                }
            }
        }
        if (this.header_pos && this.parse_body) {
            let data = this.body.parse_end();
            if (data.length) {
                data = data.toString(this.encoding)
                    .replace(/^\./gm, '..')
                    .replace(/\r?\n/gm, '\r\n');
                const line = Buffer.from(data, this.encoding);

                this.body.force_end();

                if (!this.discard_data) this.message_stream.add_line(line);
            }
        }

        if (this.discard_data) {
            cb();
        }
        else {
            this.message_stream.add_line_end(cb);
        }
    }

    add_header (key, value) {
        this.header.add_end(key, value);
        if (this.header_pos > 0) this.reset_headers();
    }

    add_leading_header (key, value) {
        this.header.add(key, value);
        if (this.header_pos > 0) this.reset_headers();
    }

    reset_headers () {
        const header_lines = this.header.lines();
        this.header_pos = header_lines.length;
    }

    remove_header (key) {
        this.header.remove(key);
        if (this.header_pos > 0) this.reset_headers();
    }

    attachment_hooks (start, data, end) {
        this.parse_body = true;
        this.attachment_start_hooks.push(start);
    }

    set_banner (text, html) {
        // throw "transaction.set_banner is currently non-functional";
        this.parse_body = true;
        if (!html) {
            html = text.replace(/\n/g, '
\n');
        }
        this.banner = [text, html];
    }

    add_body_filter (ct_match, filter) {
        this.parse_body = true;
        this.body_filters.push({ 'ct_match': ct_match, 'filter': filter });
    }

    incr_mime_count (line) {
        this.mime_part_count++;
    }
}

exports.Transaction = Transaction;
exports.MAX_HEADER_LINES = MAX_HEADER_LINES;

exports.createTransaction = uuid => {
    const t = new Transaction();
    t.uuid = uuid || utils.uuid();
    // Initialize MessageStream here to pass in the UUID
    t.message_stream = new MessageStream(
        config.get('smtp.ini'), t.uuid, t.header.header_list);
    return t;
}
<commit_msg>trans: assign conditions to named vars (#2638)<commit_after>'use strict';
// An SMTP Transaction

// node.js built-in modules
const util   = require('util');

// haraka npm modules
const config = require('haraka-config');
const Notes  = require('haraka-notes');
const utils  = require('haraka-utils');

// Haraka modules
const Header = require('./mailheader').Header;
const body   = require('./mailbody');
const MessageStream = require('./messagestream');

const MAX_HEADER_LINES = config.get('max_header_lines') || 1000;

class Transaction {
    constructor () {
        this.uuid = null;
        this.mail_from = null;
        this.rcpt_to = [];
        this.header_lines = [];
        this.data_lines = [];
        this.attachment_start_hooks = [];
        this.banner = null;
        this.body_filters = [];
        this.data_bytes = 0;
        this.header_pos = 0;
        this.found_hb_sep = false;
        this.body = null;
        this.parse_body = false;
        this.notes = new Notes();
        this.header = new Header();
        this.message_stream = null;
        this.discard_data = false;
        this.resetting = false;
        this.rcpt_count = {
            accept: 0,
            tempfail: 0,
            reject: 0,
        }
        this.msg_status = undefined;
        this.data_post_start = null;
        this.data_post_delay = 0;
        this.encoding = 'utf8';
        this.mime_part_count = 0;
    }

    ensure_body () {
        if (this.body) return;

        this.body = new body.Body(this.header);
        this.body.on('mime_boundary', m => this.incr_mime_count());
        this.attachment_start_hooks.forEach(h => {
            this.body.on('attachment_start', h);
        });

        if (this.banner) {
            this.body.set_banner(this.banner);
        }

        for (const o of body_filters) {
            this.body.add_filter((ct, enc, buf) => {
                const re_match = (util.isRegExp(o.ct_match) && o.ct_match.test(ct.toLowerCase()));
                const ct_begins = ct.toLowerCase().indexOf(String(o.ct_match).toLowerCase()) === 0;
                if (re_match || ct_begins) return o.filter(ct, enc, buf);
            })
        }
    }

    add_data (line) {
        if (typeof line === 'string') { // This shouldn't ever happen.
            line = Buffer.from(line, this.encoding);
        }
        // is this the end of headers line?
        if (this.header_pos === 0 &&
            (line[0] === 0x0A || (line[0] === 0x0D && line[1] === 0x0A))) {
            this.header.parse(this.header_lines);
            this.header_pos = this.header_lines.length;
            this.found_hb_sep = true;
            if (this.parse_body) this.ensure_body();
        }
        else if (this.header_pos === 0) {
            // Build up headers
            if (this.header_lines.length < MAX_HEADER_LINES) {
                if (line[0] === 0x2E) line = line.slice(1); // Strip leading '.'
                this.header_lines.push(line.toString(this.encoding).replace(/\r\n$/, '\n'));
            }
        }
        else if (this.header_pos && this.parse_body) {
            let new_line = line;
            if (new_line[0] === 0x2E) new_line = new_line.slice(1); // Strip leading "."

            new_line = this.body.parse_more(new_line.toString(this.encoding).replace(/\r\n$/, '\n'));

            if (!new_line.length) return; // buffering for banners
        }

        if (!this.discard_data) this.message_stream.add_line(line);
    }

    end_data (cb) {
        if (!this.found_hb_sep && this.header_lines.length) {
            // Headers not parsed yet - must be a busted email
            // Strategy: Find the first line that doesn't look like a header.
            // Treat anything before that as headers, anything after as body.
            let header_pos = 0;
            for (let i = 0; i < this.header_lines.length; i++) {
                // Anything that doesn't match a header or continuation
                if (!/^(?:([^\s:]*):\s*([\s\S]*)$|[ \t])/.test(this.header_lines[i])) {
                    break;
                }
                header_pos = i;
            }
            const body_lines = this.header_lines.splice(header_pos + 1);
            this.header.parse(this.header_lines);
            this.header_pos = header_pos;
            if (this.parse_body) {
                this.ensure_body();
                for (let j = 0; j < body_lines.length; j++) {
                    this.body.parse_more(body_lines[j]);
                }
            }
        }
        if (this.header_pos && this.parse_body) {
            let data = this.body.parse_end();
            if (data.length) {
                data = data.toString(this.encoding)
                    .replace(/^\./gm, '..')
                    .replace(/\r?\n/gm, '\r\n');
                const line = Buffer.from(data, this.encoding);

                this.body.force_end();

                if (!this.discard_data) this.message_stream.add_line(line);
            }
        }

        if (this.discard_data) {
            cb();
        }
        else {
            this.message_stream.add_line_end(cb);
        }
    }

    add_header (key, value) {
        this.header.add_end(key, value);
        if (this.header_pos > 0) this.reset_headers();
    }

    add_leading_header (key, value) {
        this.header.add(key, value);
        if (this.header_pos > 0) this.reset_headers();
    }

    reset_headers () {
        const header_lines = this.header.lines();
        this.header_pos = header_lines.length;
    }

    remove_header (key) {
        this.header.remove(key);
        if (this.header_pos > 0) this.reset_headers();
    }

    attachment_hooks (start, data, end) {
        this.parse_body = true;
        this.attachment_start_hooks.push(start);
    }

    set_banner (text, html) {
        // throw "transaction.set_banner is currently non-functional";
        this.parse_body = true;
        if (!html) {
            html = text.replace(/\n/g, '
\n');
        }
        this.banner = [text, html];
    }

    add_body_filter (ct_match, filter) {
        this.parse_body = true;
        this.body_filters.push({ 'ct_match': ct_match, 'filter': filter });
    }

    incr_mime_count (line) {
        this.mime_part_count++;
    }
}

exports.Transaction = Transaction;
exports.MAX_HEADER_LINES = MAX_HEADER_LINES;

exports.createTransaction = uuid => {
    const t = new Transaction();
    t.uuid = uuid || utils.uuid();
    // Initialize MessageStream here to pass in the UUID
    t.message_stream = new MessageStream(
        config.get('smtp.ini'), t.uuid, t.header.header_list);
    return t;
}
<|endoftext|>


<commit_before>import debug from 'debug';
import winston from 'winston';
import queue from '../queue';

export default class WebhookController {
  static debug(message) {
    debug('jiggl:WebhookController')(message);
  }

  static async processWebhook(ctx) {
    console.log(ctx.request.body);
    this.debug('processWebhook called');
    const { body } = ctx.request;
    winston.info(`${body.issue_event_type_name} webhook caught for ${body.issue.key}`, body);
    queue.create('upsert timer', body).priority('high').save();
    ctx.status = 200;
  }
}
<commit_msg>fix: remove debugging console log<commit_after>import debug from 'debug';
import winston from 'winston';
import queue from '../queue';

export default class WebhookController {
  static debug(message) {
    debug('jiggl:WebhookController')(message);
  }

  static async processWebhook(ctx) {
    this.debug('processWebhook called');
    const { body } = ctx.request;
    winston.info(`${body.issue_event_type_name} webhook caught for ${body.issue.key}`, body);
    queue.create('upsert timer', body).priority('high').save();
    ctx.status = 200;
  }
}
<|endoftext|>


<commit_before>//
//
//
var http=require('http'),
    fs=require('fs'),
    os=require('os'),
    express=require('express'),
    app = express(),
    context = {
      dRoot:'/data/tserv/dev/',
      scripts:'scripts/',
      logs:'logs/'
     },
    files = fs.readdirSync(context.dRoot + context.scripts);

app.configure(function(){
});

app.get('/scripts/:doc', function(req, res){
  var foo = fs.readFileSync(context.dRoot + context.scripts + '/' + req.params.doc);
  //res.send('deliver script:' + req.params.doc);
  res.send(foo);
});

app.get('/scripts', function(req, res){
  res.send(files);
});

app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(8000);
console.log("Server running at http://127.0.0.1:8000/");
<commit_msg>fixed tserv<commit_after>//
//
//
var http=require('http'),
    fs=require('fs'),
    os=require('os'),
    express=require('express'),
    app = express(),
    context = {
      dRoot:'/data/tserv/dev/',
      scripts:'scripts/',
      logs:'logs/'
     },
    files = fs.readdirSync(context.dRoot + context.scripts);

//app.configure(function(){});

app.get('/scripts/:doc', function(req, res){
  var foo = fs.readFileSync(context.dRoot + context.scripts + '/' + req.params.doc);
  //res.send('deliver script:' + req.params.doc);
  res.send(foo);
});

app.get('/scripts', function(req, res){
  res.send(files);
});

app.get('/', function(req, res){
  res.send('hello world');
});

app.listen(8000);
console.log("Server running at http://127.0.0.1:8000/");
<|endoftext|>


<commit_before>'use strict';
var _ = require('lodash');

var algos = {
  'eddsa': 'eddsa',
  'curve25519': 'curve25519',
  'triplesec-v3': 'triplesec-v3',
  'scrypt': 'scrypt',
  'nacl easybox': 'nacl-easybox',
  'nacl secretbox': 'nacl-secretbox',
};

algos.name = function(b) {
  b = ''+b; // coerce to string
  var found = _.findKey(algos, function(val) {
    return val === b.toLowerCase();
  });
  if (found) {
    return found;
  }

  throw new Error('unrecognized algo');
};

algos.value = function(key) {
  var algo = algos[key.toString().toLowerCase()];
  if (algo) {
    return algo;
  }

  throw new Error('unrecognized algo');
};

module.exports = algos;
<commit_msg>Rename alg value for easybox/secretbox<commit_after>'use strict';
var _ = require('lodash');

var algos = {
  'eddsa': 'eddsa',
  'curve25519': 'curve25519',
  'triplesec-v3': 'triplesec-v3',
  'scrypt': 'scrypt',
  'nacl easybox': 'easybox',
  'nacl secretbox': 'secretbox',
};

algos.name = function(b) {
  b = ''+b; // coerce to string
  var found = _.findKey(algos, function(val) {
    return val === b.toLowerCase();
  });
  if (found) {
    return found;
  }

  throw new Error('unrecognized algo');
};

algos.value = function(key) {
  var algo = algos[key.toString().toLowerCase()];
  if (algo) {
    return algo;
  }

  throw new Error('unrecognized algo');
};

module.exports = algos;
<|endoftext|>


<commit_before>/*globals document,window,$,jQuery */
/*jslint white: true, onevar: true, undef: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true, indent: 2 */

"use strict";

/*
 * jQuery UI Lightbox
 *
 * Copyright (c) 2009 Benjamin Doherty
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * Depends:
 *  ui.core.js
 *  ui.dialog.js
 *  jquery.mousewheel.js (optional)
 */
(function ($) {

  $.widget('ui.lightbox', {
    _init: function () {
      var self = this;

      // consider event delegation to make this more dynamic
      $(this.options.selector, this.element).click(function (event) {
        event.preventDefault();
        self.open(this);
      });
      $(document).keydown(function (event) {
        if (!self.lightbox || !self.lightbox.dialog('isOpen')) {
          return;
        }
        switch (event.keyCode) {
        case $.ui.keyCode.ESCAPE:
          if (self.options.closeOnEscape) {
            self.close();
          }
          break;
        case $.ui.keyCode.LEFT:
          self.prev("left");
          event.preventDefault();
          break;
        case $.ui.keyCode.UP:
          self.prev("up");
          event.preventDefault();
          break;
        case $.ui.keyCode.RIGHT:
          self.next("right");
          event.preventDefault();
          break;
        case $.ui.keyCode.DOWN:
          self.next("down");
          event.preventDefault();
          break;
        }
      });
      if ($.fn.mousewheel) {
        $(document).mousewheel(function (event, delta) {
          if (!self.lightbox.dialog('isOpen')) {
            return;
          }
          event.preventDefault();
          if (self.lightbox.is(":animated")) {
            return;
          }
          if (delta < 0) {
            self.next("down");
          }
          if (delta > 0) {
            self.prev("up");
          }
        });
      }
    },

    _makeDialog: function () {
      return $('
').dialog({
        autoOpen: false,
        closeOnEscape: false,
        modal: false,
        show: null,
        hide: null,
        dialogClass: this.options.dialogClass,
        position: this.options.position,
        resizable: this.options.resizable,
        draggable: this.options.draggable,
        height: this.options.height,
        width: this.options.width,
        // TODO: Support overlay by implementing focus,dragstop,resizestop
        open: function (event, ui) {
          $('.ui-dialog-buttonpane button', $(this).parents('.ui-dialog'))
            .each(function (index, domElement) {
              $(domElement).addClass('button-' + index);
            });
        }
      });
    },

    _buttons: {
      'Previous': function (eventObject) {
        $(this).dialog('option', '_lightbox').prev("right");
      },
      'Next': function (eventObject) {
        $(this).dialog('option', '_lightbox').next("left");
      }
    },

    destroy: function () {
      (this.overlay && this.overlay.destroy());
      this.element
        .unbind('.lightbox')
        .removeData('lightbox');
    },

    open: function (anchor) {
      var viewer = (this.lightbox = this._makeDialog());

      this.overlay = this.options.modal ? new $.ui.lightbox.overlay(viewer.data('dialog')) : null;
      this.setCurrentAnchor(anchor);

      viewer.dialog('option', 'buttons', this._buttons)
        .bind('dialogclose.lightbox', this._dialogClose)
        .dialog('open');

      viewer.dialog('option', '_lightbox', this);
    },

    close: function () {
      var self = this,
        anchor = this.getCurrentAnchor(),
        viewer = this.lightbox;

      (this.overlay && this.overlay.destroy());

      $.ui.lightbox.overlay.resize();

      // TODO: these need to be destroyed with the widget.
      viewer.dialog('destroy').remove();
    },

    next: function (direction) {
      this._rotate(":gt(", ":first", direction || "up");
    },

    prev: function (direction) {
      this._rotate(":lt(", ":last", direction || "down");
    },

    _anchors: function () {
      // if deemed necessary, cache selection here
      return this.element.find(this.options.selector);
    },

    _setData: function (key, value) {
      switch (key) {
      case "cursor":
        this.options[key] = value;
        this.spinner = new $.ui.lightbox.spinner(this);
        this._display(this._loadContent(value));
        this.spinner.destroy();
        break;
      }

      $.widget.prototype._setData.apply(this, arguments);
    },

    _display: function (content) {
      var anchor = this.getCurrentAnchor(),
        viewer = this.lightbox,
        size = this._calculateSize(content),
        $el = $(content).css(size).appendTo(viewer);

      viewer.dialog('option', 'title', $(anchor).attr('title') + this.options.titleSuffix);
      viewer.dialog('option', this._resize($el, size.width, size.height));
    },

    _loadContent: function (anchor) {
      var self = this,
        content = 'BUG',
        type = this.options.type ? this.options.type : this._deriveType(anchor);

      switch (type) {
      case "image":
        content = new Image();
        content.src = anchor.href;
        break;
      case "flash":
      case "flashvideo":
      case "quicktime":
      case "realplayer":
      case "windowsmedia":
        $(anchor).media({
          width: this.options.width,
          height: this.options.height,
          src: anchor.href,
          autoplay: 1
        }, function (element, options) {
        }, function (element, data, options, playerName) {
          content = $(data).html();
          $(data).media('undo');
        });
        break;
      case "iframe":
        content = this._element('iframe').attr('src', anchor.href).attr('frameborder', 0).attr('border', 0).html();
        break;
      case "html":
      case "dom":
        var reference = $($(anchor).attr('href'));
        content = $('
').append(reference.clone().show()).remove().html();
        break;
      case "ajax":
      case "script":
        $.ajax({
          url: anchor.href,
          type: (parseInt(self.options.post, 10) === 1) ? "POST" : "GET",
          cache: true,
          async: false,
          data: self.options.parameters,
          dataType: (type === "ajax") ? "html" : "script",
          success: function (data, textStatus) {
            content = data;
          }
        });
        break;
      }

      return content;
    },

    _deriveType: function (anchor) {
      var reference = anchor.href;
      if (reference.toLowerCase().match(/\.(gif|jpg|jpeg|png)(\?[0123456789]+)?$/)) {
        return "image";
      }
      if (reference.toLowerCase().match(/\.(swf)(\?[0123456789]+)?$/)) {
        return "flash";
      }
      if (reference.toLowerCase().match(/\.(flv)(\?[0123456789]+)?$/)) {
        return "flashvideo";
      }
      if (reference.toLowerCase().match(/\.(aif|aiff|aac|au|bmp|gsm|mov|mid|midi|mpg|mpeg|m4a|m4v|mp4|psd|qt|qtif|qif|qti|snd|tif|tiff|wav|3g2|3gp|wbmp)(\?[0123456789]+)?$/)) {
        return "quicktime";
      }
      if (reference.toLowerCase().match(/\.(ra|ram|rm|rpm|rv|smi|smil)(\?[0123456789]+)?$/)) {
        return "realplayer";
      }
      if (reference.toLowerCase().match(/\.(asf|avi|wma|wmv)(\?[0123456789]+)?$/)) {
        return "windowsmedia";
      }
      return "ajax";
    },

    _preloadNeighbours: function () {
      var anchors = this._anchors(),
        index = anchors.index(this.getCurrentAnchor());
      anchors.filter(this._neighbours(anchors.length, index)).each(function () {
        //new Image().src = this.href;
      });
    },

    _neighbours: function (index, length) {
      return ":eq(" + (index === 0 ? length - 1 : index - 1) + "), :eq(" + (index === length - 1 ? 0 : index + 1) + ")";
    },

    _position: function (img) {
    },

    _calculateSize: function (content) {
      var width,height;
      $.swap($(content).appendTo('
').appendTo(document.body)[0], {
        position: "absolute",
        visibility: "hidden",
        display: "block"
      }, function () {
        width = $(this).width(),
        height = $(this).height();
        $(this).remove();
      });
      return { width: width, height: height };
    },

    _resize: function (elem, width, height) {
      var viewer = this.lightbox,
        dialog = viewer.data('dialog'),
        offset = 20,
        finalWidth,finalHeight,

        // difference
        deltaContentWidth = viewer.outerWidth() - viewer.width(),
        deltaContentHeight = viewer.outerHeight() - viewer.height(),

        dialogTitlebarWidth = dialog.uiDialogTitlebar.outerWidth(),
        dialogTitlebarHeight = dialog.uiDialogTitlebar.outerHeight(),

        // Window
        wWidth = $(window).width(),
        wHeight = $(window).height(),

        size = {width: width, height: height};

      // Desired width
      finalWidth = size.width + deltaContentWidth,
      finalHeight = size.height + deltaContentHeight + dialogTitlebarHeight,

      ratio = Math.min(
        Math.min(
          Math.min(wWidth - deltaContentWidth - offset, size.width) / size.width,
          Math.min(wHeight - deltaContentHeight - dialogTitlebarHeight - offset, size.height) / size.height, 1)),

      $.extend(size, {
        width: Math.round(ratio * size.width),
        height: Math.round(ratio * size.height)
      });

      elem.css(size);

      return { width: size.width + deltaContentWidth, height: size.height + deltaContentHeight + dialogTitlebarHeight };
    },

    setCurrentAnchor: function (anchor) {
      this._setData('cursor', anchor);
    },

    getCurrentAnchor: function () {
      return this._getData('cursor');
    },

    _rotate: function (selectorA, selectorB, direction) {
      var self = this,
        content,
        anchors = this._anchors(),
        target = current = this.getCurrentAnchor(),
        viewer = this.lightbox,
        dialog = viewer.data('dialog').uiDialog,

        effectOut = {
          direction: direction
        },
        effectIn = {
          direction: {up:"down",down:"up",left:"right",right:"left"}[direction]
        };

      if (!this.getCurrentAnchor()) {
        console.log('Called _rotate without an anchor');
        return;
      }

      if (anchors.length === 1) {
        return;
      }

      target = anchors.filter(selectorA + anchors.index(current) + ")" + selectorB)[0];
      if (!target && this.options.loop && anchors.length > 1) {
        target = anchors.filter(selectorB)[0];
      }

      dialog.hide(this.options.rotateOut, effectOut, this.options.duration, function () {
        viewer
          .unbind('dialogclose.lightbox')
          .bind('dialogclose.lightbox', self._rotateClose).dialog('close');
        self.setCurrentAnchor(target);
        $(this).show(self.options.rotateIn, effectIn, self.options.duration, function () {
          viewer
            .dialog('open')
            .unbind('dialogclose.lightbox')
            .bind('dialogclose.lightbox', self._dialogClose);
        });
      });
    },

    // Swappable dialog event handlers.

    _rotateClose: function (event, ui) {
      $(this).empty();
    },

    _dialogClose: function (event, ui) {
      $(this).empty().dialog('option', '_lightbox').close();
    },

    _element: function (type, clazz) {
      return $("<" + type + "/>").addClass(clazz).hide();
    }
  });

  $.extend($.ui.lightbox, {
    defaults: {
      loop: true,
      modal: true,
      overlay: {},
      post: 0,
      dialogClass: 'ui-lightbox',
      closeOnEscape: true,
      resizable: false,
      draggable: false,
      selector: "a[href]:has(img[src])",
      titleSuffix: "",
      position: 'center',
      width: 'auto',
      height: 'auto',
      parameters: {},
      duration: 1000,
      rotateIn: 'drop',
      rotateOut: 'drop',
      show: '',
      hide: ''
    },
    uuid: 0,
    overlay: function (dialog) {
      this.$el = $.ui.lightbox.overlay.create(dialog);
    },
    spinner: function (lightbox) {
      this.$el = $.ui.lightbox.spinner.create(lightbox);
    }
  });

  $.extend($.ui.lightbox.overlay, $.ui.dialog.overlay, {});

  $.extend($.ui.lightbox.overlay.prototype, $.ui.dialog.overlay.prototype, {
    destroy: function() {
      $.ui.lightbox.overlay.destroy(this.$el);
    }
  });

  $.extend($.ui.lightbox.spinner, {
    instances: [],
    create: function (lightbox) {
      if (this.instances.length === 0) {
        var $el = $('

').appendTo(document.body)
          .addClass('ui-loading-indicator ui-corner-all').fadeIn("slow");

        this.instances.push($el);
        return $el;
      };
    },
    destroy: function($el) {
      this.instances.splice($.inArray(this.instances, $el), 1);

      $el.remove();
    }
  });

  $.extend($.ui.lightbox.spinner.prototype, {
    destroy: function() {
      $.ui.lightbox.spinner.destroy(this.$el);
    }
  });

})(jQuery);
<commit_msg>remove width and height attributes when content needs to be resized.<commit_after>/*globals document,window,$,jQuery */
/*jslint white: true, onevar: true, undef: true, eqeqeq: true, plusplus: true, bitwise: true, regexp: true, strict: true, newcap: true, immed: true, indent: 2 */

"use strict";

/*
 * jQuery UI Lightbox
 *
 * Copyright (c) 2009 Benjamin Doherty
 * Dual licensed under the MIT (MIT-LICENSE.txt)
 * and GPL (GPL-LICENSE.txt) licenses.
 *
 * Depends:
 *  ui.core.js
 *  ui.dialog.js
 *  jquery.mousewheel.js (optional)
 */
(function ($) {

  $.widget('ui.lightbox', {
    _init: function () {
      var self = this;

      // consider event delegation to make this more dynamic
      $(this.options.selector, this.element).click(function (event) {
        event.preventDefault();
        self.open(this);
      });
      $(document).keydown(function (event) {
        if (!self.lightbox || !self.lightbox.dialog('isOpen')) {
          return;
        }
        switch (event.keyCode) {
        case $.ui.keyCode.ESCAPE:
          if (self.options.closeOnEscape) {
            self.close();
          }
          break;
        case $.ui.keyCode.LEFT:
          self.prev("left");
          event.preventDefault();
          break;
        case $.ui.keyCode.UP:
          self.prev("up");
          event.preventDefault();
          break;
        case $.ui.keyCode.RIGHT:
          self.next("right");
          event.preventDefault();
          break;
        case $.ui.keyCode.DOWN:
          self.next("down");
          event.preventDefault();
          break;
        }
      });
      if ($.fn.mousewheel) {
        $(document).mousewheel(function (event, delta) {
          if (!self.lightbox.dialog('isOpen')) {
            return;
          }
          event.preventDefault();
          if (self.lightbox.is(":animated")) {
            return;
          }
          if (delta < 0) {
            self.next("down");
          }
          if (delta > 0) {
            self.prev("up");
          }
        });
      }
    },

    _makeDialog: function () {
      return $('
').dialog({
        autoOpen: false,
        closeOnEscape: false,
        modal: false,
        show: null,
        hide: null,
        dialogClass: this.options.dialogClass,
        position: this.options.position,
        resizable: this.options.resizable,
        draggable: this.options.draggable,
        height: this.options.height,
        width: this.options.width,
        // TODO: Support overlay by implementing focus,dragstop,resizestop
        open: function (event, ui) {
          $('.ui-dialog-buttonpane button', $(this).parents('.ui-dialog'))
            .each(function (index, domElement) {
              $(domElement).addClass('button-' + index);
            });
        }
      });
    },

    _buttons: {
      'Previous': function (eventObject) {
        $(this).dialog('option', '_lightbox').prev("right");
      },
      'Next': function (eventObject) {
        $(this).dialog('option', '_lightbox').next("left");
      }
    },

    destroy: function () {
      (this.overlay && this.overlay.destroy());
      this.element
        .unbind('.lightbox')
        .removeData('lightbox');
    },

    open: function (anchor) {
      var viewer = (this.lightbox = this._makeDialog());

      this.overlay = this.options.modal ? new $.ui.lightbox.overlay(viewer.data('dialog')) : null;
      this.setCurrentAnchor(anchor);

      viewer.dialog('option', 'buttons', this._buttons)
        .bind('dialogclose.lightbox', this._dialogClose)
        .dialog('open');

      viewer.dialog('option', '_lightbox', this);
    },

    close: function () {
      var self = this,
        anchor = this.getCurrentAnchor(),
        viewer = this.lightbox;

      (this.overlay && this.overlay.destroy());

      $.ui.lightbox.overlay.resize();

      // TODO: these need to be destroyed with the widget.
      viewer.dialog('destroy').remove();
    },

    next: function (direction) {
      this._rotate(":gt(", ":first", direction || "up");
    },

    prev: function (direction) {
      this._rotate(":lt(", ":last", direction || "down");
    },

    _anchors: function () {
      // if deemed necessary, cache selection here
      return this.element.find(this.options.selector);
    },

    _setData: function (key, value) {
      switch (key) {
      case "cursor":
        this.options[key] = value;
        this.spinner = new $.ui.lightbox.spinner(this);
        this._display(this._loadContent(value));
        this.spinner.destroy();
        break;
      }

      $.widget.prototype._setData.apply(this, arguments);
    },

    _display: function (content) {
      var anchor = this.getCurrentAnchor(),
        viewer = this.lightbox,
        size = this._calculateSize(content),
        $el = $(content).css(size).appendTo(viewer);

      viewer.dialog('option', 'title', $(anchor).attr('title') + this.options.titleSuffix);
      viewer.dialog('option', this._resize($el, size.width, size.height));
    },

    _loadContent: function (anchor) {
      var self = this,
        content = 'BUG',
        type = this.options.type ? this.options.type : this._deriveType(anchor);

      switch (type) {
      case "image":
        content = new Image();
        content.src = anchor.href;
        break;
      case "flash":
      case "flashvideo":
      case "quicktime":
      case "realplayer":
      case "windowsmedia":
        $(anchor).media({
          width: this.options.width,
          height: this.options.height,
          src: anchor.href,
          autoplay: 1
        }, function (element, options) {
        }, function (element, data, options, playerName) {
          content = $(data).html();
          $(data).media('undo');
        });
        break;
      case "iframe":
        content = this._element('iframe').attr('src', anchor.href).attr('frameborder', 0).attr('border', 0).html();
        break;
      case "html":
      case "dom":
        var reference = $($(anchor).attr('href'));
        content = $('
').append(reference.clone().show()).remove().html();
        break;
      case "ajax":
      case "script":
        $.ajax({
          url: anchor.href,
          type: (parseInt(self.options.post, 10) === 1) ? "POST" : "GET",
          cache: true,
          async: false,
          data: self.options.parameters,
          dataType: (type === "ajax") ? "html" : "script",
          success: function (data, textStatus) {
            content = data;
          }
        });
        break;
      }

      return content;
    },

    _deriveType: function (anchor) {
      var reference = anchor.href;
      if (reference.toLowerCase().match(/\.(gif|jpg|jpeg|png)(\?[0123456789]+)?$/)) {
        return "image";
      }
      if (reference.toLowerCase().match(/\.(swf)(\?[0123456789]+)?$/)) {
        return "flash";
      }
      if (reference.toLowerCase().match(/\.(flv)(\?[0123456789]+)?$/)) {
        return "flashvideo";
      }
      if (reference.toLowerCase().match(/\.(aif|aiff|aac|au|bmp|gsm|mov|mid|midi|mpg|mpeg|m4a|m4v|mp4|psd|qt|qtif|qif|qti|snd|tif|tiff|wav|3g2|3gp|wbmp)(\?[0123456789]+)?$/)) {
        return "quicktime";
      }
      if (reference.toLowerCase().match(/\.(ra|ram|rm|rpm|rv|smi|smil)(\?[0123456789]+)?$/)) {
        return "realplayer";
      }
      if (reference.toLowerCase().match(/\.(asf|avi|wma|wmv)(\?[0123456789]+)?$/)) {
        return "windowsmedia";
      }
      return "ajax";
    },

    _preloadNeighbours: function () {
      var anchors = this._anchors(),
        index = anchors.index(this.getCurrentAnchor());
      anchors.filter(this._neighbours(anchors.length, index)).each(function () {
        //new Image().src = this.href;
      });
    },

    _neighbours: function (index, length) {
      return ":eq(" + (index === 0 ? length - 1 : index - 1) + "), :eq(" + (index === length - 1 ? 0 : index + 1) + ")";
    },

    _position: function (img) {
    },

    _calculateSize: function (content) {
      var width,height;
      $.swap($(content).appendTo('
').appendTo(document.body)[0], {
        position: "absolute",
        visibility: "hidden",
        display: "block"
      }, function () {
        width = $(this).width(),
        height = $(this).height();
        $(this).remove();
      });
      return { width: width, height: height };
    },

    _resize: function (elem, width, height) {
      var viewer = this.lightbox,
        dialog = viewer.data('dialog'),
        offset = 20,
        finalWidth,finalHeight,

        // difference
        deltaContentWidth = viewer.outerWidth() - viewer.width(),
        deltaContentHeight = viewer.outerHeight() - viewer.height(),

        dialogTitlebarWidth = dialog.uiDialogTitlebar.outerWidth(),
        dialogTitlebarHeight = dialog.uiDialogTitlebar.outerHeight(),

        // Window
        wWidth = $(window).width(),
        wHeight = $(window).height(),

        size = {width: width, height: height};

      // Desired width
      finalWidth = size.width + deltaContentWidth,
      finalHeight = size.height + deltaContentHeight + dialogTitlebarHeight,

      ratio = Math.min(
        Math.min(
          Math.min(wWidth - deltaContentWidth - offset, size.width) / size.width,
          Math.min(wHeight - deltaContentHeight - dialogTitlebarHeight - offset, size.height) / size.height, 1)),

      $.extend(size, {
        width: Math.round(ratio * size.width),
        height: Math.round(ratio * size.height)
      });

      elem.css(size);

      if (ratio !== 1) {
        elem.attr('width', '').attr('height', '');
      }

      return { width: size.width + deltaContentWidth, height: size.height + deltaContentHeight + dialogTitlebarHeight };
    },

    setCurrentAnchor: function (anchor) {
      this._setData('cursor', anchor);
    },

    getCurrentAnchor: function () {
      return this._getData('cursor');
    },

    _rotate: function (selectorA, selectorB, direction) {
      var self = this,
        content,
        anchors = this._anchors(),
        target = current = this.getCurrentAnchor(),
        viewer = this.lightbox,
        dialog = viewer.data('dialog').uiDialog,

        effectOut = {
          direction: direction
        },
        effectIn = {
          direction: {up:"down",down:"up",left:"right",right:"left"}[direction]
        };

      if (!this.getCurrentAnchor()) {
        console.log('Called _rotate without an anchor');
        return;
      }

      if (anchors.length === 1) {
        return;
      }

      target = anchors.filter(selectorA + anchors.index(current) + ")" + selectorB)[0];
      if (!target && this.options.loop && anchors.length > 1) {
        target = anchors.filter(selectorB)[0];
      }

      dialog.hide(this.options.rotateOut, effectOut, this.options.duration, function () {
        viewer
          .unbind('dialogclose.lightbox')
          .bind('dialogclose.lightbox', self._rotateClose).dialog('close');
        self.setCurrentAnchor(target);
        $(this).show(self.options.rotateIn, effectIn, self.options.duration, function () {
          viewer
            .dialog('open')
            .unbind('dialogclose.lightbox')
            .bind('dialogclose.lightbox', self._dialogClose);
        });
      });
    },

    // Swappable dialog event handlers.

    _rotateClose: function (event, ui) {
      $(this).empty();
    },

    _dialogClose: function (event, ui) {
      $(this).empty().dialog('option', '_lightbox').close();
    },

    _element: function (type, clazz) {
      return $("<" + type + "/>").addClass(clazz).hide();
    }
  });

  $.extend($.ui.lightbox, {
    defaults: {
      loop: true,
      modal: true,
      overlay: {},
      post: 0,
      dialogClass: 'ui-lightbox',
      closeOnEscape: true,
      resizable: false,
      draggable: false,
      selector: "a[href]:has(img[src])",
      titleSuffix: "",
      position: 'center',
      width: 'auto',
      height: 'auto',
      parameters: {},
      duration: 1000,
      rotateIn: 'drop',
      rotateOut: 'drop',
      show: '',
      hide: ''
    },
    uuid: 0,
    overlay: function (dialog) {
      this.$el = $.ui.lightbox.overlay.create(dialog);
    },
    spinner: function (lightbox) {
      this.$el = $.ui.lightbox.spinner.create(lightbox);
    }
  });

  $.extend($.ui.lightbox.overlay, $.ui.dialog.overlay, {});

  $.extend($.ui.lightbox.overlay.prototype, $.ui.dialog.overlay.prototype, {
    destroy: function() {
      $.ui.lightbox.overlay.destroy(this.$el);
    }
  });

  $.extend($.ui.lightbox.spinner, {
    instances: [],
    create: function (lightbox) {
      if (this.instances.length === 0) {
        var $el = $('

').appendTo(document.body)
          .addClass('ui-loading-indicator ui-corner-all').fadeIn("slow");

        this.instances.push($el);
        return $el;
      };
    },
    destroy: function($el) {
      this.instances.splice($.inArray(this.instances, $el), 1);

      $el.remove();
    }
  });

  $.extend($.ui.lightbox.spinner.prototype, {
    destroy: function() {
      $.ui.lightbox.spinner.destroy(this.$el);
    }
  });

})(jQuery);
<|endoftext|>