<commit_before>//Developer: Sabbir Ahmed Siddiquee,
//Software Engineer, Selise rockin' software
//Git profile: https://github.com/prince3339

//safe code mode
// here ; is used to make sure that other codes loaded from different library/framework/file finished

//A little library to perform various operations like hide, show, sorting, add etc for our Todo Application
//Usages:
//Can be accessed like Todo().add().show() or T$().add().show()

;(function (global) {
  'use strict';
  var vm = global;
  var Todo = function (todoList) {
    //return a new object when we'll call it
    return new Todo.init(todoList);
  }

  //set a prototype to add features when needed
  //prototype holds methods (to save memory space)
  Todo.prototype = {
    //This method will add new todo
    add: function (title, description) {

      if(this.todoList) {
        var todoList = this.todoList;
      }else {
        console.log('Database is empty');
        var todoList = [];
      }

      var todo = {
        id: Math.random().toString(36).substr(2, 9),
        title: title,
        description: description,
        status: false
      }
      //console.log(todos);
      todoList.push(todo);
      this.saveToLocalStorage('todos', todoList); //Calling this method to save data to localStorage

      var myForm = config.elems.addTodoForm;
      myForm.reset();

      config.fn.showAllTodos();

      this.hide(config.elems.addModal); //Calling this method to hide addModal

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },

    update: function (editableObj) {
      //Updating specific todo using array map
      var updated_list = this.todoList.map(function(objItem) {
        var newObj = {};
        if(objItem.id == editableObj.id) {
          newObj['id'] = editableObj.id;
          newObj['title'] = editableObj.title;
          newObj['description'] = editableObj.description;
          newObj['status'] = editableObj.status;
        }else {
          newObj['id'] = objItem.id;
          newObj['title'] = objItem.title;
          newObj['description'] = objItem.description;
          newObj['status'] = objItem.status;
        }

        return newObj;
      });

       this.saveToLocalStorage('todos', updated_list); //Calling this method to hide addModal
       //vm.location.reload();
       config.fn.showAllTodos();

       this.hide(editableObj.editModal);  //Calling this method to hide editModal

       // 'this' refers to the calling object at execution time
       // makes the method chainable
       return this;
    },

    hide: function (elem) {
      elem.style.display = "none";

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },
    show: function (elem) {
      elem.style.display = "block";

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },

    saveToLocalStorage: function (key, data) {
      localStorage.setItem(key, JSON.stringify(data));

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },

    removeFromLocalStorage: function(key) {
      localStorage.removeItem(key);

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },
    //This method will return searched list
    getSearchedResult: function (search_keyword, data) {
      return data.filter(function(item) {
        return item.title.toLowerCase().indexOf(search_keyword.toLowerCase()) !=-1 || item.description.toLowerCase().indexOf(search_keyword.toLowerCase()) !=-1;
      });
    },
    //This method will return sorted list
    sortList : function (data) {
      return data.sort(function(a, b) {
        var titleA = a.title.toUpperCase(); // ignore upper and lowercase
        var titleB = b.title.toUpperCase(); // ignore upper and lowercase
        if (titleA < titleB) {
          return -1; //Title A will stay top
        }
        if (titleA > titleB) {
          return 1; //Title B will stay top
        }

        // title must be equal
        return 0;
      });
    },
    errorMsgGen: function (msg) {
      var unavailable_msg = document.createElement('li');
      unavailable_msg.className = 'display-block padding-16 selise__todo__list text-center';
      unavailable_msg.setAttribute('id', 'unavailable_msg');

      var p = document.createElement('p');
      p.className = 'font-size-fixed-18 margin-0 opacity-50';
      p.innerText = msg;

      unavailable_msg.appendChild(p);

      listContainer.appendChild(unavailable_msg);

      return this;
    }
  }

  //This function will return value when Todo.init object w'll be created
  // the actual object is created here, allowing us to 'new' an object without calling 'new'
  Todo.init = function (todoList) {
    this.todoList = todoList || getTodos();
  }

  //store the prototype value to init function
  // trick borrowed from jQuery so we don't have to use the 'new' keyword
  Todo.init.prototype = Todo.prototype;

  //Make the Todo object available to global
  //I've named it Todo as well as T$
  global.Todo = global.T$ = Todo;

})(window)
<commit_msg>minor fixes in library<commit_after>//Developer: Sabbir Ahmed Siddiquee,
//Software Engineer, Selise rockin' software
//Git profile: https://github.com/prince3339

//safe code mode
// here ; is used to make sure that other codes loaded from different library/framework/file finished

//A little library to perform various operations like hide, show, sorting, add etc for our Todo Application
//Usages:
//Can be accessed like Todo().add().show() or T$().add().show()

;(function (global) {
  'use strict';
  var vm = global;
  var Todo = function (todoList) {
    //return a new object when we'll call it
    return new Todo.init(todoList);
  }

  //set a prototype to add features when needed
  //prototype holds methods (to save memory space)
  Todo.prototype = {
    //This method will add new todo
    add: function (title, description) {

      if(this.todoList) {
        var todoList = this.todoList;
      }else {
        console.log('Database is empty');
        var todoList = [];
      }

      var todo = {
        id: Math.random().toString(36).substr(2, 9),
        title: title,
        description: description,
        status: false
      }
      //console.log(todos);
      todoList.push(todo);
      this.saveToLocalStorage('todos', todoList); //Calling this method to save data to localStorage

      config.elems.addTodoForm.reset();
      config.fn.showAllTodos();

      this.hide(config.elems.addModal); //Calling this method to hide addModal

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },

    update: function (editableObj) {
      //Updating specific todo using array map
      var updated_list = this.todoList.map(function(objItem) {
        var newObj = {};
        if(objItem.id == editableObj.id) {
          newObj['id'] = editableObj.id;
          newObj['title'] = editableObj.title;
          newObj['description'] = editableObj.description;
          newObj['status'] = editableObj.status;
        }else {
          newObj['id'] = objItem.id;
          newObj['title'] = objItem.title;
          newObj['description'] = objItem.description;
          newObj['status'] = objItem.status;
        }

        return newObj;
      });

       this.saveToLocalStorage('todos', updated_list); //Calling this method to hide addModal
       //vm.location.reload();
       config.fn.showAllTodos();

       this.hide(editableObj.editModal);  //Calling this method to hide editModal

       // 'this' refers to the calling object at execution time
       // makes the method chainable
       return this;
    },

    hide: function (elem) {
      elem.style.display = "none";

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },
    show: function (elem) {
      elem.style.display = "block";

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },

    saveToLocalStorage: function (key, data) {
      localStorage.setItem(key, JSON.stringify(data));

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },

    removeFromLocalStorage: function(key) {
      localStorage.removeItem(key);

      // 'this' refers to the calling object at execution time
      // makes the method chainable
      return this;
    },
    //This method will return searched list
    getSearchedResult: function (search_keyword, data) {
      return data.filter(function(item) {
        return item.title.toLowerCase().indexOf(search_keyword.toLowerCase()) !=-1 || item.description.toLowerCase().indexOf(search_keyword.toLowerCase()) !=-1;
      });
    },
    //This method will return sorted list
    sortList : function (data) {
      return data.sort(function(a, b) {
        var titleA = a.title.toUpperCase(); // ignore upper and lowercase
        var titleB = b.title.toUpperCase(); // ignore upper and lowercase
        if (titleA < titleB) {
          return -1; //Title A will stay top
        }
        if (titleA > titleB) {
          return 1; //Title B will stay top
        }

        // title must be equal
        return 0;
      });
    },
    errorMsgGen: function (msg) {
      var unavailable_msg = document.createElement('li');
      unavailable_msg.className = 'display-block padding-16 selise__todo__list text-center';
      unavailable_msg.setAttribute('id', 'unavailable_msg');

      var p = document.createElement('p');
      p.className = 'font-size-fixed-18 margin-0 opacity-50';
      p.innerText = msg;

      unavailable_msg.appendChild(p);

      listContainer.appendChild(unavailable_msg);

      return this;
    }
  }

  //This function will return value when Todo.init object w'll be created
  // the actual object is created here, allowing us to 'new' an object without calling 'new'
  Todo.init = function (todoList) {
    this.todoList = todoList || getTodos();
  }

  //store the prototype value to init function
  // trick borrowed from jQuery so we don't have to use the 'new' keyword
  Todo.init.prototype = Todo.prototype;

  //Make the Todo object available to global
  //I've named it Todo as well as T$
  global.Todo = global.T$ = Todo;

})(window)
<|endoftext|>


<commit_before>/**
 * Created by michael on 11/15/15.
 */
/* global BLOCKED_STRINGS,  ALLOW, DENY, SCRUB, SWWL, CUSTOM_SETTINGS */
'use strict';

var stopleak = stopleak || {};

stopleak.PIIData = [];
stopleak.deny = [];
stopleak.allow = [];
stopleak.scrub = [];
stopleak.swwl = [];
stopleak.custSettings = {};

const ACTION_ALLOW = 'allow';
const ACTION_DENY = 'deny';
const ACTION_SCRUB = 'scrub';
const ACTION_UNKNOWN = 'unknown';

/**
 * Load user data from storage.
 */
function getUserData() {
    chrome.storage.sync.get(null, function(list) {
        stopleak.PIIData = list.hasOwnProperty(BLOCKED_STRINGS) ?
            list[BLOCKED_STRINGS] : [];

        stopleak.deny = list.hasOwnProperty(DENY) ?  list[DENY] : [];

        stopleak.allow = list.hasOwnProperty(ALLOW) ?  list[ALLOW] : [];

        stopleak.scrub = list.hasOwnProperty(SCRUB) ?  list[SCRUB] : [];

        stopleak.swwl = list.hasOwnProperty(SWWL) ?  list[SWWL] : [];

        stopleak.custSettings = list.hasOwnProperty(CUSTOM_SETTINGS) ?
            list[CUSTOM_SETTINGS] : {};
    });
}

/**
 * Update the user data that has changed.
 *
 * @param {Object} changes Object mapping each key that changed to its
 *     corresponding storage.StorageChange for that item.
 * @param {string} areaName The name of the storage area ("sync", "local" or
 *     "managed") the changes are for.
 */
function updateUserData(changes, areaName) {
    console.log('Syncing user data update from ' + areaName);
    var change;
    if (changes.hasOwnProperty(BLOCKED_STRINGS)) {
        change = changes[BLOCKED_STRINGS];
        if (change.hasOwnProperty('newValue')) {
            stopleak.PIIData = change.newValue;
        }
    }

    if (changes.hasOwnProperty(ALLOW)) {
        change = changes[ALLOW];
        if (change.hasOwnProperty('newValue')) {
            stopleak.allow = change.newValue;
        }
    }

    if (changes.hasOwnProperty(DENY)) {
        change = changes[DENY];
        if (change.hasOwnProperty('newValue')) {
            stopleak.deny = change.newValue;
        }
    }

    if (changes.hasOwnProperty(SCRUB)) {
        change = changes[SCRUB];
        if (change.hasOwnProperty('newValue')) {
            stopleak.scrub = change.newValue;
        }
    }

    if (changes.hasOwnProperty(SWWL)) {
        change = changes[SWWL];
        if (change.hasOwnProperty('newValue')) {
            stopleak.swwl = change.newValue;
        }
    }

    if (changes.hasOwnProperty(CUSTOM_SETTINGS)) {
        change = changes[CUSTOM_SETTINGS];
        if (change.hasOwnProperty('newValue')) {
            stopleak.custSettings = change.custSettings;
        }
    }
}

/**
 * Returns ACTION_ALLOW, ACTION_DENY, or ACTION_SCRUB
 * if a request is allowed, null if unknown
 *
 * @param {String} src source of request
 * @param {String} dst destination of request
 */
stopleak.getReqAction = function(src, dst) {
    if (src === dst) {
        return ACTION_ALLOW;
    }

    var i;
    var len;

    //check cust settings
    if (stopleak.custSettings.hasOwnProperty(src) &&
        stopleak.custSettings[src].hasOwnProperty(dst)) {

        var action = stopleak.custSettings[src][dst];

        if (action !== ACTION_ALLOW || action !== ACTION_DENY ||
            action !== ACTION_SCRUB) {

            console.log('storage: invalid custom setting: ' + action);
            return ACTION_UNKNOWN;
        } else {
            return action;
        }
    }

    //check deny list
    len = stopleak.deny.length;
    for (i = 0; i < len; i++) {
        if (stopleak.deny[i] === dst) {
            return ACTION_DENY;
        }
    }

    //check SWWL
    len = stopleak.swwl.length;
    for (i = 0; i < len; i++) {
        if (stopleak.swwl[i] === src) {
            return ACTION_ALLOW;
        }
    }

    //check scrub list
    len = stopleak.scrub.length;
    for (i = 0; i < len; i++) {
        if (stopleak.scrub[i] === dst) {
            return ACTION_SCRUB;
        }
    }

    //check allow list
    len = stopleak.allow.length;
    for (i = 0; i < len; i++) {
        if (stopleak.allow[i] === dst) {
            return ACTION_ALLOW;
        }
    }

    return ACTION_UNKNOWN;
};

getUserData();
chrome.storage.onChanged.addListener(updateUserData);
<commit_msg>fixed for jenkins<commit_after>/**
 * Created by michael on 11/15/15.
 */
/* global BLOCKED_STRINGS,  ALLOW, DENY, SCRUB, SWWL, CUSTOM_SETTINGS */
'use strict';

var stopleak = stopleak || {};

stopleak.PIIData = [];
stopleak.deny = [];
stopleak.allow = [];
stopleak.scrub = [];
stopleak.swwl = [];
stopleak.custSettings = {};

const ACTION_ALLOW = 'allow';
const ACTION_DENY = 'deny';
const ACTION_SCRUB = 'scrub';
const ACTION_UNKNOWN = 'unknown';

/*
function addSetting(setting, map, onsuccess, onError) {
    switch (setting) {
        case CUSTOM_SETTINGS:
            chrome.storage.sync.get(null, function(items) {

                var custSett = items[CUSTOM_SETTINGS] || {};
                var inCustSett = custSett[map.src] || {};
                inCustSett[map.dst] = map.action;
                custSett[map.src] = inCustSett;
                items[CUSTOM_SETTINGS] = custSett;

                chrome.storage.sync.set(items, function() {
                    if (onsuccess !== null) {
                        onsuccess();
                    }
                });
            });
            break;
        default:
            console.log('do not know how to add: ' + setting);
            break;
    }
}
*/

/**
 * Load user data from storage.
 */
function getUserData() {
    chrome.storage.sync.get(null, function(list) {
        stopleak.PIIData = list.hasOwnProperty(BLOCKED_STRINGS) ?
            list[BLOCKED_STRINGS] : [];

        stopleak.deny = list.hasOwnProperty(DENY) ?  list[DENY] : [];

        stopleak.allow = list.hasOwnProperty(ALLOW) ?  list[ALLOW] : [];

        stopleak.scrub = list.hasOwnProperty(SCRUB) ?  list[SCRUB] : [];

        stopleak.swwl = list.hasOwnProperty(SWWL) ?  list[SWWL] : [];

        stopleak.custSettings = list.hasOwnProperty(CUSTOM_SETTINGS) ?
            list[CUSTOM_SETTINGS] : {};
    });
}

/**
 * Update the user data that has changed.
 *
 * @param {Object} changes Object mapping each key that changed to its
 *     corresponding storage.StorageChange for that item.
 * @param {string} areaName The name of the storage area ("sync", "local" or
 *     "managed") the changes are for.
 */
function updateUserData(changes, areaName) {
    console.log('Syncing user data update from ' + areaName);
    var change;
    if (changes.hasOwnProperty(BLOCKED_STRINGS)) {
        change = changes[BLOCKED_STRINGS];
        if (change.hasOwnProperty('newValue')) {
            stopleak.PIIData = change.newValue;
        }
    }

    if (changes.hasOwnProperty(ALLOW)) {
        change = changes[ALLOW];
        if (change.hasOwnProperty('newValue')) {
            stopleak.allow = change.newValue;
        }
    }

    if (changes.hasOwnProperty(DENY)) {
        change = changes[DENY];
        if (change.hasOwnProperty('newValue')) {
            stopleak.deny = change.newValue;
        }
    }

    if (changes.hasOwnProperty(SCRUB)) {
        change = changes[SCRUB];
        if (change.hasOwnProperty('newValue')) {
            stopleak.scrub = change.newValue;
        }
    }

    if (changes.hasOwnProperty(SWWL)) {
        change = changes[SWWL];
        if (change.hasOwnProperty('newValue')) {
            stopleak.swwl = change.newValue;
        }
    }

    if (changes.hasOwnProperty(CUSTOM_SETTINGS)) {
        change = changes[CUSTOM_SETTINGS];
        if (change.hasOwnProperty('newValue')) {
            stopleak.custSettings = change.custSettings;
        }
    }
}

/**
 * Returns ACTION_ALLOW, ACTION_DENY, or ACTION_SCRUB
 * if a request is allowed, null if unknown
 *
 * @param {String} src source of request
 * @param {String} dst destination of request
 */
stopleak.getReqAction = function(src, dst) {
    if (src === dst) {
        return ACTION_ALLOW;
    }

    var i;
    var len;

    //check cust settings
    if (stopleak.custSettings.hasOwnProperty(src) &&
        stopleak.custSettings[src].hasOwnProperty(dst)) {

        var action = stopleak.custSettings[src][dst];

        if (action !== ACTION_ALLOW || action !== ACTION_DENY ||
            action !== ACTION_SCRUB) {

            console.log('storage: invalid custom setting: ' + action);
            return ACTION_UNKNOWN;
        } else {
            return action;
        }
    }

    //check deny list
    len = stopleak.deny.length;
    for (i = 0; i < len; i++) {
        if (stopleak.deny[i] === dst) {
            return ACTION_DENY;
        }
    }

    //check SWWL
    len = stopleak.swwl.length;
    for (i = 0; i < len; i++) {
        if (stopleak.swwl[i] === src) {
            return ACTION_ALLOW;
        }
    }

    //check scrub list
    len = stopleak.scrub.length;
    for (i = 0; i < len; i++) {
        if (stopleak.scrub[i] === dst) {
            return ACTION_SCRUB;
        }
    }

    //check allow list
    len = stopleak.allow.length;
    for (i = 0; i < len; i++) {
        if (stopleak.allow[i] === dst) {
            return ACTION_ALLOW;
        }
    }

    return ACTION_UNKNOWN;
};

getUserData();
chrome.storage.onChanged.addListener(updateUserData);
<|endoftext|>


<commit_before>/*
   Copyright 2013 Callan Bryant <callan.bryant@gmail.com>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

// TODO: switch to requireJS or AMD modules
// TODO: convert to backbone?

$(function(){
	mf.init()
	mf.load()
})

var mf = {}

mf.init = function() {
	mf.nav = {}

	mf.nav.skip    = new mf.controllers.button('#skip')
	mf.nav.discard = new mf.controllers.button('#discard')
	mf.nav.publish = new mf.controllers.button('#publish')
	mf.nav.inspect = new mf.controllers.button('#inspect')
	mf.nav.undo    = new mf.controllers.button('#undo')

	mf.nav.skip.bind('right').bind('j').bind('s')
	mf.nav.discard.bind('x').bind('d')
	mf.nav.publish.bind('p')
	mf.nav.inspect.bind('i')
	mf.nav.undo.bind('u').bind('ctrl+z')
	mf.nav.undo.disable()

	mf.display = new mf.controllers.display('article')
	mf.display.wait()

	mf.pending = new mf.controllers.counter('#pending')
	// queue refill should load article
	mf.pending.change(function(from, to){
		document.title =  '('+to+') Megafilter'

		// is there another article to skip to?
		if (to < 2)
			mf.nav.skip.disable()
		// skip is always disabled before a load, always enable if valid
		else
			mf.nav.skip.enable()
	})

	mf.nav.skip.action(mf.skip)
	mf.nav.discard.action(mf.discard)
	mf.nav.publish.action(mf.publish)
	mf.nav.inspect.action(mf.inspect)

	setInterval(mf.check_pending,5000)

	mf.notification = new mf.controllers.notification('#notification')
}

mf.controllers = {}

mf.controllers.display = function(selector) {
	var ele = $(selector)

	// currently displayed article, if any
	this.article = null

	// render a given node-feedparser article to the page
	this.render = function(article) {
		$('body').scrollTop(0)
		// clean this up FIXME
		this.article = article
		$('#placeholder').hide()
		$('#loading').hide()
		ele.css('visibility','visible')
		$('section.description',ele).html(article.description || article.summary)
		$('> h1 a',ele).html(article.title).attr('href',article.link)
		$('time',ele).attr('datetime',article.pubdate)
		$('.note',ele).empty()
		if (article.author) $('.note',ele).html(article.author).prepend(' by ')
		if (article.source.title) $('.note',ele).prepend(''+article.source.title+'').prepend(' from ')
		// also do link to source site homepage
		// open all article links in a new window
		$('a',ele).attr('target','_new')
		return this
	}

	var discard = this.discard = function() {
		this.article = null
		ele.css('visibility','hidden')
		return this
	}

	// show loading animation
	this.wait = function(){
		$('#loading').show()
		discard()
		$('#placeholder').hide()
		return this
	}

	// show a message in place of the article
	this.placeholder = function(msg) {
		$('#loading').hide()
		discard()
		$('#placeholder').show().text(msg)
		return this
	}
}

// positive article counter
mf.controllers.counter = function(selector) {
	var ele = $(selector)
	// count
	var value = " "

	// callback for when number changes
	var change = function(from,to) {}
	this.change = function(fn) {
		change = fn
	}

	this.set = function(number) {
		// from, to
		change(value,number)
		ele.text(number)
		value = number
		return this
	}

	// initialise to zero without triggering change()
	ele.text(0)

	this.get = function() {
		return value
	}

	this.decrement = function() {
		if (value > 0) {
			ele.text(--value)
			change(value+1,value)
		}

		return this
	}

	this.increment = function() {
		ele.text(++value)
		change(value+1,value)

		return this
	}
}

// navigation buttons (sort of like a view controller)
mf.controllers.button = function(selector){
	var ele = $(selector)
	var enabled = true;

	// callback for when button is clicked or key is pressed
	var action = function() {}
	this.action = function(fn) {
		action = fn
	}

	ele.click(function() {
		if (enabled)
			action()
	})

	this.enable = function() {
		enabled = true
		ele.removeClass('disabled')
		return this
	}

	this.disable = function() {
		enabled = false
		ele.addClass('disabled')
		return this
	}

	// bind a key
	this.bind = function(key) {

		// press
		$(document).bind('keydown',key,function() {
			if (!enabled) return false

			ele.addClass('depressed')
			return false
		})
		// release
		$(document).bind('keyup',key,function() {
			if (!enabled) return false

			ele.removeClass('depressed')
			action()
			return false
		})
		return this
	}
}

mf.controllers.notification = function(selector) {
	var ele = $(selector)
	var beep = $('#beep').get()[0]

	// timeout id for the fade out animation
	var timeout

	// user may have interest
	//ele.on('mouseenter',function(){
	//	clearTimeout(timeout)
	//})

	// set the message, optionally specifying an icon from font awesome,
	// without icon- prefix
	this.say = function(msg,icon) {
		if (!arguments[1])
			var icon = 'info-sign'

		$('body').scrollTop(0)

		var icon = $('').addClass('icon-'+icon)

		// cancel animation if pending and show again
		ele.stop(1).show().css('opacity',1)

		ele.html(msg).prepend(' ').prepend(icon)

		// another message pending? don't want it's replacement to fade too quick
		clearTimeout(timeout)
		// new extended time
		timeout = setTimeout(function(){
			$(ele).fadeOut(300)
		},2500)

		// may as well log in console
		console.log(msg)

		// chaining
		return this
	}

	this.beep = function() {
		// this is necessary, otherwise it won't work more than once
		// http://stackoverflow.com/questions/2702343/html5-audio-plays-only-once-in-my-javascript-code
		beep.load()
		beep.play()

		// chaining
		return this
	}

	// chain: make the message persistent (CALL AFTER say() IN CHAIN)
	this.persist = function() {
		clearTimeout(timeout)
		return this
	}

}

// last deleted or published article (MUST BE A CLONE)
mf.trash = {}

// -------model?

// download and display the next article (or current on first load)
mf.load = mf.skip = function() {
	// remove article, show loading animation
	mf.display.wait()

	// article operations not valid during loading
	mf.nav.discard.disable()
	mf.nav.publish.disable()
	mf.nav.inspect.disable()

	// undo has lost its context
	mf.nav.undo.disable()

	// do not want to load another article whilst this one is pending
	mf.nav.skip.disable()

	$.ajax({
		url: mf.display.article?'/next':'/current',
		type:'GET',
		error:function() {
			mf.display.placeholder('No more articles')
		},
		success:function(article) {
			mf.display.render(article)

			// this will also set whether skip is possible
			mf.pending.set(article.pending)

			// re-enable article operations
			mf.nav.discard.enable()
			mf.nav.publish.enable()
			mf.nav.inspect.enable()
		}

	})
}

mf.publish = function() {
	$.ajax({
		url:'/publish/'+mf.display.article.id,
		type:'PUT',
		error:function() {
			mf.notification.say('article already published','exclamation-sign').beep()
		},
		success:function() {
			mf.notification.say('published article','ok')
			mf.nav.undo.enable().action(mf.unpublish)
			mf.nav.publish.disable()
		}

	})
}

mf.discard = function() {
	// clone old article to trash
	mf.trash = $.extend({},mf.display.article)

	mf.pending.decrement()
	$.ajax({
		url:'/queue/'+mf.display.article.id,
		type:'DELETE',
		error:function() {
			mf.notification.say('could not delete previous article','exclamation-sign').beep()
		},
		success:function() {
			mf.notification.say('discarded previous article','ok')
			mf.nav.undo.enable().action(mf.undiscard)
		}
	})
	mf.load()
}

mf.unpublish = function() {
	mf.notification.say('retracting article','spinner icon-spin').persist()
	mf.nav.undo.disable()

	$.ajax({
		url:'/published/'+mf.display.article.id,
		type:'DELETE',
		error:function() {
			mf.notification.say('could not undo publish','exclamation-sign').beep()
		},
		success:function() {
			mf.notification.say('article retracted','ok')
			mf.nav.publish.enable()
		}
	})
}

mf.inspect = function() {
	window.open(mf.display.article.link)
}

// restore last extracted article to queue
mf.undiscard = function() {
	mf.notification.say('restoring previous article to queue...','spinner icon-spin').persist()
	mf.nav.undo.disable()

	$.ajax({
		url:'/enqueue',
		type:'POST',
		data: mf.trash,
		error:function() {
			mf.notification.say('could not restore previous article','exclamation-sign').beep()
		},
		success:function() {
			mf.notification.say('sucessfully restored previous article to queue','ok')
			// load article on restore to empty queue
			if (mf.pending.get() == 0) mf.load()
			mf.pending.increment()
		}

	})
}

// periodically update counter and load new article if queue is re-formed
mf.check_pending = function () {
	$.ajax({
		url: '/pending',
		type: 'GET',
		success: function(d) {
			// queue has an item for the first time since
			if (d.pending > 0 && mf.pending.get() == 0) {
				mf.load()
				mf.notification.say('new article in queue').beep()
			}

			mf.pending.set(d.pending)
		},
		error: function() {
			mf.notification.say('network error','warning-sign')
		}
	})
}
<commit_msg>fix 404 cache problem<commit_after>/*
   Copyright 2013 Callan Bryant <callan.bryant@gmail.com>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

// TODO: switch to requireJS or AMD modules
// TODO: convert to backbone?

$(function(){
	mf.init()
	mf.load()
})

var mf = {}

mf.init = function() {
	mf.nav = {}

	mf.nav.skip    = new mf.controllers.button('#skip')
	mf.nav.discard = new mf.controllers.button('#discard')
	mf.nav.publish = new mf.controllers.button('#publish')
	mf.nav.inspect = new mf.controllers.button('#inspect')
	mf.nav.undo    = new mf.controllers.button('#undo')

	mf.nav.skip.bind('right').bind('j').bind('s')
	mf.nav.discard.bind('x').bind('d')
	mf.nav.publish.bind('p')
	mf.nav.inspect.bind('i')
	mf.nav.undo.bind('u').bind('ctrl+z')
	mf.nav.undo.disable()

	mf.display = new mf.controllers.display('article')
	mf.display.wait()

	mf.pending = new mf.controllers.counter('#pending')
	// queue refill should load article
	mf.pending.change(function(from, to){
		document.title =  '('+to+') Megafilter'

		// is there another article to skip to?
		if (to < 2)
			mf.nav.skip.disable()
		// skip is always disabled before a load, always enable if valid
		else
			mf.nav.skip.enable()
	})

	mf.nav.skip.action(mf.skip)
	mf.nav.discard.action(mf.discard)
	mf.nav.publish.action(mf.publish)
	mf.nav.inspect.action(mf.inspect)

	setInterval(mf.check_pending,5000)

	mf.notification = new mf.controllers.notification('#notification')
}

mf.controllers = {}

mf.controllers.display = function(selector) {
	var ele = $(selector)

	// currently displayed article, if any
	this.article = null

	// render a given node-feedparser article to the page
	this.render = function(article) {
		$('body').scrollTop(0)
		// clean this up FIXME
		this.article = article
		$('#placeholder').hide()
		$('#loading').hide()
		ele.css('visibility','visible')
		$('section.description',ele).html(article.description || article.summary)
		$('> h1 a',ele).html(article.title).attr('href',article.link)
		$('time',ele).attr('datetime',article.pubdate)
		$('.note',ele).empty()
		if (article.author) $('.note',ele).html(article.author).prepend(' by ')
		if (article.source.title) $('.note',ele).prepend(''+article.source.title+'').prepend(' from ')
		// also do link to source site homepage
		// open all article links in a new window
		$('a',ele).attr('target','_new')
		return this
	}

	var discard = this.discard = function() {
		this.article = null
		ele.css('visibility','hidden')
		return this
	}

	// show loading animation
	this.wait = function(){
		$('#loading').show()
		discard()
		$('#placeholder').hide()
		return this
	}

	// show a message in place of the article
	this.placeholder = function(msg) {
		$('#loading').hide()
		discard()
		$('#placeholder').show().text(msg)
		return this
	}
}

// positive article counter
mf.controllers.counter = function(selector) {
	var ele = $(selector)
	// count
	var value = " "

	// callback for when number changes
	var change = function(from,to) {}
	this.change = function(fn) {
		change = fn
	}

	this.set = function(number) {
		// from, to
		change(value,number)
		ele.text(number)
		value = number
		return this
	}

	// initialise to zero without triggering change()
	ele.text(0)

	this.get = function() {
		return value
	}

	this.decrement = function() {
		if (value > 0) {
			ele.text(--value)
			change(value+1,value)
		}

		return this
	}

	this.increment = function() {
		ele.text(++value)
		change(value+1,value)

		return this
	}
}

// navigation buttons (sort of like a view controller)
mf.controllers.button = function(selector){
	var ele = $(selector)
	var enabled = true;

	// callback for when button is clicked or key is pressed
	var action = function() {}
	this.action = function(fn) {
		action = fn
	}

	ele.click(function() {
		if (enabled)
			action()
	})

	this.enable = function() {
		enabled = true
		ele.removeClass('disabled')
		return this
	}

	this.disable = function() {
		enabled = false
		ele.addClass('disabled')
		return this
	}

	// bind a key
	this.bind = function(key) {

		// press
		$(document).bind('keydown',key,function() {
			if (!enabled) return false

			ele.addClass('depressed')
			return false
		})
		// release
		$(document).bind('keyup',key,function() {
			if (!enabled) return false

			ele.removeClass('depressed')
			action()
			return false
		})
		return this
	}
}

mf.controllers.notification = function(selector) {
	var ele = $(selector)
	var beep = $('#beep').get()[0]

	// timeout id for the fade out animation
	var timeout

	// user may have interest
	//ele.on('mouseenter',function(){
	//	clearTimeout(timeout)
	//})

	// set the message, optionally specifying an icon from font awesome,
	// without icon- prefix
	this.say = function(msg,icon) {
		if (!arguments[1])
			var icon = 'info-sign'

		$('body').scrollTop(0)

		var icon = $('').addClass('icon-'+icon)

		// cancel animation if pending and show again
		ele.stop(1).show().css('opacity',1)

		ele.html(msg).prepend(' ').prepend(icon)

		// another message pending? don't want it's replacement to fade too quick
		clearTimeout(timeout)
		// new extended time
		timeout = setTimeout(function(){
			$(ele).fadeOut(300)
		},2500)

		// may as well log in console
		console.log(msg)

		// chaining
		return this
	}

	this.beep = function() {
		// this is necessary, otherwise it won't work more than once
		// http://stackoverflow.com/questions/2702343/html5-audio-plays-only-once-in-my-javascript-code
		beep.load()
		beep.play()

		// chaining
		return this
	}

	// chain: make the message persistent (CALL AFTER say() IN CHAIN)
	this.persist = function() {
		clearTimeout(timeout)
		return this
	}

}

// last deleted or published article (MUST BE A CLONE)
mf.trash = {}

// -------model?

// download and display the next article (or current on first load)
mf.load = mf.skip = function() {
	// remove article, show loading animation
	mf.display.wait()

	// article operations not valid during loading
	mf.nav.discard.disable()
	mf.nav.publish.disable()
	mf.nav.inspect.disable()

	// undo has lost its context
	mf.nav.undo.disable()

	// do not want to load another article whilst this one is pending
	mf.nav.skip.disable()

	$.ajax({
		url: mf.display.article?'/next':'/current',
		type:'GET',
		data: {
			// despite setting headers, somehow a 404 is cached
			nocache: Math.random()
		},
		error:function() {
			mf.display.placeholder('No more articles')
		},
		success:function(article) {
			mf.display.render(article)

			// this will also set whether skip is possible
			mf.pending.set(article.pending)

			// re-enable article operations
			mf.nav.discard.enable()
			mf.nav.publish.enable()
			mf.nav.inspect.enable()
		}

	})
}

mf.publish = function() {
	$.ajax({
		url:'/publish/'+mf.display.article.id,
		type:'PUT',
		error:function() {
			mf.notification.say('article already published','exclamation-sign').beep()
		},
		success:function() {
			mf.notification.say('published article','ok')
			mf.nav.undo.enable().action(mf.unpublish)
			mf.nav.publish.disable()
		}

	})
}

mf.discard = function() {
	// clone old article to trash
	mf.trash = $.extend({},mf.display.article)

	mf.pending.decrement()
	$.ajax({
		url:'/queue/'+mf.display.article.id,
		type:'DELETE',
		error:function() {
			mf.notification.say('could not delete previous article','exclamation-sign').beep()
		},
		success:function() {
			mf.notification.say('discarded previous article','ok')
			mf.nav.undo.enable().action(mf.undiscard)
		}
	})
	mf.load()
}

mf.unpublish = function() {
	mf.notification.say('retracting article','spinner icon-spin').persist()
	mf.nav.undo.disable()

	$.ajax({
		url:'/published/'+mf.display.article.id,
		type:'DELETE',
		error:function() {
			mf.notification.say('could not undo publish','exclamation-sign').beep()
		},
		success:function() {
			mf.notification.say('article retracted','ok')
			mf.nav.publish.enable()
		}
	})
}

mf.inspect = function() {
	window.open(mf.display.article.link)
}

// restore last extracted article to queue
mf.undiscard = function() {
	mf.notification.say('restoring previous article to queue...','spinner icon-spin').persist()
	mf.nav.undo.disable()

	$.ajax({
		url:'/enqueue',
		type:'POST',
		data: mf.trash,
		error:function() {
			mf.notification.say('could not restore previous article','exclamation-sign').beep()
		},
		success:function() {
			mf.notification.say('sucessfully restored previous article to queue','ok')
			// load article on restore to empty queue
			if (mf.pending.get() == 0) mf.load()
			mf.pending.increment()
		}

	})
}

// periodically update counter and load new article if queue is re-formed
mf.check_pending = function () {
	$.ajax({
		url: '/pending',
		type: 'GET',
		success: function(d) {
			// queue has an item for the first time since
			if (d.pending > 0 && mf.pending.get() == 0) {
				mf.load()
				mf.notification.say('new article in queue').beep()
			}

			mf.pending.set(d.pending)
		},
		error: function() {
			mf.notification.say('network error','warning-sign')
		}
	})
}
<|endoftext|>


<commit_before>'use strict';
/* Dependency Tree Handler with Diff capabilities
 *
 * This service has not much to do - the tree itself is handled
 * completely by the dependencyTree directive.
 *
 * It has however additional diff capabilities, that are triggered
 * by a global diffLoaded event.
 * Knows how to pass style information to the tree visualization in
 * case a comparison/review was done.
 * * One could experiment here that this code should go back to a diff plugin
 * itself.
 * A diff plugin would calculate style information and pass this data load
 * through the diffLoaded event. The depTree service would listen to this
 * and pass the style info to its tree.
 *
 * Let's wait on a decision for this until we have done more work on the
 * diff plugin itself and resolved issue #80.
 * (https://github.com/latin-language-toolkit/llt-annotation_environment/issues/80)
 */
angular.module('arethusa.depTree').service('depTree', [
  'state',
  'configurator',
  '$rootScope',
  function (state, configurator, $rootScope) {
    var self = this;

    function configure() {
      configurator.getConfAndDelegate('depTree', self);
      self.diffMode = false;
    }

    configure();

    this.toggleDiff = function () {
      self.diffMode = !self.diffMode;
    };

    // We have three things we can colorize as wrong in the tree
    //   Label
    //   Head
    //   and the word itself for morphological stuff
    function analyseDiffs(tokens) {
      return arethusaUtil.inject({}, tokens, function (memo, id, token) {
        var diff = token.diff;
        if (diff) {
          memo[id] = analyseDiff(diff);
        }
      });
    }

    function analyseDiff(diff) {
      return arethusaUtil.inject({}, diff, function (memo, key, val) {
        if (key === 'relation') {
          memo.label = { color: 'red' };
        } else {
          if (key === 'head') {
            memo.edge = {
              stroke: 'red',
              'stroke-width': '1px'
            };
          } else {
            memo.token = { color: 'red' };
          }
        }
      });
    }

    this.diffStyles = function () {
      if (self.diffMode) {
        return self.diffInfo;
      } else {
        return false;
      }
    };

    $rootScope.$on('diffLoaded', function () {
      self.diffPresent = true;
      self.diffInfo = analyseDiffs(state.tokens);
      self.diffMode = true;
    });

    function hasNoHead(token) {
      return !(token.head || {}).id;
    }

    this.tokensWithoutHeadCount = function () {
      return state.countTokens(function (token) {
        return hasNoHead(token);
      });
    };

    // Used inside the context menu
    this.disconnect = function(token) {
      token.head.id = "";
    };

    this.toRoot = function(token) {
      token.head.id = '0000';
    };

    this.init = function () {
      configure();
    };
  }
]);
<commit_msg>Remove obsolete code from depTree<commit_after>'use strict';
/* Dependency Tree Handler with Diff capabilities
 *
 * This service has not much to do - the tree itself is handled
 * completely by the dependencyTree directive.
 *
 * It has however additional diff capabilities, that are triggered
 * by a global diffLoaded event.
 * Knows how to pass style information to the tree visualization in
 * case a comparison/review was done.
 * * One could experiment here that this code should go back to a diff plugin
 * itself.
 * A diff plugin would calculate style information and pass this data load
 * through the diffLoaded event. The depTree service would listen to this
 * and pass the style info to its tree.
 *
 * Let's wait on a decision for this until we have done more work on the
 * diff plugin itself and resolved issue #80.
 * (https://github.com/latin-language-toolkit/llt-annotation_environment/issues/80)
 */
angular.module('arethusa.depTree').service('depTree', [
  'state',
  'configurator',
  '$rootScope',
  function (state, configurator, $rootScope) {
    var self = this;

    function configure() {
      configurator.getConfAndDelegate('depTree', self);
      self.diffMode = false;
    }

    configure();

    this.toggleDiff = function () {
      self.diffMode = !self.diffMode;
    };

    // We have three things we can colorize as wrong in the tree
    //   Label
    //   Head
    //   and the word itself for morphological stuff
    function analyseDiffs(tokens) {
      return arethusaUtil.inject({}, tokens, function (memo, id, token) {
        var diff = token.diff;
        if (diff) {
          memo[id] = analyseDiff(diff);
        }
      });
    }

    function analyseDiff(diff) {
      return arethusaUtil.inject({}, diff, function (memo, key, val) {
        if (key === 'relation') {
          memo.label = { color: 'red' };
        } else {
          if (key === 'head') {
            memo.edge = {
              stroke: 'red',
              'stroke-width': '1px'
            };
          } else {
            memo.token = { color: 'red' };
          }
        }
      });
    }

    this.diffStyles = function () {
      if (self.diffMode) {
        return self.diffInfo;
      } else {
        return false;
      }
    };

    $rootScope.$on('diffLoaded', function () {
      self.diffPresent = true;
      self.diffInfo = analyseDiffs(state.tokens);
      self.diffMode = true;
    });

    // Used inside the context menu
    this.disconnect = function(token) {
      token.head.id = "";
    };

    this.toRoot = function(token) {
      token.head.id = '0000';
    };

    this.init = function () {
      configure();
    };
  }
]);
<|endoftext|>


<commit_before>const { _ } = require('@feathersjs/commons');
const debug = require('debug')('feathers-solr-query');
const { v4: uuidv4 } = require('uuid');
_.has = function has(obj, key) {
  return key.split('.').every(function (x) {
    if (typeof obj !== 'object' || obj === null || !(x in obj)) {
      return false;
    }
    obj = obj[x];
    return true;
  });
};
_.get = function get(obj, key) {
  return key.split('.').reduce(function (o, x) {
    return typeof o === 'undefined' || o === null ? o : o[x];
  }, obj);
};
const whitelist = ['$search', '$suggest', '$params', '$facet', '$populate'];

const Operators = {
  $in: (key, value) => {
    return Array.isArray(value) ? `${key}:(${value.join(' OR ')})` : `${key}:${value}`;
  },
  $or: (key, value) => {
    return Array.isArray(value) ? `(${value.join(' OR ')})` : `${key}:${value}`;
  },
  $lt: (key, value) => {
    return `${key}:[* TO ${value}}`;
  },
  $lte: (key, value) => {
    return `${key}:[* TO ${value}]`;
  },
  $gt: (key, value) => {
    return `${key}:{${value} TO *]`;
  },
  $gte: (key, value) => {
    return `${key}:[${value} TO *]`;
  },
  $like: (key, value) => {
    return `${key}:{${value}`;
  },
  $notlike: (key, value) => {
    return `!${key}:{${value}`;
  },
  $eq: (key, value) => {
    return Array.isArray(value) ? `(${value.join(' AND ')})` : `${key}:${value}`;
  },
  $ne: (key, value) => {
    return `!${key}:${value}`;
  },
  $and: (key, value) => {
    return `(${value.join(' AND ')})`;
  },
  $nin(key, value) {
    return Array.isArray(value) ? `!${key}:(${value.join(' OR ')})` : `!${key}:${value}`;
  },
  $limit(filters, paginate) {
    const result = {};
    if (typeof filters.$limit !== 'undefined') {
      if (_.has(paginate, 'max') && parseInt(filters.$limit) > parseInt(paginate.max)) {
        return;
      }
      result.limit = parseInt(filters.$limit);
    }
    return result;
  },
  // eslint-disable-next-line no-unused-vars
  $skip(filters, paginate) {
    return filters.$skip ? { offset: filters.$skip } : {};
  },
  // eslint-disable-next-line no-unused-vars
  $sort(filters, paginate) {
    const result = {};
    if (filters.$sort) {
      const sort = [];
      Object.keys(filters.$sort).forEach(name => {
        sort.push(name + (parseInt(filters.$sort[name]) === 1 ? ' asc' : ' desc'));
      });
      result.sort = sort.join(',');
    }
    return result;
  },
  $params(filters, query) {
    if (query.$params) return { params: convertBooleanAndInteger(query.$params) };
    return {};
  },
  $facet(filters, query) {
    if (query.$facet) return { facet: convertBooleanAndInteger(query.$facet) };
    return {};
  },
  $filter(filters, query) {
    if (query.$filter) return { filter: Array.isArray(query.$filter) ? query.$filter : [query.$filter] };
    return {};
  }
};

function jsonQuery(id, filters, query, paginate) {
  const adapterQuery = Object.assign({}, query);
  const result = Object.assign(
    {
      query: (adapterQuery.$search || '*:*').toString(),
      fields: filters.$select ? filters.$select.join(',') + ',id' : '*,score',
      limit: paginate.max || paginate.default || 10,
      offset: 0
    },
    Operators.$params(filters, adapterQuery),
    Operators.$facet(filters, adapterQuery),
    Operators.$sort(filters, paginate),
    Operators.$skip(filters, paginate),
    Operators.$limit(filters, paginate),
    Operators.$filter(filters, adapterQuery)
  );

  // merge id and query // TODO: Fix if query.id has operators
  if (id && _.has(adapterQuery, 'id')) {
    adapterQuery.id = `(${id} AND ${adapterQuery.id})`;
  } else if (id) {
    adapterQuery.id = id;
  }

  // convert special adapter params
  whitelist.forEach(param => {
    delete adapterQuery[param];
  });

  // convert adapterQuery
  if (!_.isEmpty(adapterQuery)) {
    result.filter = convertOperators(adapterQuery);
  }
  debug(result);
  return result;
}

function convertOperators(query, root = false) {
  if (Array.isArray(query)) {
    return query.map(q => {
      return convertOperators(q, false);
    });
  }

  if (!_.isObject(query)) {
    return query;
  }

  const converted = Object.keys(query).reduce((res, prop) => {
    const value = query[prop];
    let queryString;

    if (prop === '$or') {
      const o = [].concat.apply([], convertOperators(value, false));
      queryString = Operators.$or(root || prop, o);
    } else if (_.has(Operators, prop)) {
      queryString = Operators[prop](root || prop, value);
    } else if (typeof prop === 'string') {
      if (!_.isObject(value)) {
        queryString = Operators.$eq(root || prop, value);
      } else {
        queryString = convertOperators(value, prop);
      }
      if (Array.isArray(queryString)) {
        if (queryString.length > 1) {
          queryString = Operators.$and(root || prop, queryString);
        }
      }
      return res.concat(queryString);
    }
    res.push(queryString);
    return res;
  }, []);

  return converted;
}
function convertBooleanAndInteger(object) {
  return JSON.parse(JSON.stringify(object), (key, value) => {
    if (value == 'true') return true;
    if (value == 'false') return false;
    if (value == parseInt(value).toString()) return parseInt(value);
    return value;
  });
}
function deleteQuery(id, params) {
  if (id) {
    if (id === '*' || id === '*:*') {
      return { delete: { query: '*:*' } };
    }

    return { delete: id };
  } else if (_.isObject(params)) {
    const crit = [];

    Object.keys(params.query).forEach(function (name) {
      crit.push(name + ':' + params.query[name]);
    });

    return { delete: { query: crit.join(' AND ') } };
  }

  return { delete: { query: '*:*' } };
}

function patchQuery(toPatch, patch, idField) {
  toPatch = Array.isArray(toPatch) ? toPatch : [toPatch];

  const ids = toPatch.map(current => current[idField]);
  const atomicFieldUpdate = {};
  const actions = ['set', 'add', 'remove', 'removeregex', 'inc'];

  Object.keys(patch).forEach(field => {
    if (field !== idField) {
      const value = patch[field];
      if (_.isObject(value)) {
        if (actions.indexOf(Object.keys(value)[0]) === -1) {
          atomicFieldUpdate[field] = { add: value };
        }
      } else if (Array.isArray(value)) {
        atomicFieldUpdate[field] = { add: value };
      } else {
        atomicFieldUpdate[field] = value === '' ? { remove: value } : { set: value };
      }
    }
  });
  const patchData = toPatch.map(current => {
    return Object.assign({ [idField]: current[idField] }, atomicFieldUpdate);
  });

  return { ids, patchData };
}

const addId = (item, id) => {
  if (item[id] === undefined) {
    return Object.assign(
      {
        [id]: uuidv4()
      },
      item
    );
  }
  return item;
};

const getIds = (data, id) => {
  if (!Array.isArray(data) && data[id]) return [data[id]];

  return data.map(d => {
    return d[id];
  });
};

module.exports = {
  addId,
  getIds,
  jsonQuery,
  patchQuery,
  deleteQuery,
  whitelist,
  defaultOperators: Operators
};
<commit_msg>remove convertBooleanAndInteger<commit_after>const { _ } = require('@feathersjs/commons');
const debug = require('debug')('feathers-solr-query');
const { v4: uuidv4 } = require('uuid');
_.has = function has(obj, key) {
  return key.split('.').every(function (x) {
    if (typeof obj !== 'object' || obj === null || !(x in obj)) {
      return false;
    }
    obj = obj[x];
    return true;
  });
};
_.get = function get(obj, key) {
  return key.split('.').reduce(function (o, x) {
    return typeof o === 'undefined' || o === null ? o : o[x];
  }, obj);
};
const whitelist = ['$search', '$suggest', '$params', '$facet', '$populate'];

const Operators = {
  $in: (key, value) => {
    return Array.isArray(value) ? `${key}:(${value.join(' OR ')})` : `${key}:${value}`;
  },
  $or: (key, value) => {
    return Array.isArray(value) ? `(${value.join(' OR ')})` : `${key}:${value}`;
  },
  $lt: (key, value) => {
    return `${key}:[* TO ${value}}`;
  },
  $lte: (key, value) => {
    return `${key}:[* TO ${value}]`;
  },
  $gt: (key, value) => {
    return `${key}:{${value} TO *]`;
  },
  $gte: (key, value) => {
    return `${key}:[${value} TO *]`;
  },
  $like: (key, value) => {
    return `${key}:{${value}`;
  },
  $notlike: (key, value) => {
    return `!${key}:{${value}`;
  },
  $eq: (key, value) => {
    return Array.isArray(value) ? `(${value.join(' AND ')})` : `${key}:${value}`;
  },
  $ne: (key, value) => {
    return `!${key}:${value}`;
  },
  $and: (key, value) => {
    return `(${value.join(' AND ')})`;
  },
  $nin(key, value) {
    return Array.isArray(value) ? `!${key}:(${value.join(' OR ')})` : `!${key}:${value}`;
  },
  $limit(filters, paginate) {
    const result = {};
    if (typeof filters.$limit !== 'undefined') {
      if (_.has(paginate, 'max') && parseInt(filters.$limit) > parseInt(paginate.max)) {
        return;
      }
      result.limit = parseInt(filters.$limit);
    }
    return result;
  },
  // eslint-disable-next-line no-unused-vars
  $skip(filters, paginate) {
    return filters.$skip ? { offset: filters.$skip } : {};
  },
  // eslint-disable-next-line no-unused-vars
  $sort(filters, paginate) {
    const result = {};
    if (filters.$sort) {
      const sort = [];
      Object.keys(filters.$sort).forEach(name => {
        sort.push(name + (parseInt(filters.$sort[name]) === 1 ? ' asc' : ' desc'));
      });
      result.sort = sort.join(',');
    }
    return result;
  },
  $params(filters, query) {
    if (query.$params) return { params: query.$params };
    return {};
  },
  $facet(filters, query) {
    if (query.$facet) return { facet: query.$facet };
    return {};
  },
  $filter(filters, query) {
    if (query.$filter) return { filter: Array.isArray(query.$filter) ? query.$filter : [query.$filter] };
    return {};
  }
};

function jsonQuery(id, filters, query, paginate) {
  const adapterQuery = Object.assign({}, query);
  const result = Object.assign(
    {
      query: (adapterQuery.$search || '*:*').toString(),
      fields: filters.$select ? filters.$select.join(',') + ',id' : '*,score',
      limit: paginate.max || paginate.default || 10,
      offset: 0
    },
    Operators.$params(filters, adapterQuery),
    Operators.$facet(filters, adapterQuery),
    Operators.$sort(filters, paginate),
    Operators.$skip(filters, paginate),
    Operators.$limit(filters, paginate),
    Operators.$filter(filters, adapterQuery)
  );

  // merge id and query // TODO: Fix if query.id has operators
  if (id && _.has(adapterQuery, 'id')) {
    adapterQuery.id = `(${id} AND ${adapterQuery.id})`;
  } else if (id) {
    adapterQuery.id = id;
  }

  // convert special adapter params
  whitelist.forEach(param => {
    delete adapterQuery[param];
  });

  // convert adapterQuery
  if (!_.isEmpty(adapterQuery)) {
    result.filter = convertOperators(adapterQuery);
  }
  debug(result);
  return result;
}

function convertOperators(query, root = false) {
  if (Array.isArray(query)) {
    return query.map(q => {
      return convertOperators(q, false);
    });
  }

  if (!_.isObject(query)) {
    return query;
  }

  const converted = Object.keys(query).reduce((res, prop) => {
    const value = query[prop];
    let queryString;

    if (prop === '$or') {
      const o = [].concat.apply([], convertOperators(value, false));
      queryString = Operators.$or(root || prop, o);
    } else if (_.has(Operators, prop)) {
      queryString = Operators[prop](root || prop, value);
    } else if (typeof prop === 'string') {
      if (!_.isObject(value)) {
        queryString = Operators.$eq(root || prop, value);
      } else {
        queryString = convertOperators(value, prop);
      }
      if (Array.isArray(queryString)) {
        if (queryString.length > 1) {
          queryString = Operators.$and(root || prop, queryString);
        }
      }
      return res.concat(queryString);
    }
    res.push(queryString);
    return res;
  }, []);

  return converted;
}

function deleteQuery(id, params) {
  if (id) {
    if (id === '*' || id === '*:*') {
      return { delete: { query: '*:*' } };
    }

    return { delete: id };
  } else if (_.isObject(params)) {
    const crit = [];

    Object.keys(params.query).forEach(function (name) {
      crit.push(name + ':' + params.query[name]);
    });

    return { delete: { query: crit.join(' AND ') } };
  }

  return { delete: { query: '*:*' } };
}

function patchQuery(toPatch, patch, idField) {
  toPatch = Array.isArray(toPatch) ? toPatch : [toPatch];

  const ids = toPatch.map(current => current[idField]);
  const atomicFieldUpdate = {};
  const actions = ['set', 'add', 'remove', 'removeregex', 'inc'];

  Object.keys(patch).forEach(field => {
    if (field !== idField) {
      const value = patch[field];
      if (_.isObject(value)) {
        if (actions.indexOf(Object.keys(value)[0]) === -1) {
          atomicFieldUpdate[field] = { add: value };
        }
      } else if (Array.isArray(value)) {
        atomicFieldUpdate[field] = { add: value };
      } else {
        atomicFieldUpdate[field] = value === '' ? { remove: value } : { set: value };
      }
    }
  });
  const patchData = toPatch.map(current => {
    return Object.assign({ [idField]: current[idField] }, atomicFieldUpdate);
  });

  return { ids, patchData };
}

const addId = (item, id) => {
  if (item[id] === undefined) {
    return Object.assign(
      {
        [id]: uuidv4()
      },
      item
    );
  }
  return item;
};

const getIds = (data, id) => {
  if (!Array.isArray(data) && data[id]) return [data[id]];

  return data.map(d => {
    return d[id];
  });
};

module.exports = {
  addId,
  getIds,
  jsonQuery,
  patchQuery,
  deleteQuery,
  whitelist,
  defaultOperators: Operators
};
<|endoftext|>


<commit_before><commit_msg>Update validations.js<commit_after><|endoftext|>


<commit_before>var request = require('request'),
    async = require('async');

module.exports = function () {
    var self = this,
        flatten = function(arrayOfArray) {
            return [].concat.apply([], arrayOfArray);
        },
        buildProjectUrl = function(projectId) {
            return self.config.url + '/api/v4/projects/' + projectId;
        },
        buildProjectPipelinesUrl = function(projectId, ref) {
            var url = self.config.url + '/api/v4/projects/' + projectId + '/pipelines';
            if(ref) { url = url + '?ref=' + ref; }
            return url;
        },
        buildPipelineDetailsUrl = function(projectId, pipelineId) {
            return self.config.url + '/api/v4/projects/' + projectId + '/pipelines/' + pipelineId;
        },
        buildJobUrl = function(projectId, pipelineId) {
            return self.config.url + '/api/v4/projects/' + projectId + '/pipelines/' + pipelineId + '/jobs/';
        },
        getProjectsApiUrl = function(page, perPage) {
            var query = '?page=' + page + '&per_page=' + perPage + self.config.additional_query;
            return self.config.url + '/api/v4/projects' + query;
        },
        getRequestHeaders = function() {
            return { 'PRIVATE-TOKEN': self.config.token };
        },
        makeRequest = function (url, callback) {
            request({
                headers: getRequestHeaders(),
                'url': url,
                json: true
            }, function(err, response, body) {
                callback(err, body);
            });
        },
        getProjectPipelines = function(project, callback) {
            makeRequest(buildProjectPipelinesUrl(project.id, project.ref), function(err, pipelines) {
                if(err) {
                    callback(err);
                    return;
                }
                if (typeof self.config.numberOfPipelinesPerProject !== 'undefined') {
                    pipelines = pipelines.slice(0, self.config.numberOfPipelinesPerProject);
                }
                async.map(pipelines, function(pipeline, callback) {
                    getPipelineDetails(project, pipeline.id, callback);
                }, callback);
            });
        },
        getPipelineDetails = function(project, pipelineId, callback) {
            async.waterfall([
                function(callback) {
                    makeRequest(buildPipelineDetailsUrl(project.id, pipelineId), callback);
                },
                function(pipeline, callback) {
                    makeRequest(buildJobUrl(project.id, pipelineId), function(err, jobs) {
                        pipeline.jobs = jobs;
                        callback(err, simplifyBuild(project, pipeline));
                    });
                }
            ], callback);
        },
        getBuilds = function(callback) {
            if(self.projects.length === 0) {
                loadProjects();
            }
            async.map(self.projects, getProjectPipelines, function(err, builds) {
                if(err) {
                    callback(err);
                    return;
                }
                callback(err, flatten(builds));
            });
        },
        simplifyBuild = function(project, build) {
            return {
                id: project.id + '|' + build.id,
                number: build.id,
                project: project.name + '/' + build.ref,
                isRunning: ['running', 'pending'].includes(build.status),
                startedAt: getDateTime(build.started_at),
                finishedAt: getDateTime(build.finished_at),
                requestedFor: getAuthor(build),
                status: getBuildStatus(build.status),
                statusText: build.status,
                reason: getCommitMessage(build),
                hasErrors: false,
                hasWarnings: false,
                url: getBuildUrl(project, build)
            };
        },
        getDateTime = function(dateTime) {
            return dateTime ? new Date(dateTime) : dateTime;
        },
        getCommitMessage = function(build) {
            var job = build.jobs && build.jobs[0];
            return job && job.commit ? job.commit.message : undefined;
        },
        getAuthor = function(build) {
            var job = build.jobs && build.jobs[0];
            return job && job.commit ? job.commit.author_name : undefined;
        },
        getBuildStatus = function(status) {
            switch (status) {
                case 'pending':
                    return '#ffa500';
                case 'running':
                    return 'Blue';
                case 'failed':
                    return 'Red';
                case 'success':
                    return 'Green';
                default:
                    return 'Gray';
            }
        },
        getBuildUrl = function(project, build) {
            if(build.jobs && build.jobs[0]) {
                var base = self.config.url + '/';
                return base + project.path_with_namespace + '/-/jobs/' + build.jobs[0].id; //Not sure whether this works
            } else {
                return "";
            }
        },
        getAllProjects = function(callback) {
            request({
                headers: getRequestHeaders(),
                'url': getProjectsApiUrl(1, 100),
                json: true
            }, function(err, response, body) {
                if (!err && response.statusCode == 200) {
                    var urls = [], pages = Math.ceil(
                        parseInt(response.headers['x-total-pages'], 10));
                    for (var i = 1; i <= pages; i = i + 1) {
                        urls.push(getProjectsApiUrl(i, 100));
                    }

                    async.map(urls, makeRequest, function(err, projects){
                        callback(err, flatten(projects));
                    });
                }
            });
        },
        loadProjects = function() {
            var slugs = self.config.slugs,
                matchers = slugs.map(slug => slug.project);
            getAllProjects(function(err, projects){
                if(err) return;
                var indexOfAllMatch = matchers.indexOf('*/*');
                projects.forEach(function(project){
                    var indexOfNamespace = matchers.indexOf(project.namespace.name + "/*"),
                        indexOfProject = matchers.indexOf(project.path_with_namespace),
                        index = indexOfAllMatch > -1 ? indexOfAllMatch : (
                            indexOfNamespace > -1 ? indexOfNamespace : (
                            indexOfProject > -1 ? indexOfProject : null));

                    if(index !== null) {
                        if(slugs[index].ref) {
                            project.ref = slugs[index].ref;
                        }
                        self.projects.push(project);
                    }
                });
            });
        };

    self.configure = function (config) {
        self.config = config;
        self.projects = [];
        if (typeof self.config.slugs === 'undefined') {
            self.config.slugs = [{project: '*/*'}];
        }
        if (typeof self.config.additional_query === 'undefined') {
          self.config.additional_query = "";
        }
        if (typeof process.env.GITLAB_TOKEN !== 'undefined') {
            self.config.token = process.env.GITLAB_TOKEN;
        }
        if (typeof self.config.caPath !== 'undefined') {
            request = request.defaults({
                agentOptions: {
                    ca: require('fs').readFileSync(self.config.caPath).toString().split("\n\n")
                }
            });
        }
    };

    self.check = getBuilds;
};
<commit_msg>GitLab: getBuilds() did not call its callback when the projects were not loaded yet -- now it does<commit_after>var request = require('request'),
    async = require('async');

module.exports = function () {
    var self = this,
        flatten = function(arrayOfArray) {
            return [].concat.apply([], arrayOfArray);
        },
        buildProjectUrl = function(projectId) {
            return self.config.url + '/api/v4/projects/' + projectId;
        },
        buildProjectPipelinesUrl = function(projectId, ref) {
            var url = self.config.url + '/api/v4/projects/' + projectId + '/pipelines';
            if(ref) { url = url + '?ref=' + ref; }
            return url;
        },
        buildPipelineDetailsUrl = function(projectId, pipelineId) {
            return self.config.url + '/api/v4/projects/' + projectId + '/pipelines/' + pipelineId;
        },
        buildJobUrl = function(projectId, pipelineId) {
            return self.config.url + '/api/v4/projects/' + projectId + '/pipelines/' + pipelineId + '/jobs/';
        },
        getProjectsApiUrl = function(page, perPage) {
            var query = '?page=' + page + '&per_page=' + perPage + self.config.additional_query;
            return self.config.url + '/api/v4/projects' + query;
        },
        getRequestHeaders = function() {
            return { 'PRIVATE-TOKEN': self.config.token };
        },
        makeRequest = function (url, callback) {
            request({
                headers: getRequestHeaders(),
                'url': url,
                json: true
            }, function(err, response, body) {
                callback(err, body);
            });
        },
        getProjectPipelines = function(project, callback) {
            makeRequest(buildProjectPipelinesUrl(project.id, project.ref), function(err, pipelines) {
                if(err) {
                    callback(err);
                    return;
                }
                if (typeof self.config.numberOfPipelinesPerProject !== 'undefined') {
                    pipelines = pipelines.slice(0, self.config.numberOfPipelinesPerProject);
                }
                async.map(pipelines, function(pipeline, callback) {
                    getPipelineDetails(project, pipeline.id, callback);
                }, callback);
            });
        },
        getPipelineDetails = function(project, pipelineId, callback) {
            async.waterfall([
                function(callback) {
                    makeRequest(buildPipelineDetailsUrl(project.id, pipelineId), callback);
                },
                function(pipeline, callback) {
                    makeRequest(buildJobUrl(project.id, pipelineId), function(err, jobs) {
                        pipeline.jobs = jobs;
                        callback(err, simplifyBuild(project, pipeline));
                    });
                }
            ], callback);
        },
        getBuilds = function(callback) {
            if(self.projects.length === 0) {
                loadProjects(function () {
                    _getBuilds(callback);
                });
            } else {
                _getBuilds(callback);
            }
        },
        _getBuilds = function(callback) {
            async.map(self.projects, getProjectPipelines, function(err, builds) {
                if(err) {
                    callback(err);
                    return;
                }
                callback(err, flatten(builds));
            });
        },
        simplifyBuild = function(project, build) {
            return {
                id: project.id + '|' + build.id,
                number: build.id,
                project: project.name + '/' + build.ref,
                isRunning: ['running', 'pending'].includes(build.status),
                startedAt: getDateTime(build.started_at),
                finishedAt: getDateTime(build.finished_at),
                requestedFor: getAuthor(build),
                status: getBuildStatus(build.status),
                statusText: build.status,
                reason: getCommitMessage(build),
                hasErrors: false,
                hasWarnings: false,
                url: getBuildUrl(project, build)
            };
        },
        getDateTime = function(dateTime) {
            return dateTime ? new Date(dateTime) : dateTime;
        },
        getCommitMessage = function(build) {
            var job = build.jobs && build.jobs[0];
            return job && job.commit ? job.commit.message : undefined;
        },
        getAuthor = function(build) {
            var job = build.jobs && build.jobs[0];
            return job && job.commit ? job.commit.author_name : undefined;
        },
        getBuildStatus = function(status) {
            switch (status) {
                case 'pending':
                    return '#ffa500';
                case 'running':
                    return 'Blue';
                case 'failed':
                    return 'Red';
                case 'success':
                    return 'Green';
                default:
                    return 'Gray';
            }
        },
        getBuildUrl = function(project, build) {
            if(build.jobs && build.jobs[0]) {
                var base = self.config.url + '/';
                return base + project.path_with_namespace + '/-/jobs/' + build.jobs[0].id; //Not sure whether this works
            } else {
                return "";
            }
        },
        getAllProjects = function(callback) {
            request({
                headers: getRequestHeaders(),
                'url': getProjectsApiUrl(1, 100),
                json: true
            }, function(err, response, body) {
                if (!err && response.statusCode == 200) {
                    var urls = [], pages = Math.ceil(
                        parseInt(response.headers['x-total-pages'], 10));
                    for (var i = 1; i <= pages; i = i + 1) {
                        urls.push(getProjectsApiUrl(i, 100));
                    }

                    async.map(urls, makeRequest, function(err, projects){
                        callback(err, flatten(projects));
                    });
                }
            });
        },
        loadProjects = function(callback) {
            var slugs = self.config.slugs,
                matchers = slugs.map(slug => slug.project);
            getAllProjects(function(err, projects){
                if(err) return;
                var indexOfAllMatch = matchers.indexOf('*/*');
                projects.forEach(function(project){
                    var indexOfNamespace = matchers.indexOf(project.namespace.name + "/*"),
                        indexOfProject = matchers.indexOf(project.path_with_namespace),
                        index = indexOfAllMatch > -1 ? indexOfAllMatch : (
                            indexOfNamespace > -1 ? indexOfNamespace : (
                            indexOfProject > -1 ? indexOfProject : null));

                    if(index !== null) {
                        if(slugs[index].ref) {
                            project.ref = slugs[index].ref;
                        }
                        self.projects.push(project);
                    }
                });
                callback();
            });
        };

    self.configure = function (config) {
        self.config = config;
        self.projects = [];
        if (typeof self.config.slugs === 'undefined') {
            self.config.slugs = [{project: '*/*'}];
        }
        if (typeof self.config.additional_query === 'undefined') {
          self.config.additional_query = "";
        }
        if (typeof process.env.GITLAB_TOKEN !== 'undefined') {
            self.config.token = process.env.GITLAB_TOKEN;
        }
        if (typeof self.config.caPath !== 'undefined') {
            request = request.defaults({
                agentOptions: {
                    ca: require('fs').readFileSync(self.config.caPath).toString().split("\n\n")
                }
            });
        }
    };

    self.check = getBuilds;
};
<|endoftext|>


<commit_before>pc.extend(pc, function () {
    var ElementComponent = function ElementComponent (system, entity) {

        this._anchor = new pc.Vec4();
        this._pivot = new pc.Vec2();

        this._width = 32;
        this._height = 32;

        // the world transform in the 2D space
        this._worldTransform = new pc.Mat4();
        // the model transform used to render
        this._modelTransform = new pc.Mat4();
        // transform that updates local position according to anchor values
        this._anchorTransform = new pc.Mat4();

        this._anchorDirty = true;

        this.entity.on('insert', this._onInsert, this);

        this.screen = null;

        this._findScreen();
        entity.sync = this._sync;
    };
    ElementComponent = pc.inherits(ElementComponent, pc.Component);

    pc.extend(ElementComponent.prototype, {
        _onInsert: function (parent) {
            // when the entity is reparented find a possible new screen
            this._findScreen();
            if (this.screen) {
                this.entity.sync = this._sync;

                // calculate draw order
                this.screen.screen.syncDrawOrder();
            }
        },

        _findScreen: function () {
            var screen = this.entity._parent;
            while(screen && !screen.screen) {
                screen = screen._parent;
            }

            if (this.screen && this.screen !== screen) {
                this.screen.screen.off('set:resolution', this._onScreenResize, this);
                this.screen.screen.off('set:screenspace', this._onScreenSpaceChange, this);
            }

            this.screen = screen;
            if (this.screen) {
                this.screen.screen.on('set:resolution', this._onScreenResize, this);
                this.screen.screen.on('set:screenspace', this._onScreenSpaceChange, this);

                this.fire('set:screen', this.screen);
            }
        },

        _onScreenResize: function (res) {
            this._anchorDirty = true;
            this.entity.dirtyWorld = true;

            var minx = this._anchor.x;
            var miny = this._anchor.y;
            var maxx = this._anchor.z;
            var maxy = this._anchor.w;

            if (minx) {

            }

            // this.left = -100;
            // this.right = -100;
            // this.top = -100;
            // this.bottom = -100;

            // var dx = (maxx-minx);
            // if (dx > 0) {
            //     this.width = res.x*dx;
            // }
            // var dy = (maxy-miny);
            // if (dy > 0) {
            //     this.height = res.y*dy;
            // }

            // var p = this.entity.getLocalPosition();
            // if (dx > 0) p.x = -res.x*dx*this.pivot.x;
            // if (dy > 0) p.y = -res.y*dy*this.pivot.y;
            // this.entity.setLocalPosition(p);

            this.fire('screen:set:resolution', res);
        },

        _onScreenSpaceChange: function () {
            this.entity.dirtyWorld = true;
            this.fire('screen:set:screenspace', this.screen.screen.screenSpace);
        },

        _sync: function () {
            if (this.dirtyLocal) {
                this.localTransform.setTRS(this.localPosition, this.localRotation, this.localScale);

                this.dirtyLocal = false;
                this.dirtyWorld = true;
                this._aabbVer++;
            }

            var resx = 0;
            var resy = 0;
            var screen = this.element.screen;

            if (this.element._anchorDirty) {
                var px = 0;
                var py = 0;
                if (this._parent && this._parent.element) {
                    // use parent rect
                    resx = this._parent.element.width;
                    resy = this._parent.element.height;
                    px = this._parent.element.pivot.x - 0.5;
                    py = this._parent.element.pivot.y - 0.5;
                } else if (screen) {
                    // use screen rect
                    var resolution = screen.screen.resolution;
                    resx = resolution.x;
                    resy = resolution.y;
                }
                this.element._anchorTransform.setTranslate(-(resx*(this.element.anchor.x-px-0.5)), -(resy * (this.element.anchor.y-py-0.5)), 0);
                this.element._anchorDirty = false;
            }

            if (this.dirtyWorld) {
                if (this._parent === null) {
                    this.worldTransform.copy(this.localTransform);
                } else {
                    // transform element hierarchy
                    if (this._parent.element) {
                        this.element._worldTransform.mul2(this.element._anchorTransform, this.localTransform);
                        this.element._worldTransform.copy(this.localTransform);
                        this.element._worldTransform.mul2(this._parent.element._worldTransform, this.element._worldTransform);
                    } else {
                        this.element._worldTransform.mul2(this.element._anchorTransform, this.localTransform);
                        this.element._worldTransform.copy(this.localTransform);
                    }

                    if (screen) {
                        this.element._modelTransform.mul2(screen.screen._screenMatrix, this.element._worldTransform);

                        if (!screen.screen.screenSpace) {
                            this.worldTransform.mul2(screen.worldTransform, this.element._modelTransform);
                        } else {
                            this.worldTransform.copy(this.element._modelTransform);
                        }
                    } else {
                        this.worldTransform.copy(this.element._worldTransform);
                    }
                }

                this.dirtyWorld = false;
                var child;

                for (var i = 0, len = this._children.length; i < len; i++) {
                    child = this._children[i];
                    child.dirtyWorld = true;
                    child._aabbVer++;

                }
            }
        }
    });

    Object.defineProperty(ElementComponent.prototype, "drawOrder", {
        get: function () {
            return this._drawOrder;
        },

        set: function (value) {
            this._drawOrder = value;
            this.fire('set:draworder', this._drawOrder);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "left", {
        get: function () {
            var p = this.entity.getLocalPosition();
            var px = this.pivot.x;
            var resx = this.screen.screen.resolution.x;

            return p.x + resx*this._anchor.x + px*this.width;
        },

        set: function (value) {
            var p = this.entity.getLocalPosition();
            var resx = this.screen.screen.resolution.x;

            if (this._anchor.x === this._anchor.z) {
                p.x = value - this.pivot.x*this.width;
            } else {
                var diff = this.left - value;
                this.width += diff;
                p.x = this.pivot.x*this.width;
            }
            this.entity.setLocalPosition(p);

            // this.fire('set:left', this._width);
            // this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "right", {
        get: function () {
            var p = this.entity.getLocalPosition();
            var px = this.pivot.x;
            var resx = this.screen.screen.resolution.x;

            return p.x + resx*this._anchor.z - px*this.width;
        },

        set: function (value) {
            var p = this.entity.getLocalPosition();
            var resx = this.screen.screen.resolution.x;

            var diff = this.right - value;
            this.width += diff;

            p.x = resx*this._anchor.z - this.pivot.x*this.width;
            this.entity.setLocalPosition(p);
            // this.fire('set:left', this._width);
            // this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "top", {
        get: function () {
            var p = this.entity.getLocalPosition();
            return p.y - this.pivot.y*this.height;
        },

        set: function (value) {
            var p = this.entity.getLocalPosition();
            p.y = value - this.pivot.y*this.height;
            this.entity.setLocalPosition(p);

            // this.fire('set:left', this._width);
            // this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "bottom", {
        get: function () {
            var p = this.entity.getLocalPosition();
            return p.y + this.pivot.y*this.height;
        },

        set: function (value) {
            var p = this.entity.getLocalPosition();
            p.y = value + this.pivot.y*this.height;
            this.entity.setLocalPosition(p);

            // this.fire('set:left', this._width);
            // this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "width", {
        get: function () {
            return this._width;
        },

        set: function (value) {
            this._width = value;
            this.fire('set:width', this._width);
            this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "height", {
        get: function () {
            return this._height;
        },

        set: function (value) {
            this._height = value;
            this.fire('set:height', this._height);
            this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "pivot", {
        get: function () {
            return this._pivot;
        },

        set: function (value) {
            if (value instanceof pc.Vec2) {
                this._pivot.set(value.x, value.y);
            } else {
                this._pivot.set(value[0], value[1]);
            }
            this.fire('set:pivot', this._pivot);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "anchor", {
        get: function () {
            return this._anchor;
        },

        set: function (value) {
            if (value instanceof pc.Vec4) {
                this._anchor.set(value.x, value.y, value.z, value.w);
            } else {
                this._anchor.set(value[0], value[1], value[2], value[3]);
            }
            this._anchorDirty = true;
            this.fire('set:anchor', this._anchor);
        }
    });

    return {
        ElementComponent: ElementComponent
    };
}());

<commit_msg>Scaling anchors now working.<commit_after>pc.extend(pc, function () {
    var ElementComponent = function ElementComponent (system, entity) {

        this._anchor = new pc.Vec4();
        this._worldAnchor = new pc.Vec4();

        this._pivot = new pc.Vec2();

        this._width = 32;
        this._height = 32;

        // the world transform in the 2D space
        this._worldTransform = new pc.Mat4();
        // the model transform used to render
        this._modelTransform = new pc.Mat4();
        // transform that updates local position according to anchor values
        this._anchorTransform = new pc.Mat4();

        this._anchorDirty = true;

        this.entity.on('insert', this._onInsert, this);

        this.screen = null;

        this._findScreen();
        entity.sync = this._sync;
    };
    ElementComponent = pc.inherits(ElementComponent, pc.Component);

    pc.extend(ElementComponent.prototype, {
        _onInsert: function (parent) {
            // when the entity is reparented find a possible new screen
            this._findScreen();
            if (this.screen) {
                this.entity.sync = this._sync;

                // calculate draw order
                this.screen.screen.syncDrawOrder();
            }
        },

        _findScreen: function () {
            var screen = this.entity._parent;
            while(screen && !screen.screen) {
                screen = screen._parent;
            }

            if (this.screen && this.screen !== screen) {
                this.screen.screen.off('set:resolution', this._onScreenResize, this);
                this.screen.screen.off('set:screenspace', this._onScreenSpaceChange, this);
            }

            this.screen = screen;
            if (this.screen) {
                this.screen.screen.on('set:resolution', this._onScreenResize, this);
                this.screen.screen.on('set:screenspace', this._onScreenSpaceChange, this);

                this._setAnchors();
                this.fire('set:screen', this.screen);
            }
        },

        _onScreenResize: function (res) {
            this._anchorDirty = true;
            this.entity.dirtyWorld = true;

            var minx = this._worldAnchor.x;
            var miny = this._worldAnchor.y;
            var maxx = this._worldAnchor.z;
            var maxy = this._worldAnchor.w;
            var width = this.width;
            var height = this.height;
            var px = this.pivot.x;
            var py = this.pivot.y;

            this._setAnchors();

            var l = minx + this.left;
            var r = minx + this.right;

            var ldist = l - minx;
            var rdist = r - maxx;

            var newl = this._worldAnchor.x + ldist;
            var newr = this._worldAnchor.z + rdist;

            var newleft = newl - this._worldAnchor.x;
            var newright = newr - this._worldAnchor.x;

            this.width = newright - newleft;

            var b = miny + this.bottom;
            var t = miny + this.top;

            var bdist = b - miny;
            var tdist = t - maxy;

            var newb = this._worldAnchor.y + bdist;
            var newt = this._worldAnchor.w + tdist;

            var newbottom = newb - this._worldAnchor.y;
            var newtop = newt - this._worldAnchor.y;

            this.height = newtop - newbottom;

            var p = this.entity.getLocalPosition();
            p.x = p.x - px*width + px*this.width;
            p.y = p.y - py*height + py*this.height;
            this.entity.setLocalPosition(p);

            this.fire('screen:set:resolution', res);
        },

        _onScreenSpaceChange: function () {
            this.entity.dirtyWorld = true;
            this.fire('screen:set:screenspace', this.screen.screen.screenSpace);
        },

        _sync: function () {
            if (this.dirtyLocal) {
                this.localTransform.setTRS(this.localPosition, this.localRotation, this.localScale);

                this.dirtyLocal = false;
                this.dirtyWorld = true;
                this._aabbVer++;
            }

            var resx = 0;
            var resy = 0;
            var screen = this.element.screen;

            if (this.element._anchorDirty) {
                var px = 0;
                var py = 0;
                if (this._parent && this._parent.element) {
                    // use parent rect
                    resx = this._parent.element.width;
                    resy = this._parent.element.height;
                    px = this._parent.element.pivot.x;// - 0.5;
                    py = this._parent.element.pivot.y;// - 0.5;
                } else if (screen) {
                    // use screen rect
                    var resolution = screen.screen.resolution;
                    resx = resolution.x;
                    resy = resolution.y;
                }
                // this.element._anchorTransform.copy(pc.Mat4.IDENTITY);
                this.element._anchorTransform.setTranslate((resx*(this.element.anchor.x - px)), -(resy * (this.element.anchor.y - py)), 0);
                this.element._anchorDirty = false;
            }

            if (this.dirtyWorld) {
                if (this._parent === null) {
                    this.worldTransform.copy(this.localTransform);
                } else {
                    // transform element hierarchy
                    if (this._parent.element) {
                        this.element._worldTransform.mul2(this.element._anchorTransform, this.localTransform);
                        // this.element._worldTransform.copy(this.localTransform);
                        this.element._worldTransform.mul2(this._parent.element._worldTransform, this.element._worldTransform);
                    } else {
                        this.element._worldTransform.mul2(this.element._anchorTransform, this.localTransform);
                        // this.element._worldTransform.copy(this.localTransform);
                    }

                    if (screen) {
                        this.element._modelTransform.mul2(screen.screen._screenMatrix, this.element._worldTransform);

                        if (!screen.screen.screenSpace) {
                            this.worldTransform.mul2(screen.worldTransform, this.element._modelTransform);
                        } else {
                            this.worldTransform.copy(this.element._modelTransform);
                        }
                    } else {
                        this.worldTransform.copy(this.element._worldTransform);
                    }
                }

                this.dirtyWorld = false;
                var child;

                for (var i = 0, len = this._children.length; i < len; i++) {
                    child = this._children[i];
                    child.dirtyWorld = true;
                    child._aabbVer++;

                }
            }
        },

        _setAnchors: function () {
            var resx = 0;
            var resy = 0;
            if (this._parent && this._parent.element) {
                resx = this._parent.element.width;
                resy = this._parent.element.height;
            } else if (this.screen) {
                var res = this.screen.screen.resolution
                resx = res.x;
                resy = res.y;
            }

            this._worldAnchor.set(
                this._anchor.x*resx,
                this._anchor.y*resy,
                this._anchor.z*resx,
                this._anchor.w*resy
            );
        }
    });

    Object.defineProperty(ElementComponent.prototype, "drawOrder", {
        get: function () {
            return this._drawOrder;
        },

        set: function (value) {
            this._drawOrder = value;
            this.fire('set:draworder', this._drawOrder);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "left", {
        get: function () {
            var p = this.entity.getLocalPosition();
            return p.x - this.pivot.x * this.width;
        },

        set: function (value) {
            var change = value - this.left;
            this.width = this.right - value;
            this.entity.translateLocal(change/2,0,0);

            // this.fire('set:left', this._width);
            // this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "right", {
        get: function () {
            var p = this.entity.getLocalPosition();

            return p.x + (1 - this.pivot.x) * this.width;
        },

        set: function (value) {
            var change = value - this.right;
            this.width = value - this.left;
            this.entity.translateLocal(change/2, 0, 0);
            // this.fire('set:left', this._width);
            // this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "top", {
        get: function () {
            var p = this.entity.getLocalPosition();
            return p.y + this.pivot.y*this.height;
        },

        set: function (value) {
            var change = value - this.top;
            this.height = value - this.bottom;
            this.entity.translateLocal(0, change/2, 0);
            // this.fire('set:left', this._width);
            // this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "bottom", {
        get: function () {
            var p = this.entity.getLocalPosition();
            return p.y - (1 - this.pivot.y) * this.height;
        },

        set: function (value) {
            var change = value - this.bottom;
            this.height = this.top - value;
            this.entity.translateLocal(0, change/2, 0);
            // this.fire('set:left', this._width);
            // this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "width", {
        get: function () {
            return this._width;
        },

        set: function (value) {
            this._width = value;
            this.fire('set:width', this._width);
            this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "height", {
        get: function () {
            return this._height;
        },

        set: function (value) {
            this._height = value;
            this.fire('set:height', this._height);
            this.fire('resize', this._width, this._height);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "pivot", {
        get: function () {
            return this._pivot;
        },

        set: function (value) {
            if (value instanceof pc.Vec2) {
                this._pivot.set(value.x, value.y);
            } else {
                this._pivot.set(value[0], value[1]);
            }
            this.fire('set:pivot', this._pivot);
        }
    });

    Object.defineProperty(ElementComponent.prototype, "anchor", {
        get: function () {
            return this._anchor;
        },

        set: function (value) {
            if (value instanceof pc.Vec4) {
                this._anchor.set(value.x, value.y, value.z, value.w);
            } else {
                this._anchor.set(value[0], value[1], value[2], value[3]);
            }

            this._setAnchors();

            this._anchorDirty = true;
            this.fire('set:anchor', this._anchor);
        }
    });

    return {
        ElementComponent: ElementComponent
    };
}());

<|endoftext|>