<commit_before>if (typeof Prototype == 'undefined')
{
  warning = "ActiveScaffold Error: Prototype could not be found. Please make sure that your application's layout includes prototype.js (e.g. <%= javascript_include_tag :defaults %>) *before* it includes active_scaffold.js (e.g. <%= active_scaffold_includes %>).";
  alert(warning);
}
if (Prototype.Version.substring(0, 8) == '1.5.0_rc')
{
  warning = "ActiveScaffold Error: Prototype 1.5.0_rc is not supported. Please update prototype.js (rake rails:update:javascripts).";
  alert(warning);
}

/*
 * Simple utility methods
 */

var ActiveScaffold = {
  stripe: function(tableBody) {
    var even = false;
    var tableBody = $(tableBody);
    var tableRows = tableBody.down("tr");
    var length = tableBody.rows.length;

    for (var i = 0; i < length; i++) {
      var tableRow = tableBody.rows[i];
      //Make sure to skip rows that are create or edit rows or messages
      if (!tableRow.hasClassName("create")
        && !tableRow.hasClassName("update")
        && !tableRow.hasClassName("inline-adapter")
        && !tableRow.hasClassName("active-scaffold-calculations")) {

        if (even) {
          tableRow.addClassName("even");
        } else {
          tableRow.removeClassName("even");
        }
        even = !even;
      }
    }
  },
  toggleEmptyMessage: function(tableBody, emptyMessageElement) {
    // Check to see if this was the last element in the list
    if ($(tableBody).rows.length == 0) {
      $(emptyMessageElement).show();
    } else {
      $(emptyMessageElement).hide();
    }
  },
  removeSortClasses: function(scaffold_id) {
    $$('#' + scaffold_id + ' td.sorted').each(function(element) {
      element.removeClassName("sorted");
    });
    $$('#' + scaffold_id + ' th.sorted').each(function(element) {
      element.removeClassName("sorted");
      element.removeClassName("asc");
      element.removeClassName("desc");
    });
  },
  decrement_record_count: function(scaffold_id) {
    count = $$('#' + scaffold_id + ' span.active-scaffold-records').first();
    count.innerHTML = parseInt(count.innerHTML) - 1;
  },
  increment_record_count: function(scaffold_id) {
    count = $$('#' + scaffold_id + ' span.active-scaffold-records').first();
    count.innerHTML = parseInt(count.innerHTML) + 1;
  },

  server_error_response: '',
  report_500_response: function(active_scaffold_id) {
    messages_container = $(active_scaffold_id).down('td.messages-container');
    new Insertion.Top(messages_container, this.server_error_response);
  }
}

/*
 * Add-ons/Patches to Prototype
 */

/* patch to support replacing TR/TD/TBODY in Internet Explorer, courtesy of http://dev.rubyonrails.org/ticket/4273 */
Element.replace = function(element, html) {
  element = $(element);
  if (element.outerHTML) {
    try {
    element.outerHTML = html.stripScripts();
    } catch (e) {
      var tn = element.tagName;
      if(tn=='TBODY' || tn=='TR' || tn=='TD')
      {
              var tempDiv = document.createElement("div");
              tempDiv.innerHTML = '
' + html.stripScripts() + '
';
              element.parentNode.replaceChild(tempDiv.getElementsByTagName(tn).item(0), element);
      }
      else throw e;
    }
  } else {
    var range = element.ownerDocument.createRange();
    /* patch to fix <form> replaces in Firefox. see http://dev.rubyonrails.org/ticket/8010 */
    range.selectNodeContents(element.parentNode);
    element.parentNode.replaceChild(range.createContextualFragment(html.stripScripts()), element);
  }
  setTimeout(function() {html.evalScripts()}, 10);
  return element;
};

/*
 * URL modification support. Incomplete functionality.
 */
Object.extend(String.prototype, {
  append_params: function(params) {
    url = this;
    if (url.indexOf('?') == -1) url += '?';
    else if (url.lastIndexOf('&') != url.length) url += '&';

    url += $H(params).collect(function(item) {
      return item.key + '=' + item.value;
    }).join('&');

    return url;
  }
});

/**
 * A set of links. As a set, they can be controlled such that only one is "open" at a time, etc.
 */
ActiveScaffold.Actions = new Object();
ActiveScaffold.Actions.Abstract = function(){}
ActiveScaffold.Actions.Abstract.prototype = {
  initialize: function(links, target, loading_indicator, options) {
    this.target = $(target);
    this.loading_indicator = $(loading_indicator);
    this.options = options;
    this.links = links.collect(function(link) {
      return this.instantiate_link(link);
    }.bind(this));
  },

  instantiate_link: function(link) {
    throw 'unimplemented'
  }
}

/**
 * A DataStructures::ActionLink, represented in JavaScript.
 * Concerned with AJAX-enabling a link and adapting the result for insertion into the table.
 */
ActiveScaffold.ActionLink = new Object();
ActiveScaffold.ActionLink.Abstract = function(){}
ActiveScaffold.ActionLink.Abstract.prototype = {
  initialize: function(a, target, loading_indicator) {
    this.tag = $(a);
    this.url = this.tag.href;
    this.target = target;
    this.loading_indicator = loading_indicator;
    this.hide_target = false;
    this.position = this.tag.getAttribute('position');

    this.onclick = this.tag.onclick;
    this.tag.onclick = null;
    this.tag.observe('click', function(event) {
      this.open();
      Event.stop(event);
    }.bind(this));

    this.tag.action_link = this;
  },

  open: function() {
    if (this.is_disabled()) return;

		if (this.tag.hasAttribute( "dhtml_confirm")) {
			if (this.onclick) this.onclick();
			return;
		} else {
			if (this.onclick && !this.onclick()) return;//e.g. confirmation messages
			this.open_action();
		}
  },

	open_action: function() {
		if (this.position) this.disable();
    this.loading_indicator.style.visibility = 'visible';
    new Ajax.Request(this.url, {
      asynchronous: true,
      evalScripts: true,

      onSuccess: function(request) {
        if (this.position) {
          this.insert(request.responseText);
          if (this.hide_target) this.target.hide();
        } else {
          request.evalResponse();
        }
      }.bind(this),

      onFailure: function(request) {
        ActiveScaffold.report_500_response(this.scaffold_id());
        if (this.position) this.enable()
      }.bind(this),

      onComplete: function(request) {
        this.loading_indicator.style.visibility = 'hidden';
      }.bind(this)
		});
	},

  insert: function(content) {
    throw 'unimplemented'
  },

  close: function() {
    this.enable();
    this.adapter.remove();
    if (this.hide_target) this.target.show();
  },

  reload: function() {
    this.close();
    this.open();
  },

  get_new_adapter_id: function() {
    var id = 'adapter_';
    var i = 0;
    while ($(id + i)) i++;
    return id + i;
  },

  enable: function() {
    return this.tag.removeClassName('disabled');
  },

  disable: function() {
    return this.tag.addClassName('disabled');
  },

  is_disabled: function() {
    return this.tag.hasClassName('disabled');
  },

  scaffold_id: function() {
    return this.tag.up('div.active-scaffold').id;
  }
}

/**
 * Concrete classes for record actions
 */
ActiveScaffold.Actions.Record = Class.create();
ActiveScaffold.Actions.Record.prototype = Object.extend(new ActiveScaffold.Actions.Abstract(), {
  instantiate_link: function(link) {
    var l = new ActiveScaffold.ActionLink.Record(link, this.target, this.loading_indicator);
    l.refresh_url = this.options.refresh_url;
    if (l.position) l.url = l.url.append_params({adapter: '_list_inline_adapter'});
    l.set = this;
    return l;
  }
});

ActiveScaffold.ActionLink.Record = Class.create();
ActiveScaffold.ActionLink.Record.prototype = Object.extend(new ActiveScaffold.ActionLink.Abstract(), {
  insert: function(content) {
    this.set.links.each(function(item) {
      if (item.url != this.url && item.is_disabled() && item.adapter) item.close();
    }.bind(this));

    if (this.position == 'replace') {
      this.position = 'after';
      this.hide_target = true;
    }

    if (this.position == 'after') {
      new Insertion.After(this.target, content);
      this.adapter = this.target.next();
    }
    else if (this.position == 'before') {
      new Insertion.Before(this.target, content);
      this.adapter = this.target.previous();
    }
    else {
      return false;
    }

    var closer = function(event) {
      this.close_with_refresh();
      if (event) Event.stop(event);
    }.bind(this);
    var self = this;

    this.adapter.down('a.inline-adapter-close').observe('click', closer);
    // anything in the insert with a class of cancel gets the closer method, and a reference to this object for good measure
    this.adapter.getElementsByClassName('cancel').each(function(elem) {
      elem.observe('click', closer);
      elem.link = self;
    })

    new Effect.Highlight(this.adapter.down('td'));
  },

  close_with_refresh: function() {
    new Ajax.Request(this.refresh_url, {
      asynchronous: true,
      evalScripts: true,

      onSuccess: function(request) {
        Element.replace(this.target, request.responseText);
        var new_target = $(this.target.id);
        if (this.target.hasClassName('even')) new_target.addClassName('even');
        this.target = new_target;
        this.close();
      }.bind(this),

      onFailure: function(request) {
        ActiveScaffold.report_500_response(this.scaffold_id());
      }
    });
  },

  enable: function() {
    this.set.links.each(function(item) {
      if (item.url != this.url) return;
      item.tag.removeClassName('disabled');
    }.bind(this));
  },

  disable: function() {
    this.set.links.each(function(item) {
      if (item.url != this.url) return;
      item.tag.addClassName('disabled');
    }.bind(this));
  }
});

/**
 * Concrete classes for table actions
 */
ActiveScaffold.Actions.Table = Class.create();
ActiveScaffold.Actions.Table.prototype = Object.extend(new ActiveScaffold.Actions.Abstract(), {
  instantiate_link: function(link) {
    var l = new ActiveScaffold.ActionLink.Table(link, this.target, this.loading_indicator);
    if (l.position) l.url = l.url.append_params({adapter: '_list_inline_adapter'});
    return l;
  }
});

ActiveScaffold.ActionLink.Table = Class.create();
ActiveScaffold.ActionLink.Table.prototype = Object.extend(new ActiveScaffold.ActionLink.Abstract(), {
  insert: function(content) {
    if (this.position == 'top') {
      new Insertion.Top(this.target, content);
      this.adapter = this.target.immediateDescendants().first();
    }
    else {
      throw 'Unknown position "' + this.position + '"'
    }

    var closer = function(event) {
      this.close();
      if (event) Event.stop(event);
    }.bind(this);
    var self = this;

    this.adapter.down('a.inline-adapter-close').observe('click', closer);
    // anything in the insert with a class of cancel gets the closer method, and a reference to this object for good measure
    this.adapter.getElementsByClassName('cancel').each(function(elem) {
      elem.observe('click', closer);
      elem.link = self;
    })

    new Effect.Highlight(this.adapter.down('td'));
  }
});<commit_msg>fix for table striping after css changes<commit_after>if (typeof Prototype == 'undefined')
{
  warning = "ActiveScaffold Error: Prototype could not be found. Please make sure that your application's layout includes prototype.js (e.g. <%= javascript_include_tag :defaults %>) *before* it includes active_scaffold.js (e.g. <%= active_scaffold_includes %>).";
  alert(warning);
}
if (Prototype.Version.substring(0, 8) == '1.5.0_rc')
{
  warning = "ActiveScaffold Error: Prototype 1.5.0_rc is not supported. Please update prototype.js (rake rails:update:javascripts).";
  alert(warning);
}

/*
 * Simple utility methods
 */

var ActiveScaffold = {
  stripe: function(tableBody) {
    var even = false;
    var tableBody = $(tableBody);
    var tableRows = tableBody.down("tr");
    var length = tableBody.rows.length;

    for (var i = 0; i < length; i++) {
      var tableRow = tableBody.rows[i];
      //Make sure to skip rows that are create or edit rows or messages
      if (!tableRow.hasClassName("create")
        && !tableRow.hasClassName("update")
        && !tableRow.hasClassName("inline-adapter")
        && !tableRow.hasClassName("active-scaffold-calculations")) {

        if (even) {
          tableRow.addClassName("even-record");
        } else {
          tableRow.removeClassName("even-record");
        }
        even = !even;
      }
    }
  },
  toggleEmptyMessage: function(tableBody, emptyMessageElement) {
    // Check to see if this was the last element in the list
    if ($(tableBody).rows.length == 0) {
      $(emptyMessageElement).show();
    } else {
      $(emptyMessageElement).hide();
    }
  },
  removeSortClasses: function(scaffold_id) {
    $$('#' + scaffold_id + ' td.sorted').each(function(element) {
      element.removeClassName("sorted");
    });
    $$('#' + scaffold_id + ' th.sorted').each(function(element) {
      element.removeClassName("sorted");
      element.removeClassName("asc");
      element.removeClassName("desc");
    });
  },
  decrement_record_count: function(scaffold_id) {
    count = $$('#' + scaffold_id + ' span.active-scaffold-records').first();
    count.innerHTML = parseInt(count.innerHTML) - 1;
  },
  increment_record_count: function(scaffold_id) {
    count = $$('#' + scaffold_id + ' span.active-scaffold-records').first();
    count.innerHTML = parseInt(count.innerHTML) + 1;
  },

  server_error_response: '',
  report_500_response: function(active_scaffold_id) {
    messages_container = $(active_scaffold_id).down('td.messages-container');
    new Insertion.Top(messages_container, this.server_error_response);
  }
}

/*
 * Add-ons/Patches to Prototype
 */

/* patch to support replacing TR/TD/TBODY in Internet Explorer, courtesy of http://dev.rubyonrails.org/ticket/4273 */
Element.replace = function(element, html) {
  element = $(element);
  if (element.outerHTML) {
    try {
    element.outerHTML = html.stripScripts();
    } catch (e) {
      var tn = element.tagName;
      if(tn=='TBODY' || tn=='TR' || tn=='TD')
      {
              var tempDiv = document.createElement("div");
              tempDiv.innerHTML = '
' + html.stripScripts() + '
';
              element.parentNode.replaceChild(tempDiv.getElementsByTagName(tn).item(0), element);
      }
      else throw e;
    }
  } else {
    var range = element.ownerDocument.createRange();
    /* patch to fix <form> replaces in Firefox. see http://dev.rubyonrails.org/ticket/8010 */
    range.selectNodeContents(element.parentNode);
    element.parentNode.replaceChild(range.createContextualFragment(html.stripScripts()), element);
  }
  setTimeout(function() {html.evalScripts()}, 10);
  return element;
};

/*
 * URL modification support. Incomplete functionality.
 */
Object.extend(String.prototype, {
  append_params: function(params) {
    url = this;
    if (url.indexOf('?') == -1) url += '?';
    else if (url.lastIndexOf('&') != url.length) url += '&';

    url += $H(params).collect(function(item) {
      return item.key + '=' + item.value;
    }).join('&');

    return url;
  }
});

/**
 * A set of links. As a set, they can be controlled such that only one is "open" at a time, etc.
 */
ActiveScaffold.Actions = new Object();
ActiveScaffold.Actions.Abstract = function(){}
ActiveScaffold.Actions.Abstract.prototype = {
  initialize: function(links, target, loading_indicator, options) {
    this.target = $(target);
    this.loading_indicator = $(loading_indicator);
    this.options = options;
    this.links = links.collect(function(link) {
      return this.instantiate_link(link);
    }.bind(this));
  },

  instantiate_link: function(link) {
    throw 'unimplemented'
  }
}

/**
 * A DataStructures::ActionLink, represented in JavaScript.
 * Concerned with AJAX-enabling a link and adapting the result for insertion into the table.
 */
ActiveScaffold.ActionLink = new Object();
ActiveScaffold.ActionLink.Abstract = function(){}
ActiveScaffold.ActionLink.Abstract.prototype = {
  initialize: function(a, target, loading_indicator) {
    this.tag = $(a);
    this.url = this.tag.href;
    this.target = target;
    this.loading_indicator = loading_indicator;
    this.hide_target = false;
    this.position = this.tag.getAttribute('position');

    this.onclick = this.tag.onclick;
    this.tag.onclick = null;
    this.tag.observe('click', function(event) {
      this.open();
      Event.stop(event);
    }.bind(this));

    this.tag.action_link = this;
  },

  open: function() {
    if (this.is_disabled()) return;

		if (this.tag.hasAttribute( "dhtml_confirm")) {
			if (this.onclick) this.onclick();
			return;
		} else {
			if (this.onclick && !this.onclick()) return;//e.g. confirmation messages
			this.open_action();
		}
  },

	open_action: function() {
		if (this.position) this.disable();
    this.loading_indicator.style.visibility = 'visible';
    new Ajax.Request(this.url, {
      asynchronous: true,
      evalScripts: true,

      onSuccess: function(request) {
        if (this.position) {
          this.insert(request.responseText);
          if (this.hide_target) this.target.hide();
        } else {
          request.evalResponse();
        }
      }.bind(this),

      onFailure: function(request) {
        ActiveScaffold.report_500_response(this.scaffold_id());
        if (this.position) this.enable()
      }.bind(this),

      onComplete: function(request) {
        this.loading_indicator.style.visibility = 'hidden';
      }.bind(this)
		});
	},

  insert: function(content) {
    throw 'unimplemented'
  },

  close: function() {
    this.enable();
    this.adapter.remove();
    if (this.hide_target) this.target.show();
  },

  reload: function() {
    this.close();
    this.open();
  },

  get_new_adapter_id: function() {
    var id = 'adapter_';
    var i = 0;
    while ($(id + i)) i++;
    return id + i;
  },

  enable: function() {
    return this.tag.removeClassName('disabled');
  },

  disable: function() {
    return this.tag.addClassName('disabled');
  },

  is_disabled: function() {
    return this.tag.hasClassName('disabled');
  },

  scaffold_id: function() {
    return this.tag.up('div.active-scaffold').id;
  }
}

/**
 * Concrete classes for record actions
 */
ActiveScaffold.Actions.Record = Class.create();
ActiveScaffold.Actions.Record.prototype = Object.extend(new ActiveScaffold.Actions.Abstract(), {
  instantiate_link: function(link) {
    var l = new ActiveScaffold.ActionLink.Record(link, this.target, this.loading_indicator);
    l.refresh_url = this.options.refresh_url;
    if (l.position) l.url = l.url.append_params({adapter: '_list_inline_adapter'});
    l.set = this;
    return l;
  }
});

ActiveScaffold.ActionLink.Record = Class.create();
ActiveScaffold.ActionLink.Record.prototype = Object.extend(new ActiveScaffold.ActionLink.Abstract(), {
  insert: function(content) {
    this.set.links.each(function(item) {
      if (item.url != this.url && item.is_disabled() && item.adapter) item.close();
    }.bind(this));

    if (this.position == 'replace') {
      this.position = 'after';
      this.hide_target = true;
    }

    if (this.position == 'after') {
      new Insertion.After(this.target, content);
      this.adapter = this.target.next();
    }
    else if (this.position == 'before') {
      new Insertion.Before(this.target, content);
      this.adapter = this.target.previous();
    }
    else {
      return false;
    }

    var closer = function(event) {
      this.close_with_refresh();
      if (event) Event.stop(event);
    }.bind(this);
    var self = this;

    this.adapter.down('a.inline-adapter-close').observe('click', closer);
    // anything in the insert with a class of cancel gets the closer method, and a reference to this object for good measure
    this.adapter.getElementsByClassName('cancel').each(function(elem) {
      elem.observe('click', closer);
      elem.link = self;
    })

    new Effect.Highlight(this.adapter.down('td'));
  },

  close_with_refresh: function() {
    new Ajax.Request(this.refresh_url, {
      asynchronous: true,
      evalScripts: true,

      onSuccess: function(request) {
        Element.replace(this.target, request.responseText);
        var new_target = $(this.target.id);
        if (this.target.hasClassName('even')) new_target.addClassName('even');
        this.target = new_target;
        this.close();
      }.bind(this),

      onFailure: function(request) {
        ActiveScaffold.report_500_response(this.scaffold_id());
      }
    });
  },

  enable: function() {
    this.set.links.each(function(item) {
      if (item.url != this.url) return;
      item.tag.removeClassName('disabled');
    }.bind(this));
  },

  disable: function() {
    this.set.links.each(function(item) {
      if (item.url != this.url) return;
      item.tag.addClassName('disabled');
    }.bind(this));
  }
});

/**
 * Concrete classes for table actions
 */
ActiveScaffold.Actions.Table = Class.create();
ActiveScaffold.Actions.Table.prototype = Object.extend(new ActiveScaffold.Actions.Abstract(), {
  instantiate_link: function(link) {
    var l = new ActiveScaffold.ActionLink.Table(link, this.target, this.loading_indicator);
    if (l.position) l.url = l.url.append_params({adapter: '_list_inline_adapter'});
    return l;
  }
});

ActiveScaffold.ActionLink.Table = Class.create();
ActiveScaffold.ActionLink.Table.prototype = Object.extend(new ActiveScaffold.ActionLink.Abstract(), {
  insert: function(content) {
    if (this.position == 'top') {
      new Insertion.Top(this.target, content);
      this.adapter = this.target.immediateDescendants().first();
    }
    else {
      throw 'Unknown position "' + this.position + '"'
    }

    var closer = function(event) {
      this.close();
      if (event) Event.stop(event);
    }.bind(this);
    var self = this;

    this.adapter.down('a.inline-adapter-close').observe('click', closer);
    // anything in the insert with a class of cancel gets the closer method, and a reference to this object for good measure
    this.adapter.getElementsByClassName('cancel').each(function(elem) {
      elem.observe('click', closer);
      elem.link = self;
    })

    new Effect.Highlight(this.adapter.down('td'));
  }
});<|endoftext|>


<commit_before>describe('ui.router: \'help\'', function() {

  var state;

  // excuted before each "it" is run
  beforeEach(function() {

    // load the module
    module('help');

    // inject dependencies
    inject(function($state) {
      state = $state;
    });

  });

  describe("States Map", function() {

    it("$state should be defined", function() {
      expect(state).toBeDefined();
    });

    describe("help state", function() {

      var config;

      it("should be defined", function() {
        // arrange
        config = state.get('help');

        // assertions
        expect(config).toBeDefined();
      });

      it("should map url to \'/help\'", function() {
        expect(config.url).toEqual('/help');
      });

      describe("views", function() {

        var views;

        it("should be defined", function() {
          // arrange
          views = config.views;

          // assertions
          expect(views).toBeDefined();
        });

        describe("master", function() {

          var master;

          it("should be defined", function() {
            // arrange
            /*jshint sub:true*/
            master = views['master'];

            // assertions
            expect(master).toBeDefined();
          });

          it("should map to templateUrl \'app/main/templates/layout.html\'", function() {
            expect(master.templateUrl).toEqual('app/main/templates/layout.html');
          });

        });

        describe("content", function() {

          var content;

          it("should be defined", function() {
            // arrange
            /*jshint sub:true*/
            content = views['content@help'];

            // assertions
            expect(content).toBeDefined();
          });

          it("should map to templateUrl \'app/help/template.html\'", function() {
            expect(content.templateUrl).toEqual('app/help/template.html');
          });

          it("should map to controller HelpCtrl", function() {
            expect(content.controller).toEqual('HelpCtrl');
          });

          it("should map to controllerAs vm", function() {
            expect(content.controllerAs).toEqual('vm');
          });

        });

      });

    });

  }); //--- end: States Map

});
<commit_msg>help routes.spec.js updated #25<commit_after>describe('ui.router: \'help\'', function() {

  var state;

  // excuted before each "it" is run
  beforeEach(function() {

    // load the module
    module('help');

    // inject dependencies
    inject(function($state) {
      state = $state;
    });

  });

  describe("States Map", function() {

    it("$state should be defined", function() {
      expect(state).toBeDefined();
    });

    describe("help state", function() {

      var config;

      it("should be defined", function() {
        // arrange
        config = state.get('help');

        // assertions
        expect(config).toBeDefined();
      });

      it("should map url to \'/help\'", function() {
        expect(config.url).toEqual('/help');
      });

      describe("views", function() {

        var views;

        it("should be defined", function() {
          // arrange
          views = config.views;

          // assertions
          expect(views).toBeDefined();
        });

        describe("master", function() {

          var master;

          it("should be defined", function() {
            // arrange
            /*jshint sub:true*/
            master = views['master'];

            // assertions
            expect(master).toBeDefined();
          });

          it("should map to templateUrl \'app/main/templates/layout.html\'", function() {
            expect(master.templateUrl).toEqual('app/main/templates/layout.html');
          });

        });

        describe("content", function() {

          var content;

          it("should be defined", function() {
            // arrange
            /*jshint sub:true*/
            content = views['content@help'];

            // assertions
            expect(content).toBeDefined();
          });

          it("should map to templateUrl \'app/help/template.html\'", function() {
            expect(content.templateUrl).toEqual('app/help/template.html');
          });

          it("should map to controller \'HelpCtrl\'", function() {
            expect(content.controller).toEqual('HelpCtrl');
          });

          it("should map to controllerAs \'vm\'", function() {
            expect(content.controllerAs).toEqual('vm');
          });

        });

      });

    });

  }); //--- end: States Map

});
<|endoftext|>


<commit_before>/** unauthenticated.js
* @file: /src/app/routes/ajax/unauthenticated.js
* @description: Handles express routing for the unathenticated ajax routes
* @parameters: Object(app), Object(resources)
* @returns: Express routes
*/

var ObjectID = require('mongodb').ObjectID
var striptags = require('striptags')
var extend = require('util')._extend;

module.exports = function (app, resources) {
  /** /ajax/map
  * @type: GET
  * @description: Ajax route for loading page info to a map.
  * @parameters: {
      Object(filter): {
        id: String("568207facddd6a6a0808c770"),
        type: String("5")
      }
  * }
  */
  app.get('/ajax/map', function (req, res) {
    var filter = {
      accepted: true, //Only published pages
      $or: [
        { type: '3' },
        { type: '5' },
        { type: '6' }
      ]
    }

    if(req.query.filter !== undefined) { //A filter is used
      var filters = {
        id: function(id) {
          return {
            _id: new ObjectID(id)
          }
        },
        type: function(type) {
          return {
            type: String(type)
          }
        },
        ids: function(ids) {
          var objIds = ids.map(function(id) {
            return new ObjectID(id)
          })

          return {
            _id: {
              $in: objIds
            }
          }
        },
        ignore: function(id) {
          return {
            _id: {
              $ne: new ObjectID(id)
            }
          }
        },
        city: function(city) {
          return {
            "post.city": String(city).toLowerCase()
          }
        }
      }

      for(var key in req.query.filter) {
        if(key in filters) {
          filter = filters[key](req.query.filter[key])
        }
      }
    }

    var pagesdb = resources.collections.pages

    pagesdb.find(filter).toArray(function(err, pages) {
      pages = pages.filter(function(page) {
        if('post' in page) {
          if('content' in page.post) {
            page.post.content = striptags(page.post.content, ['br'])
            return page
          }
        }
      })

      res.json(pages)
    })
  })

  /** /ajax/search
  *
  */
  app.get('/ajax/search', function (req, res) {
    var searchString = req.query.s
    var searchArray = searchString.toLowerCase().split(' ')

    var valid = false
    for (var i = searchArray.length - 1; i >= 0; i--) {
      if(searchArray[i].match(/\w/gi) !== null ) {
        valid = true
      }
    }

    //No valid word characters, quit early
    if(!valid) {
      res.json([])
      return
    }

    //The default query object
    var query = {}

    //General query object
    var filteredQuery = {
      $match: {
        accepted: true //Only published pages
      }
    }

    //The default fields we search
    var searchFields = ['url','type','slug','title','post.content','post.food','post.product_type','post.city']

    var searchWeights = {
      'post.veg_type': 30, //This is very high, since it can only match 'vegan', 'lacto_ovo' and 'animal'
      'type': 25,
      'title': 25,
      'post.product_type': 18,
      'slug': 15,
      'post.content': 15,
      'post.city': 15,
      'post.food': 10,
      'post.veg_offer': 1
    }

    //Check if query matches a type
    var queryType = false

    var queryOperations = {
      'butik': function() {
        query['type'] = '5'
        return true
      },
      'produkt': function() {
        query['type'] = '4'
        return true
      },
      'restaurang': function() {
        query['type'] = '3'
        return true
      },
      'recept': function() {
        query['type'] = '2'
        return true
      },
      'fakta': function() {
        query['type'] = '1'
        return true
      },
      'vegan': function() {
        if('type' in query && query.type === '3') {
          query['veg_type'] = 'vegan'
          return true
        } else {
          searchFields.push({
            property: 'post.veg_type',
            value: 'vegan'
          })
          return false
        }
      },
      'laktoovo': function() {
        if('type' in query && query.type === '3') {
          query['veg_type'] = 'lacto_ovo'
          return true
        } else {
          searchFields.push({
            property: 'post.veg_type',
            value: 'lacto_ovo'
          })
          return false
        }
      },
      'animal': function() {
        if('type' in query && query.type === '3') {
          query['veg_type'] = 'animal'
          return true
        } else {
          searchFields.push({
            property: 'post.veg_type',
            value: 'animal'
          })
          return false
        }
      }
    }

    var queryTypes = function(string) {
      string = string.toLowerCase().replace(/é|è/gi, 'e') //Remove accent, mainly for café/kafé
      string = string.replace(/[^a-z]/gi, '') //Remove non alphabet characters

      var keywords = ['butik','restaurang','produkt','recept','fakta','vegan','laktoovo','animal']
      var regexMatches = new RegExp('^' + keywords.join('|'))

      var match = string.match(regexMatches)
      if(match !== null && match.length > 0) {
        var key = match[0]

        if(key in queryOperations) {
          //Perform the operations on the search query
          if(queryOperations[key]()) {
            //Update the search string to remove the key from the search, otherwise might result in unwanted results
            searchString = searchString.toLowerCase().replace(/é|è/gi, 'e') //Remove accent, mainly for café/kafé
            var tmpArray = searchString.toLowerCase().split(' ')

            //Filter out the current key from the search query
            searchString = tmpArray.filter(function(text) {
              var isKey = text.match(key)
              if(isKey === null) {
                return text
              }
            }).join(' ').trim()
          }
        }
      }
    }

    for (var i in searchArray) {
      queryTypes(searchArray[i])
    }

    //Remove extraneous spaces
    searchString = searchString.replace(/\s+/g, " ").replace(/^\s|\s$/g, "")
    console.log(searchString)

    //Go over all the keys
    for(var key in query) {
      if( ! (key in filteredQuery.$match) ) {
        filteredQuery.$match[key] = query[key]
      }
    }

    //Array to hold regex objects of search strings
    var regexArray = []

    //As the searchString has been manipulated it might not even be anything at all anymore
    if(searchString.length > 0) {
      regexArray = searchString.split(' ').map(function(text) {
        return new RegExp(text, 'gi')
      })

      var orFields = []
      for (var i = searchFields.length - 1; i >= 0; i--) {
        var obj = {}

        if(typeof searchFields[i] === 'object') {
          if('property' in searchFields[i] && 'value' in searchFields[i]) {
            obj[searchFields[i].property] = {
              $in: [searchFields[i].value]
            }
          }
        } else {
          obj[searchFields[i]] = {
            $in: regexArray
          }
        }

        orFields.push(obj)
      }

      //Add $or matches with the regex strings
      filteredQuery.$match.$or = orFields
    }

    //Check if query matches a city
    var citiesdb = resources.collections.cities
    citiesdb.find({
      name: {
        $in: searchArray
      }
    }).toArray(function(err, cities) {
      if(err) throw err

      if(cities.length > 0) {
        filteredQuery.$match['post.city'] = {
          '$regex': cities[0].name, //Search only this city
          '$options': '-i'
        }

      }

      /*
      //Check if query matches a category
      var categoriesdb = resources.collections.categories
      categoriesdb.find({
        $or: [{
          name: {
            $in: searchArray
          }
        }, {
          subcategory: {
            $in: searchArray
          }
        }]
      }).toArray(function(err, categories) {
        //console.log(categories)
        //If we found a match here, we might want to restrict the query to that type of category
      })
      */

      //Find pages
      var pagesdb = resources.collections.pages
      pagesdb.aggregate([filteredQuery], function (err, pages) {
        if(err) throw err

        var weightedResult = [] //To hold the page results with their newly calculated weights

        for (var i = 0; i < pages.length; i++) {
          //Look up weights for each field and reorder array of results accordingly

          //The order is important, not just the weights.
          //If a match is made later that value can not match against another key in searchWeights, choose carefully

          //Calculate the weight of each result
          var totalWeight = 0
          var matchedValues = []
          for(var key in searchWeights) {
            var weight = searchWeights[key]
            var value = ''

            //TODO This should really be replaced with a loop that supports deeper structures, will do for now though considering our weights are only 2 levels max
            if(key.indexOf('.') >= 0) {
              subKey = key.substr( key.indexOf('.') +1 )
              parentKey = key.substr(0, key.indexOf('.') )
              if(parentKey in pages[i]) {
                if(subKey in pages[i][parentKey]) {
                    value = pages[i][parentKey][subKey]
                }
              }
            } else {
              if(key in pages[i]) {
                value = pages[i][key]
              }
            }

            if(value) { //If value was found, check which fields it matched in the db query
              var matches = 0 //No matches = no points

              //Count the matched regex cases against the search query string
              for(var index in regexArray) {
                var regex = regexArray[index]
                var match = value.match(regex)
                if(match !== null && match.length > 0) {
                  var matchedValue = match[0].toLowerCase()
                  if(matchedValues.indexOf(matchedValue) === -1) {
                    var points = 1

                    //If this was an "exact" case insensitive match, half a bonus point is given
                    var exactRegex = new RegExp('^'+matchedValue+'$', 'i')
                    var exactMatch = value.match(exactRegex)

                    if(exactMatch !== null) {
                      points += .5
                    }

                    matches += points
                    matchedValues.push(matchedValue)
                  }
                }
              }

              totalWeight += Math.abs(matches * weight) //Add the new weight to the totalWeight
            }
          }

          //Give bonus points if several matches were made
          if(matchedValues.length > 1) {
            totalWeight += (totalWeight * Math.floor(matchedValues.length * .5) )
          }

          if('post' in pages[i]) {
            if('content' in pages[i].post) {
              //All pages should have content, if not, then something is wrong with document in db, anyway make sure this is not cause of failure
              //Strip html tags of content
              pages[i].post.content = striptags(pages[i].post.content, ['br','p','a','span','i','b'])

              //Add the page with its new weight
              weightedResult.push({
                page: pages[i],
                weight: totalWeight,
                keywords: matchedValues
              })
            }
          }
        }

        //Sort the weighted results based on the weight value
        weightedResult.sort(function(a,b) {
          if (a.weight < b.weight)
            return 1
          else if (a.weight > b.weight)
            return -1
          else
            return 0
        })

        //Get the final result array of just the pages
        var finalResult = weightedResult.map(function(obj) {
          //console.log(obj.page.title, obj.weight) //This is really useful info for debugging search result weights
          return obj.page
        })

        res.json(finalResult)
      })
    })
  })

  /** /ajax/imageInfo
  *
  */
  app.get('/ajax/imageInfo', function (req, res) {
    if(req.query.id != undefined) {
      var imagesdb = resources.collections.images
      imagesdb.find({ _id : new ObjectID(req.query.id) }).toArray(function (err, doc) {
        res.json(doc)
      })
    }
  })
}<commit_msg>Adjustments to cafe search<commit_after>/** unauthenticated.js
* @file: /src/app/routes/ajax/unauthenticated.js
* @description: Handles express routing for the unathenticated ajax routes
* @parameters: Object(app), Object(resources)
* @returns: Express routes
*/

var ObjectID = require('mongodb').ObjectID
var striptags = require('striptags')
var extend = require('util')._extend;

module.exports = function (app, resources) {
  var functions = resources.functions

  /** /ajax/map
  * @type: GET
  * @description: Ajax route for loading page info to a map.
  * @parameters: {
      Object(filter): {
        id: String("568207facddd6a6a0808c770"),
        type: String("5")
      }
  * }
  */
  app.get('/ajax/map', function (req, res) {
    var filter = {
      accepted: true, //Only published pages
      $or: [
        { type: '3' },
        { type: '5' },
        { type: '6' }
      ]
    }

    if(req.query.filter !== undefined) { //A filter is used
      var filters = {
        id: function(id) {
          return {
            _id: new ObjectID(id)
          }
        },
        type: function(type) {
          return {
            type: String(type)
          }
        },
        ids: function(ids) {
          var objIds = ids.map(function(id) {
            return new ObjectID(id)
          })

          return {
            _id: {
              $in: objIds
            }
          }
        },
        ignore: function(id) {
          return {
            _id: {
              $ne: new ObjectID(id)
            }
          }
        },
        city: function(city) {
          return {
            "post.city": String(city).toLowerCase()
          }
        }
      }

      for(var key in req.query.filter) {
        if(key in filters) {
          filter = filters[key](req.query.filter[key])
        }
      }
    }

    var pagesdb = resources.collections.pages

    pagesdb.find(filter).toArray(function(err, pages) {
      pages = pages.filter(function(page) {
        if('post' in page) {
          if('content' in page.post) {
            page.post.content = striptags(page.post.content, ['br'])
            return page
          }
        }
      })

      res.json(pages)
    })
  })

  /** /ajax/search
  *
  */
  app.get('/ajax/search', function (req, res) {
    var searchString = req.query.s
    var searchArray = searchString.toLowerCase().split(' ')

    var valid = false
    for (var i = searchArray.length - 1; i >= 0; i--) {
      if(searchArray[i].match(/\w/gi) !== null ) {
        valid = true
      }
    }

    //No valid word characters, quit early
    if(!valid) {
      res.json([])
      return
    }

    //The default query object
    var query = {}

    //General query object
    var filteredQuery = {
      $match: {
        accepted: true //Only published pages
      }
    }

    //The default fields we search
    var searchFields = ['url','type','slug','title','post.content','post.food','post.product_type','post.city']

    var searchWeights = {
      'post.veg_type': 30, //This is very high, since it can only match 'vegan', 'lacto_ovo' and 'animal'
      'type': 25,
      'title': 25,
      'post.product_type': 18,
      'slug': 15,
      'post.content': 15,
      'post.city': 15,
      'post.food': 10,
    }

    //Check if query matches a type
    var queryType = false

    var queryOperations = {
      'kafe': function() {
        searchWeights.slug = 30
        searchFields.push({
          property: 'slug',
          value: 'kafe'
        })

        searchString += ' cafe'

        return false
      },
      'cafe': function() {
        searchWeights.slug = 30
        searchFields.push({
          property: 'slug',
          value: 'cafe'
        })

        searchString += ' kafe'

        return false
      },
      'butik': function() {
        query['type'] = '5'
        return true
      },
      'produkt': function() {
        query['type'] = '4'
        return true
      },
      'restaurang': function() {
        query['type'] = '3'
        return true
      },
      'recept': function() {
        query['type'] = '2'
        return true
      },
      'fakta': function() {
        query['type'] = '1'
        return true
      },
      'vegan': function() {
        if('type' in query && query.type === '4') {
          query['veg_type'] = 'vegan'
          return true
        } else {
          searchFields.push({
            property: 'post.veg_type',
            value: 'vegan'
          })
          return false
        }
      },
      'laktoovo': function() {
        if('type' in query && query.type === '4') {
          query['veg_type'] = 'lacto_ovo'
          return true
        } else {
          searchFields.push({
            property: 'post.veg_type',
            value: 'lacto_ovo'
          })
          return false
        }
      },
      'animal': function() {
        if('type' in query && query.type === '4') {
          query['veg_type'] = 'animal'
          return true
        } else {
          searchFields.push({
            property: 'post.veg_type',
            value: 'animal'
          })
          return false
        }
      }
    }

    var queryTypes = function(string) {
      string = functions.replaceDiacritics(string)
      string = string.replace(/[^a-z]/gi, '') //Remove non alphabet characters

      var keywords = ['cafe','kafe','butik','restaurang','produkt','recept','fakta','vegan','laktoovo','animal']
      var regexMatches = new RegExp('^' + keywords.join('|'))

      var match = string.match(regexMatches)
      if(match !== null && match.length > 0) {
        var key = match[0]

        if(key in queryOperations) {
          //Perform the operations on the search query
          if(queryOperations[key]()) {
            //Update the search string to remove the key from the search, otherwise might result in unwanted results
            var tmpArray = functions.replaceDiacritics(searchString).split(' ')

            //Filter out the current key from the search query
            searchString = tmpArray.filter(function(text) {
              var isKey = text.match(key)
              if(isKey === null) {
                return text
              }
            }).join(' ').trim()
          }
        }
      }
    }

    for (var i in searchArray) {
      queryTypes(searchArray[i])
    }

    //Remove extraneous spaces
    searchString = searchString.replace(/\s+/g, " ").replace(/^\s|\s$/g, "")

    //Go over all the keys
    for(var key in query) {
      if( ! (key in filteredQuery.$match) ) {
        filteredQuery.$match[key] = query[key]
      }
    }

    //Array to hold regex objects of search strings
    var regexArray = []

    //As the searchString has been manipulated it might not even be anything at all anymore
    if(searchString.length > 0) {
      regexArray = searchString.split(' ').map(function(text) {
        return new RegExp(text, 'gi')
      })

      var orFields = []
      for (var i = searchFields.length - 1; i >= 0; i--) {
        var obj = {}

        if(typeof searchFields[i] === 'object') {
          if('property' in searchFields[i] && 'value' in searchFields[i]) {
            obj[searchFields[i].property] = {
              $in: [searchFields[i].value]
            }
          }
        } else {
          obj[searchFields[i]] = {
            $in: regexArray
          }
        }

        orFields.push(obj)
      }

      //Add $or matches with the regex strings
      filteredQuery.$match.$or = orFields
    }

    //Check if query matches a city
    var citiesdb = resources.collections.cities
    citiesdb.find({
      name: {
        $in: searchArray
      }
    }).toArray(function(err, cities) {
      if(err) throw err

      if(cities.length > 0) {
        filteredQuery.$match['post.city'] = {
          '$regex': cities[0].name, //Search only this city
          '$options': '-i'
        }

      }

      /*
      //Check if query matches a category
      var categoriesdb = resources.collections.categories
      categoriesdb.find({
        $or: [{
          name: {
            $in: searchArray
          }
        }, {
          subcategory: {
            $in: searchArray
          }
        }]
      }).toArray(function(err, categories) {
        //console.log(categories)
        //If we found a match here, we might want to restrict the query to that type of category
      })
      */

      //Find pages
      var pagesdb = resources.collections.pages
      pagesdb.aggregate([filteredQuery], function (err, pages) {
        if(err) throw err

        var weightedResult = [] //To hold the page results with their newly calculated weights

        for (var i = 0; i < pages.length; i++) {
          //Look up weights for each field and reorder array of results accordingly

          //The order is important, not just the weights.
          //If a match is made later that value can not match against another key in searchWeights, choose carefully

          //Calculate the weight of each result
          var totalWeight = 0
          var matchedValues = []
          for(var key in searchWeights) {
            var weight = searchWeights[key]
            var value = ''

            //TODO This should really be replaced with a loop that supports deeper structures, will do for now though considering our weights are only 2 levels max
            if(key.indexOf('.') >= 0) {
              subKey = key.substr( key.indexOf('.') +1 )
              parentKey = key.substr(0, key.indexOf('.') )
              if(parentKey in pages[i]) {
                if(subKey in pages[i][parentKey]) {
                    value = pages[i][parentKey][subKey]
                }
              }
            } else {
              if(key in pages[i]) {
                value = pages[i][key]
              }
            }

            if(value) { //If value was found, check which fields it matched in the db query
              var matches = 0 //No matches = no points

              //Count the matched regex cases against the search query string
              for(var index in regexArray) {
                var regex = regexArray[index]
                var match = value.match(regex)
                if(match !== null && match.length > 0) {
                  var matchedValue = match[0].toLowerCase()
                  if(matchedValues.indexOf(matchedValue) === -1) {
                    var points = 1

                    //If this was an "exact" case insensitive match, half a bonus point is given
                    var exactRegex = new RegExp('^'+matchedValue+'$', 'i')
                    var exactMatch = value.match(exactRegex)

                    if(exactMatch !== null) {
                      points += .5
                    }

                    matches += points
                    matchedValues.push(matchedValue)
                  }
                }
              }

              totalWeight += Math.abs(matches * weight) //Add the new weight to the totalWeight
            }
          }

          //Give bonus points if several matches were made
          if(matchedValues.length > 1) {
            totalWeight += (totalWeight * Math.floor(matchedValues.length * .5) )
          }

          if('post' in pages[i]) {
            if('content' in pages[i].post) {
              //All pages should have content, if not, then something is wrong with document in db, anyway make sure this is not cause of failure
              //Strip html tags of content
              pages[i].post.content = striptags(pages[i].post.content, ['br','p','a','span','i','b'])

              //Add the page with its new weight
              weightedResult.push({
                page: pages[i],
                weight: totalWeight,
                keywords: matchedValues
              })
            }
          }
        }

        //Sort the weighted results based on the weight value
        weightedResult.sort(function(a,b) {
          if (a.weight < b.weight)
            return 1
          else if (a.weight > b.weight)
            return -1
          else
            return 0
        })

        //Get the final result array of just the pages
        var finalResult = weightedResult.map(function(obj) {
          //console.log(obj.page.title, obj.weight) //This is really useful info for debugging search result weights
          return obj.page
        })

        res.json(finalResult)
      })
    })
  })

  /** /ajax/imageInfo
  *
  */
  app.get('/ajax/imageInfo', function (req, res) {
    if(req.query.id != undefined) {
      var imagesdb = resources.collections.images
      imagesdb.find({ _id : new ObjectID(req.query.id) }).toArray(function (err, doc) {
        res.json(doc)
      })
    }
  })
}<|endoftext|>


<commit_before>/*
 * Copyright 2012 Amadeus s.a.s.
 * 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.
 */

/**
 * Beans used for the data model of the list module controller.
 * @class aria.widgets.form.list.CfgBeans
 */
Aria.beanDefinitions({
    $package : "aria.widgets.form.list.CfgBeans",
    $description : "Beans used for the data model of the list module controller.",
    $namespaces : {
        "json" : "aria.core.JsonTypes"
    },
    $beans : {
        "ListModel" : {
            $type : "json:Object",
            $description : "Root of the list data model.",
            $properties : {
                "items" : {
                    $type : "ItemsArray"
                },
                "itemsView" : {
                    $type : "json:ObjectRef",
                    $description : "View on the items, with the correct order to display them.",
                    $classpath : "aria.templates.View"
                },
                "activateSort" : {
                    $type : "json:Boolean",
                    $description : "Whether sorting is enabled."
                },
                "multipleSelect" : {
                    $type : "json:Boolean",
                    $description : "Specify whether it is possible to select multiple items at the same time (true) or at most one (false)."
                },
                "maxSelectedCount" : {
                    $type : "json:Integer",
                    $minValue : 1,
                    $description : "Maximum number of items that can be selected at the same time. It is only taken into account if multipleSelect is true. A null value means there is no maximum."
                },
                "disabled" : {
                    $type : "json:Boolean",
                    $description : "Specify whether the list is disabled (true) or enabled (false)."
                },
                "selectedIndex" : {
                    $type : "json:Integer",
                    $minValue : -1,
                    $description : "Index of the selected element in the list, if only one is selected. -1 means no element is selected. A null value means several elements are selected."
                },
                "selectedCount" : {
                    $type : "json:Integer",
                    $description : "Number of selected items.",
                    $minValue : 0
                },
                "displayOptions" : {
                    $type : "displayOptions"
                },
                "cfg" : {
                    $type : "json:ObjectRef",
                    $description : "Configuration which should be used for the div surrounding the widget."
                },
                "skin" : {
                    $type : "json:ObjectRef",
                    $description : "Skin properties."
                },
                "numberOfColumns" : {
                    $type : "numberOfColumns",
                    $description : "How many Rows the multi-select should contain"
                },
                "numberOfRows" : {
                    $type : "numberOfRows",
                    $description : "How many Columns the multi-select should contain"
                },
                /*
                 * TODO remove? "numItemsLastCol" : { $type : "numberOfRows", $description : "How many Columns the
                 * multi-select should contain" },
                 */
                "focusIndex" : {
                    $type : "json:Integer",
                    $description : "The index of the list item with focus",
                    $default : 0
                },
                "preselect" : {
                    $type : "json:String",
                    $description : "strict: for strict highlighting (exact match only), always: for selecting the first item everytime, none: for no highlighting",
                    $default : "none"
                }
            }
        },
        "numberOfColumns" : {
            $type : "json:Integer",
            $description : "How many columns are to be used in the display of the list",
            $default : 0,
            $minValue : 0
        },
        "numberOfRows" : {
            $type : "json:Integer",
            $description : "How many columns are to be used in the display of the list",
            $default : 0,
            $minValue : 0
        },
        "displayOptions" : {
            $type : "json:Object",
            $description : "Display options that are not interpreted by the list controller.",
            $default : {
                "flowOrientation" : 'vertical',
                "tableMode" : false
            },
            $properties : {
                "flowOrientation" : {
                    $type : "json:Enum",
                    $description : "The Orientation of a list whether items are listed horizontally or vertically first. Display options may not be supported by all list templates.",
                    $enumValues : ["horizontal", "vertical"],
                    $default : 'vertical'
                },
                "tableMode" : {
                    $type : "json:Boolean",
                    $description : "Specifies whether the list uses Table Mode which is when the values of a list are split on a seperator and list resulting values are put 1 on each row",
                    $default : false
                },
                "listDisplay" : {
                    $type : "json:Enum",
                    $description : "Whether to display code, value or both on the label next to the checkbox.",
                    $enumValues : ["code", "label", "both"],
                    $default : 'code'
                },
                "displayFooter" : {
                    $type : "json:Boolean",
                    $description : "Used to display the select all, deselect all, and close links.",
                    $default : false
                }
            }
        },
        "ItemsArray" : {
            $type : "json:Array",
            $description : "Array of items.",
            $contentType : {
                $type : "Item"
            }
        },
        "Item" : {
            $type : "json:Object",
            $description : "Structure describing an item in the list",
            $properties : {
                "label" : {
                    $type : "json:String",
                    $description : "Text to display to the user - must be translated in user language."
                },
                "value" : {
                    $type : "json:String",
                    $description : "Internal value associated to the item - usually a language-independent code."
                },
                "currentlyDisabled" : {
                    $type : "json:Boolean",
                    $description : "Whether the item is disabled (true) or enabled (false), taking into account whether the maximum number of selected items has been reached or not."
                },
                "initiallyDisabled" : {
                    $type : "json:Boolean",
                    $description : "Whether the item is disabled (true) or enabled (false), independently of whether the maximum number of selected items has been reached or not."
                },
                "object" : {
                    $type : "json:ObjectRef",
                    $description : "Reference to the object storing extra information about the item."
                },
                "selected" : {
                    $type : "json:Boolean",
                    $description : "Specifies whether the item is selected (true) or not (false)."
                },
                "index" : {
                    $type : "json:Integer",
                    $description : "Index in the items array."
                }
            }
        }
    }
});
<commit_msg>fixes #103.<commit_after>/*
 * Copyright 2012 Amadeus s.a.s.
 * 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.
 */

/**
 * Beans used for the data model of the list module controller.
 * @class aria.widgets.form.list.CfgBeans
 */
Aria.beanDefinitions({
    $package : "aria.widgets.form.list.CfgBeans",
    $description : "Beans used for the data model of the list module controller.",
    $namespaces : {
        "json" : "aria.core.JsonTypes"
    },
    $beans : {
        "ListModel" : {
            $type : "json:Object",
            $description : "Root of the list data model.",
            $properties : {
                "items" : {
                    $type : "ItemsArray"
                },
                "itemsView" : {
                    $type : "json:ObjectRef",
                    $description : "View on the items, with the correct order to display them.",
                    $classpath : "aria.templates.View"
                },
                "activateSort" : {
                    $type : "json:Boolean",
                    $description : "Whether sorting is enabled."
                },
                "multipleSelect" : {
                    $type : "json:Boolean",
                    $description : "Specify whether it is possible to select multiple items at the same time (true) or at most one (false)."
                },
                "maxSelectedCount" : {
                    $type : "json:Integer",
                    $minValue : 1,
                    $description : "Maximum number of items that can be selected at the same time. It is only taken into account if multipleSelect is true. A null value means there is no maximum."
                },
                "disabled" : {
                    $type : "json:Boolean",
                    $description : "Specify whether the list is disabled (true) or enabled (false)."
                },
                "selectedIndex" : {
                    $type : "json:Integer",
                    $minValue : -1,
                    $description : "Index of the selected element in the list, if only one is selected. -1 means no element is selected. A null value means several elements are selected."
                },
                "selectedCount" : {
                    $type : "json:Integer",
                    $description : "Number of selected items.",
                    $minValue : 0
                },
                "displayOptions" : {
                    $type : "displayOptions"
                },
                "cfg" : {
                    $type : "json:ObjectRef",
                    $description : "Configuration which should be used for the div surrounding the widget."
                },
                "skin" : {
                    $type : "json:ObjectRef",
                    $description : "Skin properties."
                },
                "numberOfColumns" : {
                    $type : "numberOfColumns",
                    $description : "How many Rows the multi-select should contain"
                },
                "numberOfRows" : {
                    $type : "numberOfRows",
                    $description : "How many rows are to be used in the display of the list, if the number of items isn't divisible (without remainder) by the number of rows then the next number of rows value that is divisible (without remainder) will be taken into account"
                },
                /*
                 * TODO remove? "numItemsLastCol" : { $type : "numberOfRows", $description : "How many Columns the
                 * multi-select should contain" },
                 */
                "focusIndex" : {
                    $type : "json:Integer",
                    $description : "The index of the list item with focus",
                    $default : 0
                },
                "preselect" : {
                    $type : "json:String",
                    $description : "strict: for strict highlighting (exact match only), always: for selecting the first item everytime, none: for no highlighting",
                    $default : "none"
                }
            }
        },
        "numberOfColumns" : {
            $type : "json:Integer",
            $description : "How many columns are to be used in the display of the list",
            $default : 0,
            $minValue : 0
        },
        "numberOfRows" : {
            $type : "json:Integer",
            $description : "How many columns are to be used in the display of the list",
            $default : 0,
            $minValue : 0
        },
        "displayOptions" : {
            $type : "json:Object",
            $description : "Display options that are not interpreted by the list controller.",
            $default : {
                "flowOrientation" : 'vertical',
                "tableMode" : false
            },
            $properties : {
                "flowOrientation" : {
                    $type : "json:Enum",
                    $description : "The Orientation of a list whether items are listed horizontally or vertically first. Display options may not be supported by all list templates.",
                    $enumValues : ["horizontal", "vertical"],
                    $default : 'vertical'
                },
                "tableMode" : {
                    $type : "json:Boolean",
                    $description : "Specifies whether the list uses Table Mode which is when the values of a list are split on a seperator and list resulting values are put 1 on each row",
                    $default : false
                },
                "listDisplay" : {
                    $type : "json:Enum",
                    $description : "Whether to display code, value or both on the label next to the checkbox.",
                    $enumValues : ["code", "label", "both"],
                    $default : 'code'
                },
                "displayFooter" : {
                    $type : "json:Boolean",
                    $description : "Used to display the select all, deselect all, and close links.",
                    $default : false
                }
            }
        },
        "ItemsArray" : {
            $type : "json:Array",
            $description : "Array of items.",
            $contentType : {
                $type : "Item"
            }
        },
        "Item" : {
            $type : "json:Object",
            $description : "Structure describing an item in the list",
            $properties : {
                "label" : {
                    $type : "json:String",
                    $description : "Text to display to the user - must be translated in user language."
                },
                "value" : {
                    $type : "json:String",
                    $description : "Internal value associated to the item - usually a language-independent code."
                },
                "currentlyDisabled" : {
                    $type : "json:Boolean",
                    $description : "Whether the item is disabled (true) or enabled (false), taking into account whether the maximum number of selected items has been reached or not."
                },
                "initiallyDisabled" : {
                    $type : "json:Boolean",
                    $description : "Whether the item is disabled (true) or enabled (false), independently of whether the maximum number of selected items has been reached or not."
                },
                "object" : {
                    $type : "json:ObjectRef",
                    $description : "Reference to the object storing extra information about the item."
                },
                "selected" : {
                    $type : "json:Boolean",
                    $description : "Specifies whether the item is selected (true) or not (false)."
                },
                "index" : {
                    $type : "json:Integer",
                    $description : "Index in the items array."
                }
            }
        }
    }
});
<|endoftext|>


<commit_before>// ==UserScript==
// @name         亚马逊降价商品过滤
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  亚马逊降价商品自动过滤
// @author       titilima
// @match        https://www.amazon.cn/gp/registry/wishlist?*
// @run-at       document-end
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var button = document.createElement('button');
    button.innerText = '自动化过滤降价商品';
    button.onclick = function() {
        var scrollId = window.setInterval(function() {
            if (document.getElementById('endOfListMarker')) {
                clearInterval(scrollId);
                var items = document.querySelectorAll('.g-item-sortable');
                items.forEach(function (e) {
                    var priceDrop = e.querySelector('.itemPriceDrop');
                    if (!priceDrop) {
                        e.remove();
                    }
                });
                return;
            }

            window.scrollBy(0, 2000);
        }, 2000);
    };

    var searchForm = document.getElementById('wl-item-search');
    searchForm.parentNode.appendChild(button);
})();<commit_msg>Update.<commit_after>// ==UserScript==
// @name         亚马逊降价商品过滤
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  亚马逊降价商品自动过滤
// @author       titilima
// @match        https://www.amazon.cn/gp/registry/wishlist?*
// @run-at       document-end
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var button = document.createElement('button');
    button.innerText = '自动化过滤降价商品';
    button.onclick = function() {
        var lastHeight = document.documentElement.scrollHeight;
        window.scrollBy(0, 2000);
        var scrollId = window.setInterval(function() {
            var currentHeight = document.documentElement.scrollHeight;
            if (lastHeight == currentHeight) {
                clearInterval(scrollId);
                var items = document.querySelectorAll('.g-item-sortable');
                items.forEach(function (e) {
                    var priceDrop = e.querySelector('.itemPriceDrop');
                    if (!priceDrop) {
                        e.remove();
                    }
                });
                return;
            } else {
                lastHeight = currentHeight;
                window.scrollBy(0, 2000);
            }
        }, 2000);
    };

    var searchForm = document.getElementById('wl-item-search');
    searchForm.parentNode.appendChild(button);
})();<|endoftext|>


<commit_before>// Copyright 2013 Google Inc. All Rights Reserved.

/**
 * @fileoverview Implementation bindings for attaching to Chrome Extension.
 * @author hungte@google.com (Hung-Te Lin)
 */

var ChromeInputImeImplChromeExtension = function (type) {
  var self = this;
  self.ime_api = chrome.input.ime;
  self.engineID = "chrome_input_ime#impl#chromeext";

  self._debug = false;
  self.log = function () {
    console.log.apply(console, arguments);
  }
  self.debug = function () {
    if (!self._debug)
      return;
    self.log.apply(null, arguments);
  }

  switch (type) {
    case 'background':
      self.init = self.InitBackground;
      break;
    case 'content':
      self.init = self.InitContent;
      break;
    default:
      self.log("ERROR: unknown type:", type);
      break;
  }

  return self;
};

ChromeInputImeImplChromeExtension.prototype.InitBackground = function () {
  var self = this;
  self.attach = function () { };

  var ime_api = self.ime_api;
  var ipc = new ImeEvent.ImeExtensionIPC('background');
  ipc.attach();

  function ShowPageAction() {
    chrome.tabs.getSelected(null, function(tab) {
      chrome.pageAction.show(tab.id);
    });
  }

  // Forward UI events to IME Frame.
  // Menu is installed by page action window.
  function ForwardUiToImeFrame (event_name) {
    return function (arg) {
      ipc.send(event_name, arg);
    };
  }
  ime_api.onUiComposition.addListener(ForwardUiToImeFrame("UiComposition"));
  ime_api.onUiCandidates.addListener(ForwardUiToImeFrame("UiCandidates"));
  ime_api.onUiCandidateWindow.addListener(
      ForwardUiToImeFrame("UiCandidateWindow"));

  // Send a refresh for content.js when any menu item is clicked.
  ime_api.onMenuItemActivated.addListener(function () {
    ipc.send("IpcRefreshIME");
  });

  ime_api.onActivate.addListener(function () {
    ShowPageAction();
  });
  ime_api.onFocus.addListener(function (context) {
    // BUG: Try harder to show page action, if haven't.
    ShowPageAction();
    // Notify content.js new context results.
    ipc.send("Focus", context);
  });
  ime_api.onImplCommitText.addListener(function (contextID, text) {
    ipc.send("ImplCommitText", contextID, text);
  });

  ipc.listen({
    IpcSnapshotIME: function () {
      self.debug("IpcSnapshotIME");
      return {
        im_data: jscin.getTableData(croscin.instance.im_name),
        im_name: croscin.instance.im_name,
        imctx: croscin.instance.imctx }; },

    IpcGetSystemStatus: function () {
      self.debug("IpcGetSystemStatus");
      return {
        default_enabled: croscin.instance.prefGetDefaultEnabled(),
        debug: croscin.instance.debug }; },

    IpcSetDefaultEnabled: function (new_value) {
      self.debug("IpcSetDefaultEnabled", new_value);
      return croscin.instance.prefSetDefaultEnabled(new_value); }

  }, function () {
    self.debug("Ipc Uncaught event:", arguments);
    return ime_api.dispatchEvent.apply(null, arguments);
  });
};

ChromeInputImeImplChromeExtension.prototype.InitContent = function () {
  var self = this;

  // Variables.
  self.frame = undefined;
  self.contextID = undefined;
  self.ipc = undefined;
  self.enabled = undefined;
  self.toggleHotKey = 16;  // Shift.
  self.waitForHotkey = false;

  function SendMessage() {
    self.ipc.send.apply(null, arguments);
  }

  function AttachKeyEvents(node) {
    node.addEventListener('keydown', KeyDownEventHandler);
    node.addEventListener('keyup', KeyUpEventHandler);
  }

  function DetachKeyEvents(node) {
    node.removeEventListener('keydown', KeyDownEventHandler);
    node.removeEventListener('keyup', KeyUpEventHandler);
  }

  function SetEnabled(enabled) {
    self.debug("SetEnabled", enabled, self.enabled);
    if (typeof(self.enabled) == 'undefined') {
      // First time setting enabled.
      self.enabled = enabled;
      // TODO(hungte) should we show frame?
    } else {
      // Apparently user is already doing something.
      self.enabled = enabled;
      if (enabled) {
        self.debug("setEnabled: frame.fadeIn(100)");
        self.frame.finish();
        self.frame.fadeIn(100);
      } else {
        self.debug("setEnabled: frame.fadeOut(100)");
        self.frame.finish();
        self.frame.fadeOut(100);
      }
      // Notify background page to change settings.
      SendMessage('IpcSetDefaultEnabled', enabled);
    }
  }

  function KeyUpEventHandler(ev) {
    // Assume our IME won't do anything on key up, let's only check hotkeys.
    if (!self.waitForHotkey)
      return;

    if (ev.keyCode == self.toggleHotKey) {
      self.debug("Got toggle hot key!", self.enabled);
      SetEnabled(!self.enabled);
    }
    self.waitForHotkey = false;
  }

  function UpdateUI() {
    self.debug("UpdateUI");
    var imctx = self.imctx;
    SendMessage("ImplUpdateUI", imctx.keystroke, imctx.mcch, imctx.selkey);
  }

  // Same as croscin.ProcessKeyEvent. Need self.im*.
  function ProcessKeyEvent(ev) {
    self.debug("ProcessKeyEvent:", ev.key);

    // Currently all of the modules uses key down.
    if (ev.type != 'keydown') {
      return false;
    }
    var ret = self.im.onKeystroke(self.imctx, ev);

    switch (ret) {
      case jscin.IMKEY_COMMIT:
        self.debug("im.onKeystroke: return IMKEY_COMMIT");
        UpdateUI();
        // croscin may have extra UI processing when committing text (ex,
        // cross-query) so we don't want to do ImplCommitText directly. Let's
        // route back to croscin.
        SendMessage("ImplCommit", self.imctx.cch);
        return true;

      case jscin.IMKEY_ABSORB:
        self.debug("im.onKeystroke: return IMKEY_ABSORB");
        UpdateUI();
        return true;

      case jscin.IMKEY_IGNORE:
        self.debug("im.onKeystroke: return IMKEY_IGNORE");
        UpdateUI();
        return false;
    }

    // default: Unknown return value.
    self.debug("ProcessKeyEvent: Unknown return value:", ret);
    return false;
  }

  function KeyDownEventHandler(ev) {
    if (!self.im) {
      self.debug("Key event before IM is ready.");
      return;
    }

    if (self.waitForHotkey)
      self.waitForHotkey = false;

    if (ev.keyCode == self.toggleHotKey && !ev.ctrlKey && !ev.altKey) {
      self.debug("Wait to check toggle hotkey!");
      self.waitForHotkey = true;
      // Assume our IME don't need to handle single shift key.
      return;
    }

    if (!self.enabled)
      return;

    var ev2 = ImeEvent.ImeKeyEvent(ev);
    var node = ev.target;
    self.debug("impl.KeyDownEventHandler", ev, ev2);

    // Simulation by SnapshotIME.
    if (ProcessKeyEvent(ev2)) {
      ev.preventDefault();
    }
  }

  function GetAbsoluteOffset(node) {
    var offset = { left: 0, 'top': 0};
    while (node) {
      offset.left += node.offsetLeft;
      offset['top'] += node.offsetTop;
      node = node.offsetParent;
    }
    return offset;
  }

  function ImplCommitText(node, text) {
    if (node.tagName == 'TEXTAREA' || node.tagName == 'INPUT') {
      // input or textarea.
      var newpos = node.selectionStart + text.length;
      node.value = (node.value.substring(0, node.selectionStart) +
                    text + node.value.substring(node.selectionEnd));
      node.selectionStart = newpos;
      node.selectionEnd = newpos;
      return;
    }

    // Probably a [contenteditable] element.
    var sel = window.getSelection();
    if (sel.rangeCount) {
      var range = sel.getRangeAt(0);
      range.deleteContents();
      var newnode = document.createTextNode(text);
      range.insertNode(newnode);
      range = range.cloneRange();
      range.setStartAfter(newnode);
      range.setEndAfter(newnode);
      sel.removeAllRanges();
      sel.addRange(range);
      range.commonAncestorContainer.normalize();
      return;
    }
  }

  function FocusHandler(ev) {
    var node = ev.target;
    self.debug("on focus", node);
    self.node = node;
    SendMessage("ImplFocus");
  }

  function BlurHandler(ev) {
    if (self.contextID) {
      self.debug("on blur", self.contextID);
      if (self.enabled) {
        self.debug("BlurHandler: frame.fadeOut(100)");
        self.frame.finish();
        self.frame.fadeOut(100);
      }
      DetachKeyEvents(ev.target);
      SendMessage("Blur", self.contextID);
      self.contextID = undefined;
    }
  }

  function SnapshotIME() {
    self.im = undefined;
    self.imctx = undefined;
    SendMessage("IpcSnapshotIME", function (result) {
      var name = result.im_name;
      self.debug("Snapshot - IM:", result);
      if (!name) {
        self.debug("Remote IM is not ready... good luck.");
        // TODO(hungte) Don't continue.
        return;
      }
      jscin.register_input_method(name, 'GenInp', 'snapshot');
      self.imctx = result.imctx || {};
      self.im = jscin.create_input_method(name, self.imctx, result.im_data);
    });
  }

  function InitContent() {
    var ipc = new ImeEvent.ImeExtensionIPC('content');
    self.ipc = ipc;
    ipc.attach();

    ipc.listen({
      IpcUiReady: function () {
        self.debug("UIReady",document.activeElement);
        SendMessage("Activate", self.engineID); // Update menu & pageAction.
        // Delayed init.
        if (self.init_node) {
          self.debug("Found init_node:", self.init_node);
          FocusHandler({target: self.init_node});
        }
        self.init_node = undefined;
      },

      IpcRefreshIME: function () {
        // Need to request for another snapshot.
        SnapshotIME();
      },

      ImplCommitText: function (contextID, text) {
        ImplCommitText(self.node, text);
      },

      Focus: function (context) {
        var node = self.node;
        self.contextID = context.contextID;
        AttachKeyEvents(node);

        var offset = GetAbsoluteOffset(node);
        offset.left += 5;
        // TODO(hungte) Remove jquery -- although height() is hard to replace.
        offset.top += $(node).height();
        self.frame.css(offset);
        if (self.enabled) {
          self.debug("Focus: frame.fadeIn(250)", self.frame.offset());
          self.frame.finish();
          self.frame.fadeIn(250);
        }
      },

      MenuItemActivated: function (engineID, name) {
        // (Legacy, when menu is included in iframe) forward to background.
        self.ipc.send.apply("MenuItemActivated", engineID, name);
        SnapshotIME();
      }
    });

    SendMessage('IpcGetSystemStatus', function (result) {
      self.debug("IpcGetSystemStatus received:", result);
      self._debug = result.debug;
      SetEnabled(result.default_enabled);
    });
    SnapshotIME();
  }

  self.attach = function (node) {
    node.addEventListener("focus", FocusHandler);
    node.addEventListener("blur", BlurHandler);
  };

  self.setFrame = function (frame) {
    self.frame = frame;
  }

  self.setInitNode = function (node) {
    self.init_node = node;
  }

  InitContent();
};
<commit_msg>Load correct IME module in non-ChromeOS mode.<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.

/**
 * @fileoverview Implementation bindings for attaching to Chrome Extension.
 * @author hungte@google.com (Hung-Te Lin)
 */

var ChromeInputImeImplChromeExtension = function (type) {
  var self = this;
  self.ime_api = chrome.input.ime;
  self.engineID = "chrome_input_ime#impl#chromeext";

  self._debug = false;
  self.log = function () {
    console.log.apply(console, arguments);
  }
  self.debug = function () {
    if (!self._debug)
      return;
    self.log.apply(null, arguments);
  }

  switch (type) {
    case 'background':
      self.init = self.InitBackground;
      break;
    case 'content':
      self.init = self.InitContent;
      break;
    default:
      self.log("ERROR: unknown type:", type);
      break;
  }

  return self;
};

ChromeInputImeImplChromeExtension.prototype.InitBackground = function () {
  var self = this;
  self.attach = function () { };

  var ime_api = self.ime_api;
  var ipc = new ImeEvent.ImeExtensionIPC('background');
  ipc.attach();

  function ShowPageAction() {
    chrome.tabs.getSelected(null, function(tab) {
      chrome.pageAction.show(tab.id);
    });
  }

  // Forward UI events to IME Frame.
  // Menu is installed by page action window.
  function ForwardUiToImeFrame (event_name) {
    return function (arg) {
      ipc.send(event_name, arg);
    };
  }
  ime_api.onUiComposition.addListener(ForwardUiToImeFrame("UiComposition"));
  ime_api.onUiCandidates.addListener(ForwardUiToImeFrame("UiCandidates"));
  ime_api.onUiCandidateWindow.addListener(
      ForwardUiToImeFrame("UiCandidateWindow"));

  // Send a refresh for content.js when any menu item is clicked.
  ime_api.onMenuItemActivated.addListener(function () {
    ipc.send("IpcRefreshIME");
  });

  ime_api.onActivate.addListener(function () {
    ShowPageAction();
  });
  ime_api.onFocus.addListener(function (context) {
    // BUG: Try harder to show page action, if haven't.
    ShowPageAction();
    // Notify content.js new context results.
    ipc.send("Focus", context);
  });
  ime_api.onImplCommitText.addListener(function (contextID, text) {
    ipc.send("ImplCommitText", contextID, text);
  });

  ipc.listen({
    IpcSnapshotIME: function () {
      self.debug("IpcSnapshotIME");
      return {
        im_module: jscin.getDefaultModuleName(),
        im_data: jscin.getTableData(croscin.instance.im_name),
        im_name: croscin.instance.im_name,
        imctx: croscin.instance.imctx }; },

    IpcGetSystemStatus: function () {
      self.debug("IpcGetSystemStatus");
      return {
        default_enabled: croscin.instance.prefGetDefaultEnabled(),
        debug: croscin.instance.debug }; },

    IpcSetDefaultEnabled: function (new_value) {
      self.debug("IpcSetDefaultEnabled", new_value);
      return croscin.instance.prefSetDefaultEnabled(new_value); }

  }, function () {
    self.debug("Ipc Uncaught event:", arguments);
    return ime_api.dispatchEvent.apply(null, arguments);
  });
};

ChromeInputImeImplChromeExtension.prototype.InitContent = function () {
  var self = this;

  // Variables.
  self.frame = undefined;
  self.contextID = undefined;
  self.ipc = undefined;
  self.enabled = undefined;
  self.toggleHotKey = 16;  // Shift.
  self.waitForHotkey = false;

  function SendMessage() {
    self.ipc.send.apply(null, arguments);
  }

  function AttachKeyEvents(node) {
    node.addEventListener('keydown', KeyDownEventHandler);
    node.addEventListener('keyup', KeyUpEventHandler);
  }

  function DetachKeyEvents(node) {
    node.removeEventListener('keydown', KeyDownEventHandler);
    node.removeEventListener('keyup', KeyUpEventHandler);
  }

  function SetEnabled(enabled) {
    self.debug("SetEnabled", enabled, self.enabled);
    if (typeof(self.enabled) == 'undefined') {
      // First time setting enabled.
      self.enabled = enabled;
      // TODO(hungte) should we show frame?
    } else {
      // Apparently user is already doing something.
      self.enabled = enabled;
      if (enabled) {
        self.debug("setEnabled: frame.fadeIn(100)");
        self.frame.finish();
        self.frame.fadeIn(100);
      } else {
        self.debug("setEnabled: frame.fadeOut(100)");
        self.frame.finish();
        self.frame.fadeOut(100);
      }
      // Notify background page to change settings.
      SendMessage('IpcSetDefaultEnabled', enabled);
    }
  }

  function KeyUpEventHandler(ev) {
    // Assume our IME won't do anything on key up, let's only check hotkeys.
    if (!self.waitForHotkey)
      return;

    if (ev.keyCode == self.toggleHotKey) {
      self.debug("Got toggle hot key!", self.enabled);
      SetEnabled(!self.enabled);
    }
    self.waitForHotkey = false;
  }

  function UpdateUI() {
    self.debug("UpdateUI");
    var imctx = self.imctx;
    SendMessage("ImplUpdateUI", imctx.keystroke, imctx.mcch, imctx.selkey);
  }

  // Same as croscin.ProcessKeyEvent. Need self.im*.
  function ProcessKeyEvent(ev) {
    self.debug("ProcessKeyEvent:", ev.key);

    // Currently all of the modules uses key down.
    if (ev.type != 'keydown') {
      return false;
    }
    var ret = self.im.onKeystroke(self.imctx, ev);

    switch (ret) {
      case jscin.IMKEY_COMMIT:
        self.debug("im.onKeystroke: return IMKEY_COMMIT");
        UpdateUI();
        // croscin may have extra UI processing when committing text (ex,
        // cross-query) so we don't want to do ImplCommitText directly. Let's
        // route back to croscin.
        SendMessage("ImplCommit", self.imctx.cch);
        return true;

      case jscin.IMKEY_ABSORB:
        self.debug("im.onKeystroke: return IMKEY_ABSORB");
        UpdateUI();
        return true;

      case jscin.IMKEY_IGNORE:
        self.debug("im.onKeystroke: return IMKEY_IGNORE");
        UpdateUI();
        return false;
    }

    // default: Unknown return value.
    self.debug("ProcessKeyEvent: Unknown return value:", ret);
    return false;
  }

  function KeyDownEventHandler(ev) {
    if (!self.im) {
      self.debug("Key event before IM is ready.");
      return;
    }

    if (self.waitForHotkey)
      self.waitForHotkey = false;

    if (ev.keyCode == self.toggleHotKey && !ev.ctrlKey && !ev.altKey) {
      self.debug("Wait to check toggle hotkey!");
      self.waitForHotkey = true;
      // Assume our IME don't need to handle single shift key.
      return;
    }

    if (!self.enabled)
      return;

    var ev2 = ImeEvent.ImeKeyEvent(ev);
    var node = ev.target;
    self.debug("impl.KeyDownEventHandler", ev, ev2);

    // Simulation by SnapshotIME.
    if (ProcessKeyEvent(ev2)) {
      ev.preventDefault();
    }
  }

  function GetAbsoluteOffset(node) {
    var offset = { left: 0, 'top': 0};
    while (node) {
      offset.left += node.offsetLeft;
      offset['top'] += node.offsetTop;
      node = node.offsetParent;
    }
    return offset;
  }

  function ImplCommitText(node, text) {
    if (node.tagName == 'TEXTAREA' || node.tagName == 'INPUT') {
      // input or textarea.
      var newpos = node.selectionStart + text.length;
      node.value = (node.value.substring(0, node.selectionStart) +
                    text + node.value.substring(node.selectionEnd));
      node.selectionStart = newpos;
      node.selectionEnd = newpos;
      return;
    }

    // Probably a [contenteditable] element.
    var sel = window.getSelection();
    if (sel.rangeCount) {
      var range = sel.getRangeAt(0);
      range.deleteContents();
      var newnode = document.createTextNode(text);
      range.insertNode(newnode);
      range = range.cloneRange();
      range.setStartAfter(newnode);
      range.setEndAfter(newnode);
      sel.removeAllRanges();
      sel.addRange(range);
      range.commonAncestorContainer.normalize();
      return;
    }
  }

  function FocusHandler(ev) {
    var node = ev.target;
    self.debug("on focus", node);
    self.node = node;
    SendMessage("ImplFocus");
  }

  function BlurHandler(ev) {
    if (self.contextID) {
      self.debug("on blur", self.contextID);
      if (self.enabled) {
        self.debug("BlurHandler: frame.fadeOut(100)");
        self.frame.finish();
        self.frame.fadeOut(100);
      }
      DetachKeyEvents(ev.target);
      SendMessage("Blur", self.contextID);
      self.contextID = undefined;
    }
  }

  function SnapshotIME() {
    self.im = undefined;
    self.imctx = undefined;
    SendMessage("IpcSnapshotIME", function (result) {
      var name = result.im_name;
      self.debug("Snapshot - IM:", result);
      if (!name) {
        self.debug("Remote IM is not ready... good luck.");
        // TODO(hungte) Don't continue.
        return;
      }
      jscin.register_input_method(name, result.im_module, 'snapshot');
      self.imctx = result.imctx || {};
      self.im = jscin.create_input_method(name, self.imctx, result.im_data);
    });
  }

  function InitContent() {
    var ipc = new ImeEvent.ImeExtensionIPC('content');
    self.ipc = ipc;
    ipc.attach();

    ipc.listen({
      IpcUiReady: function () {
        self.debug("UIReady",document.activeElement);
        SendMessage("Activate", self.engineID); // Update menu & pageAction.
        // Delayed init.
        if (self.init_node) {
          self.debug("Found init_node:", self.init_node);
          FocusHandler({target: self.init_node});
        }
        self.init_node = undefined;
      },

      IpcRefreshIME: function () {
        // Need to request for another snapshot.
        SnapshotIME();
      },

      ImplCommitText: function (contextID, text) {
        ImplCommitText(self.node, text);
      },

      Focus: function (context) {
        var node = self.node;
        self.contextID = context.contextID;
        AttachKeyEvents(node);

        var offset = GetAbsoluteOffset(node);
        offset.left += 5;
        // TODO(hungte) Remove jquery -- although height() is hard to replace.
        offset.top += $(node).height();
        self.frame.css(offset);
        if (self.enabled) {
          self.debug("Focus: frame.fadeIn(250)", self.frame.offset());
          self.frame.finish();
          self.frame.fadeIn(250);
        }
      },

      MenuItemActivated: function (engineID, name) {
        // (Legacy, when menu is included in iframe) forward to background.
        self.ipc.send.apply("MenuItemActivated", engineID, name);
        SnapshotIME();
      }
    });

    SendMessage('IpcGetSystemStatus', function (result) {
      self.debug("IpcGetSystemStatus received:", result);
      self._debug = result.debug;
      SetEnabled(result.default_enabled);
    });
    SnapshotIME();
  }

  self.attach = function (node) {
    node.addEventListener("focus", FocusHandler);
    node.addEventListener("blur", BlurHandler);
  };

  self.setFrame = function (frame) {
    self.frame = frame;
  }

  self.setInitNode = function (node) {
    self.init_node = node;
  }

  InitContent();
};
<|endoftext|>


<commit_before>/**
 * 	@author       Rémi MARTIN <martinremi60@gmail.com>
 * 	@copyright    2014 Rémi MARTIN
 */

/**
 *  Called when a click is performed on the canvas
 *  @method yom_click_event_action
 * 	@param {Object} [e] - The event performed
 */
var yom_click_event_action = function(e) {
	// alert(e);
	// var posX = $(this).position().left,
	// 	posY = $(this).position().top;
	// var params = {
	// 	x: e.pageX - posX,
	// 	y: e.pageY - posY
	// };
	// yom.input.handleInput(yom.input.EventType.CLICK, params);
};

/**
 *  Called when the mouse is pressed down
 *  @method yom_mouse_down_event_action
 * 	@param {Object} [e] - The event performed
 */
var yom_mouse_down_event_action = function(e) {
	// var posX = $(this).position().left,
	// 	posY = $(this).position().top;
	// var params = {
	// 	x: e.pageX - posX,
	// 	y: e.pageY - posY
	// };
	// yom.input.handleInput(yom.input.EventType.MOUSE_DOWN, params);
};

/**
 *  Called when the mouse is released
 *  @method yom_mouse_up_event_action
 * 	@param {Object} [e] - The event performed
 */
var yom_mouse_up_event_action = function(e) {
	// var posX = $(this).position().left,
	// 	posY = $(this).position().top;
	// var params = {
	// 	x: e.pageX - posX,
	// 	y: e.pageY - posY
	// };
	// yom.input.handleInput(yom.input.EventType.MOUSE_UP, params);
};

/**
 *  Called when the mouse is moved
 *  @method yom_mouse_move_event_action
 * 	@param {Object} [e] - The event performed
 */
var yom_mouse_move_event_action = function(e) {
	// var posX = $(this).position().left,
	// 	posY = $(this).position().top;
	// var params = {
	// 	x: e.pageX - posX,
	// 	y: e.pageY - posY
	// };
	// yom.input.handleInput(yom.input.EventType.MOUSE_MOVE, params);
};
<commit_msg>Replaced jquery events by their equivalent in pure js<commit_after>/**
 * 	@author       Rémi MARTIN <martinremi60@gmail.com>
 * 	@copyright    2014 Rémi MARTIN
 */

/**
 *  Called when a click is performed on the canvas
 *  @method yom_click_event_action
 * 	@param {Object} [e] - The event performed
 */
var yom_click_event_action = function(e) {
	var parentPosition = getPosition(e.currentTarget);
	var x = e.clientX - parentPosition.x;
    var y = e.clientY - parentPosition.y;
	var params = {
		x: x,
		y: y
	};
	yom.input.handleInput(yom.input.EventType.CLICK, params);
};

/**
 *  Called when the mouse is pressed down
 *  @method yom_mouse_down_event_action
 * 	@param {Object} [e] - The event performed
 */
var yom_mouse_down_event_action = function(e) {
	var parentPosition = getPosition(e.currentTarget);
	var x = e.clientX - parentPosition.x;
    var y = e.clientY - parentPosition.y;
	var params = {
		x: x,
		y: y
	};
	yom.input.handleInput(yom.input.EventType.MOUSE_DOWN, params);
};

/**
 *  Called when the mouse is released
 *  @method yom_mouse_up_event_action
 * 	@param {Object} [e] - The event performed
 */
var yom_mouse_up_event_action = function(e) {
	var parentPosition = getPosition(e.currentTarget);
	var x = e.clientX - parentPosition.x;
    var y = e.clientY - parentPosition.y;
	var params = {
		x: x,
		y: y
	};
	yom.input.handleInput(yom.input.EventType.MOUSE_UP, params);
};

/**
 *  Called when the mouse is moved
 *  @method yom_mouse_move_event_action
 * 	@param {Object} [e] - The event performed
 */
var yom_mouse_move_event_action = function(e) {
	var parentPosition = getPosition(e.currentTarget);
	var x = e.clientX - parentPosition.x;
    var y = e.clientY - parentPosition.y;
	var params = {
		x: x,
		y: y
	};
	yom.input.handleInput(yom.input.EventType.MOUSE_MOVE, params);
};

function getPosition(element) {
    var xPosition = 0;
    var yPosition = 0;

    while (element) {
        xPosition += (element.offsetLeft - element.scrollLeft + element.clientLeft);
        yPosition += (element.offsetTop - element.scrollTop + element.clientTop);
        element = element.offsetParent;
    }
    return { x: xPosition, y: yPosition };
}
<|endoftext|>