diff --git a/.gitignore b/.gitignore
index ecbd7c4..fffde51 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@
.idea
*.iml
out
+.DS_Store
diff --git a/README.md b/README.md
index 7070516..d859851 100644
--- a/README.md
+++ b/README.md
@@ -1,23 +1,22 @@
zabbixnotifier
==============
-Zabbix Notifier for Google Chrome
+Zabbix Notifier for Mozilla Firefox
![
-](https://raw.github.com/gigatec/zabbixnotifier/master/screenshot.png)
+](https://github.com/rsanting/zabbixnotifier/blob/master/screenshot.png)
-Funktionen:
-Mit dieser Erweiterung werden aktuelle Statusmeldungen des Zabbix Monitoring-Systems direkt im Browser angezeigt. Störungen und Probleme werden so schnell und zuverlässig erkannt.
-Auch eine Soundbenachrichtigung ist konfigurierbar.
-Durch die sich ändernde Farbe des Icons in der Stautsleiste des Browsers ist auf einen Blick ein Überblick über die überwachten Systeme und Dienste möglich.
-Details:
-- Getestet mit Zabbix 2.0.x
-- Nutzt die Zabbix-API
-- User-Name, Passwort, URL, Port und Updateintervall sind konfigurierbar
+features:
+With this extension, current status messages of the Zabbix monitoring system are displayed directly in the browser. Faults and problems are detected quickly and reliably. Due to the changing color of the icon in the browser's browser bar, the monitored systems and services can be viewed at a glance.
+
+details:
+- Tested with Zabbix 2.0.x
+- Use the Zabbix API
+- User name, password, URL, port, notification sound and update interval are configurable
ChangeLog:
-- 1.1.3 - Behebt Login-Probleme in Verbindung mit Zabbix 2.0.4
+- 1.1.3 - Fixes login issues related to Zabbix 2.0.4
- 1.2 - Bugfixes (Update-Problems), Number of Problems is displayed in the icon
- 1.3 - Bugfix (Wrong icon when there are no notifications)
- 1.4 - Dependency Checking (skipDependent: '1')
@@ -25,12 +24,4 @@ ChangeLog:
- 1.6 - Bugfix Zabbix 2.4 Authentication
- 1.6.1 - Add feature filter by group
- 1.7 - Add support for zabbix 3.4, display the System Name
-
-Chrome Web Store: https://chrome.google.com/webstore/detail/zabbix-notifier/ikeijbmpddnkaeejokgifioccbcijjfo
-
-Contributors:
-- Stefan Kreuter
-- skreuter
-- gigatec
-- Nicolas Berthe
-- interpeix
+- 1.7.3 - Added Mozilla Firefox support
diff --git a/build.sh b/build.sh
index cee8ea7..78075f7 100755
--- a/build.sh
+++ b/build.sh
@@ -4,4 +4,4 @@ mkdir -p out
cd src/
VERSION=$(sed -n 's/^.*"version": *"\([0-9\.]*\)",$/\1/p' manifest.json)
-zip -r ../out/zabbixnotifier_$VERSION.zip *
+zip -r ../out/zabbixwatcher_$VERSION.zip *
diff --git a/screenshot.png b/screenshot.png
index 1240501..fb287f2 100644
Binary files a/screenshot.png and b/screenshot.png differ
diff --git a/src/background.html b/src/background.html
index 1459fde..19faff2 100644
--- a/src/background.html
+++ b/src/background.html
@@ -4,7 +4,7 @@
+
+ The default context of the compiled template will be the view instance itself:
+
+ AView = Ember.View.extend({
+ template: Ember.Handlebars.compile('Hello {{excitedGreeting}}')
+ })
+
+ aView = AView.create({
+ content: Ember.Object.create({
+ firstName: 'Barry'
+ })
+ excitedGreeting: function(){
+ return this.getPath("content.firstName") + "!!!"
+ }
+ })
+
+ Will result in an HTML representation of:
+
+
Hello Barry!!!
+
+ Within an Ember application is more common to define a Handlebars templates as
+ part of a page:
+
+
+
+ And associate it by name using a view's `templateName` property:
+
+ AView = Ember.View.extend({
+ templateName: 'some-template'
+ })
+
+ Using a value for `templateName` that does not have a Handlebars template with a
+ matching `data-template-name` attribute will throw an error.
+
+ Assigning a value to both `template` and `templateName` properties will throw an error.
+
+ For views classes that may have a template later defined (e.g. as the block portion of a `{{view}}`
+ Handlebars helper call in another template or in a subclass), you can provide a `defaultTemplate`
+ property set to compiled template function. If a template is not later provided for the view
+ instance the `defaultTemplate` value will be used:
+
+ AView = Ember.View.extend({
+ defaultTemplate: Ember.Handlebars.compile('I was the default'),
+ template: null,
+ templateName: null
+ })
+
+ Will result in instances with an HTML representation of:
+
+
I was the default
+
+ If a `template` or `templateName` is provided it will take precedence over `defaultTemplate`:
+
+ AView = Ember.View.extend({
+ defaultTemplate: Ember.Handlebars.compile('I was the default')
+ })
+
+ aView = AView.create({
+ template: Ember.Handlebars.compile('I was the template, not default')
+ })
+
+ Will result in the following HTML representation when rendered:
+
+
I was the template, not default
+
+ ## Layouts
+ Views can have a secondary outer template that wraps their main template. Like
+ primary templates, layouts can be any function that accepts an optional context
+ parameter and returns a string of HTML that will be inserted a view's tag.
+ Most typically in Ember this function will be a compiled Ember.Handlebars template.
+
+ A view's layout can be set directly with the `layout` property or reference an
+ existing Handlebars template by name with the `layoutName` property.
+
+ A template used as a layout must contain a single use of the Handlebars `{{yield}}`
+ helper. The HTML contents of a view's rendered `template` will be inserted at this location:
+
+ AViewWithLayout = Ember.View.extend({
+ layout: Ember.Handlebars.compile("
{{yield}}
")
+ template: Ember.Handlebars.compile("I got wrapped"),
+ })
+
+ Will result in view instances with an HTML representation of:
+
+
+
+ I got wrapped
+
+
+
+ See `Handlebars.helpers.yield` for more information.
+
+ ## Responding to Browser Events
+ Views can respond to user-initiated events in one of three ways: method implementation,
+ through an event manager, and through `{{action}}` helper use in their template or layout.
+
+ ### Method Implementation
+ Views can respond to user-initiated events by implementing a method that matches the
+ event name. A `jQuery.Event` object will be passed as the argument to this method.
+
+ AView = Ember.View.extend({
+ click: function(event){
+ // will be called when when an instance's
+ // rendered element is clicked
+ }
+ })
+
+ ### Event Managers
+ Views can define an object as their `eventManager` property. This object can then
+ implement methods that match the desired event names. Matching events that occur
+ on the view's rendered HTML or the rendered HTML of any of its DOM descendants
+ will trigger this method. A `jQuery.Event` object will be passed as the first
+ argument to the method and an `Ember.View` object as the second. The `Ember.View`
+ will be the view whose rendered HTML was interacted with. This may be the view with
+ the `eventManager` property or one of its descendent views.
+
+ AView = Ember.View.extend({
+ eventManager: Ember.Object.create({
+ doubleClick: function(event, view){
+ // will be called when when an instance's
+ // rendered element or any rendering
+ // of this views's descendent
+ // elements is clicked
+ }
+ })
+ })
+
+
+ An event defined for an event manager takes precedence over events of the same
+ name handled through methods on the view.
+
+
+ AView = Ember.View.extend({
+ mouseEnter: function(event){
+ // will never trigger.
+ },
+ eventManager: Ember.Object.create({
+ mouseEnter: function(event, view){
+ // takes presedence over AView#mouseEnter
+ }
+ })
+ })
+
+ Similarly a view's event manager will take precedence for events of any views
+ rendered as a descendent. A method name that matches an event name will not be called
+ if the view instance was rendered inside the HTML representation of a view that has
+ an `eventManager` property defined that handles events of the name. Events not handled
+ by the event manager will still trigger method calls on the descendent.
+
+ OuterView = Ember.View.extend({
+ eventManager: Ember.Object.create({
+ template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"),
+ mouseEnter: function(event, view){
+ // view might be instance of either
+ // OutsideView or InnerView depending on
+ // where on the page the user interaction occured
+ }
+ })
+ })
+
+ InnerView = Ember.View.extend({
+ click: function(event){
+ // will be called if rendered inside
+ // an OuterView because OuterView's
+ // eventManager doesn't handle click events
+ },
+ mouseEnter: function(event){
+ // will never be called if rendered inside
+ // an OuterView.
+ }
+ })
+
+ ### Handlebars `{{action}}` Helper
+ See `Handlebars.helpers.action`.
+
+ ### Event Names
+ Possible events names for any of the responding approaches described above are:
+
+ Touch events: 'touchStart', 'touchMove', 'touchEnd', 'touchCancel'
+
+ Keyboard events: 'keyDown', 'keyUp', 'keyPress'
+
+ Mouse events: 'mouseDown', 'mouseUp', 'contextMenu', 'click', 'doubleClick', 'mouseMove',
+ 'focusIn', 'focusOut', 'mouseEnter', 'mouseLeave'
+
+ Form events: 'submit', 'change', 'focusIn', 'focusOut', 'input'
+
+ HTML5 drag and drop events: 'dragStart', 'drag', 'dragEnter', 'dragLeave', 'drop', 'dragEnd'
+
+ ## Handlebars `{{view}}` Helper
+ Other `Ember.View` instances can be included as part of a view's template by using the `{{view}}`
+ Handlebars helper. See `Handlebars.helpers.view` for additional information.
+
+ @extends Ember.Object
+*/
+Ember.View = Ember.Object.extend(Ember.Evented,
+/** @scope Ember.View.prototype */ {
+
+ /** @private */
+ concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'],
+
+ /**
+ @type Boolean
+ @default true
+ @constant
+ */
+ isView: true,
+
+ // ..........................................................
+ // TEMPLATE SUPPORT
+ //
+
+ /**
+ The name of the template to lookup if no template is provided.
+
+ Ember.View will look for a template with this name in this view's
+ `templates` object. By default, this will be a global object
+ shared in `Ember.TEMPLATES`.
+
+ @type String
+ @default null
+ */
+ templateName: null,
+
+ /**
+ The name of the layout to lookup if no layout is provided.
+
+ Ember.View will look for a template with this name in this view's
+ `templates` object. By default, this will be a global object
+ shared in `Ember.TEMPLATES`.
+
+ @type String
+ @default null
+ */
+ layoutName: null,
+
+ /**
+ The hash in which to look for `templateName`.
+
+ @type Ember.Object
+ @default Ember.TEMPLATES
+ */
+ templates: Ember.TEMPLATES,
+
+ /**
+ The template used to render the view. This should be a function that
+ accepts an optional context parameter and returns a string of HTML that
+ will be inserted into the DOM relative to its parent view.
+
+ In general, you should set the `templateName` property instead of setting
+ the template yourself.
+
+ @field
+ @type Function
+ */
+ template: Ember.computed(function(key, value) {
+ if (value !== undefined) { return value; }
+
+ var templateName = get(this, 'templateName'),
+ template = this.templateForName(templateName, 'template');
+
+ return template || get(this, 'defaultTemplate');
+ }).property('templateName').cacheable(),
+
+ /**
+ The controller managing this view. If this property is set, it will be
+ made available for use by the template.
+
+ @type Object
+ */
+ controller: null,
+
+ /**
+ A view may contain a layout. A layout is a regular template but
+ supersedes the `template` property during rendering. It is the
+ responsibility of the layout template to retrieve the `template`
+ property from the view and render it in the correct location.
+
+ This is useful for a view that has a shared wrapper, but which delegates
+ the rendering of the contents of the wrapper to the `template` property
+ on a subclass.
+
+ @field
+ @type Function
+ */
+ layout: Ember.computed(function(key, value) {
+ if (arguments.length === 2) { return value; }
+
+ var layoutName = get(this, 'layoutName'),
+ layout = this.templateForName(layoutName, 'layout');
+
+ return layout || get(this, 'defaultLayout');
+ }).property('layoutName').cacheable(),
+
+ templateForName: function(name, type) {
+ if (!name) { return; }
+
+ var templates = get(this, 'templates'),
+ template = get(templates, name);
+
+ if (!template) {
+ throw new Ember.Error(fmt('%@ - Unable to find %@ "%@".', [this, type, name]));
+ }
+
+ return template;
+ },
+
+ /**
+ The object from which templates should access properties.
+
+ This object will be passed to the template function each time the render
+ method is called, but it is up to the individual function to decide what
+ to do with it.
+
+ By default, this will be the view itself.
+
+ @type Object
+ */
+ templateContext: Ember.computed(function(key, value) {
+ if (arguments.length === 2) {
+ set(this, '_templateContext', value);
+ return value;
+ } else {
+ return get(this, '_templateContext');
+ }
+ }).cacheable(),
+
+ /**
+ @private
+
+ Private copy of the view's template context. This can be set directly
+ by Handlebars without triggering the observer that causes the view
+ to be re-rendered.
+ */
+ _templateContext: Ember.computed(function(key, value) {
+ var parentView;
+
+ if (arguments.length === 2) {
+ return value;
+ }
+
+ if (VIEW_PRESERVES_CONTEXT) {
+ parentView = get(this, '_parentView');
+ if (parentView) {
+ return get(parentView, '_templateContext');
+ }
+ }
+
+ return this;
+ }).cacheable(),
+
+ /**
+ If a value that affects template rendering changes, the view should be
+ re-rendered to reflect the new value.
+
+ @private
+ */
+ _displayPropertyDidChange: Ember.observer(function() {
+ this.rerender();
+ }, 'templateContext', 'controller'),
+
+ /**
+ If the view is currently inserted into the DOM of a parent view, this
+ property will point to the parent of the view.
+
+ @type Ember.View
+ @default null
+ */
+ parentView: Ember.computed(function() {
+ var parent = get(this, '_parentView');
+
+ if (parent && parent.isVirtual) {
+ return get(parent, 'parentView');
+ } else {
+ return parent;
+ }
+ }).property('_parentView').volatile(),
+
+ _parentView: null,
+
+ // return the current view, not including virtual views
+ concreteView: Ember.computed(function() {
+ if (!this.isVirtual) { return this; }
+ else { return get(this, 'parentView'); }
+ }).property('_parentView').volatile(),
+
+ /**
+ If false, the view will appear hidden in DOM.
+
+ @type Boolean
+ @default null
+ */
+ isVisible: true,
+
+ /**
+ Array of child views. You should never edit this array directly.
+ Instead, use appendChild and removeFromParent.
+
+ @private
+ @type Array
+ @default []
+ */
+ childViews: childViewsProperty,
+
+ _childViews: [],
+
+ /**
+ When it's a virtual view, we need to notify the parent that their
+ childViews will change.
+ */
+ _childViewsWillChange: Ember.beforeObserver(function() {
+ if (this.isVirtual) {
+ var parentView = get(this, 'parentView');
+ if (parentView) { Ember.propertyWillChange(parentView, 'childViews'); }
+ }
+ }, 'childViews'),
+
+ /**
+ When it's a virtual view, we need to notify the parent that their
+ childViews did change.
+ */
+ _childViewsDidChange: Ember.observer(function() {
+ if (this.isVirtual) {
+ var parentView = get(this, 'parentView');
+ if (parentView) { Ember.propertyDidChange(parentView, 'childViews'); }
+ }
+ }, 'childViews'),
+
+ /**
+ Return the nearest ancestor that is an instance of the provided
+ class.
+
+ @param {Class} klass Subclass of Ember.View (or Ember.View itself)
+ @returns Ember.View
+ */
+ nearestInstanceOf: function(klass) {
+ var view = get(this, 'parentView');
+
+ while (view) {
+ if(view instanceof klass) { return view; }
+ view = get(view, 'parentView');
+ }
+ },
+
+ /**
+ Return the nearest ancestor that has a given property.
+
+ @param {String} property A property name
+ @returns Ember.View
+ */
+ nearestWithProperty: function(property) {
+ var view = get(this, 'parentView');
+
+ while (view) {
+ if (property in view) { return view; }
+ view = get(view, 'parentView');
+ }
+ },
+
+ /**
+ Return the nearest ancestor whose parent is an instance of
+ `klass`.
+
+ @param {Class} klass Subclass of Ember.View (or Ember.View itself)
+ @returns Ember.View
+ */
+ nearestChildOf: function(klass) {
+ var view = get(this, 'parentView');
+
+ while (view) {
+ if(get(view, 'parentView') instanceof klass) { return view; }
+ view = get(view, 'parentView');
+ }
+ },
+
+ /**
+ Return the nearest ancestor that is an Ember.CollectionView
+
+ @returns Ember.CollectionView
+ */
+ collectionView: Ember.computed(function() {
+ return this.nearestInstanceOf(Ember.CollectionView);
+ }).cacheable(),
+
+ /**
+ Return the nearest ancestor that is a direct child of
+ an Ember.CollectionView
+
+ @returns Ember.View
+ */
+ itemView: Ember.computed(function() {
+ return this.nearestChildOf(Ember.CollectionView);
+ }).cacheable(),
+
+ /**
+ Return the nearest ancestor that has the property
+ `content`.
+
+ @returns Ember.View
+ */
+ contentView: Ember.computed(function() {
+ return this.nearestWithProperty('content');
+ }).cacheable(),
+
+ /**
+ @private
+
+ When the parent view changes, recursively invalidate
+ collectionView, itemView, and contentView
+ */
+ _parentViewDidChange: Ember.observer(function() {
+ if (this.isDestroying) { return; }
+
+ this.invokeRecursively(function(view) {
+ view.propertyDidChange('collectionView');
+ view.propertyDidChange('itemView');
+ view.propertyDidChange('contentView');
+ });
+ }, '_parentView'),
+
+ cloneKeywords: function() {
+ var templateData = get(this, 'templateData'),
+ controller = get(this, 'controller');
+
+ var keywords = templateData ? Ember.copy(templateData.keywords) : {};
+ keywords.view = get(this, 'concreteView');
+
+ // If the view has a controller specified, make it available to the
+ // template. If not, pass along the parent template's controller,
+ // if it exists.
+ if (controller) {
+ keywords.controller = controller;
+ }
+
+ return keywords;
+ },
+
+ /**
+ Called on your view when it should push strings of HTML into a
+ Ember.RenderBuffer. Most users will want to override the `template`
+ or `templateName` properties instead of this method.
+
+ By default, Ember.View will look for a function in the `template`
+ property and invoke it with the value of `templateContext`. The value of
+ `templateContext` will be the view itself unless you override it.
+
+ @param {Ember.RenderBuffer} buffer The render buffer
+ */
+ render: function(buffer) {
+ // If this view has a layout, it is the responsibility of the
+ // the layout to render the view's template. Otherwise, render the template
+ // directly.
+ var template = get(this, 'layout') || get(this, 'template');
+
+ if (template) {
+ var context = get(this, '_templateContext');
+ var keywords = this.cloneKeywords();
+
+ var data = {
+ view: this,
+ buffer: buffer,
+ isRenderData: true,
+ keywords: keywords
+ };
+
+ // Invoke the template with the provided template context, which
+ // is the view by default. A hash of data is also passed that provides
+ // the template with access to the view and render buffer.
+
+ Ember.assert('template must be a function. Did you mean to specify templateName instead?', typeof template === 'function');
+ // The template should write directly to the render buffer instead
+ // of returning a string.
+ var output = template(context, { data: data });
+
+ // If the template returned a string instead of writing to the buffer,
+ // push the string onto the buffer.
+ if (output !== undefined) { buffer.push(output); }
+ }
+ },
+
+ invokeForState: function(name) {
+ var stateName = this.state, args;
+
+ // try to find the function for the state in the cache
+ if (fn = invokeForState[stateName][name]) {
+ args = a_slice.call(arguments);
+ args[0] = this;
+
+ return fn.apply(this, args);
+ }
+
+ // otherwise, find and cache the function for this state
+ var parent = this, states = parent.states, state;
+
+ while (states) {
+ state = states[stateName];
+
+ while (state) {
+ var fn = state[name];
+
+ if (fn) {
+ invokeForState[stateName][name] = fn;
+
+ args = a_slice.call(arguments, 1);
+ args.unshift(this);
+
+ return fn.apply(this, args);
+ }
+
+ state = state.parentState;
+ }
+
+ states = states.parent;
+ }
+ },
+
+ /**
+ Renders the view again. This will work regardless of whether the
+ view is already in the DOM or not. If the view is in the DOM, the
+ rendering process will be deferred to give bindings a chance
+ to synchronize.
+
+ If children were added during the rendering process using `appendChild`,
+ `rerender` will remove them, because they will be added again
+ if needed by the next `render`.
+
+ In general, if the display of your view changes, you should modify
+ the DOM element directly instead of manually calling `rerender`, which can
+ be slow.
+ */
+ rerender: function() {
+ return this.invokeForState('rerender');
+ },
+
+ clearRenderedChildren: function() {
+ var lengthBefore = this.lengthBeforeRender,
+ lengthAfter = this.lengthAfterRender;
+
+ // If there were child views created during the last call to render(),
+ // remove them under the assumption that they will be re-created when
+ // we re-render.
+
+ // VIEW-TODO: Unit test this path.
+ var childViews = get(this, '_childViews');
+ for (var i=lengthAfter-1; i>=lengthBefore; i--) {
+ if (childViews[i]) { childViews[i].destroy(); }
+ }
+ },
+
+ /**
+ @private
+
+ Iterates over the view's `classNameBindings` array, inserts the value
+ of the specified property into the `classNames` array, then creates an
+ observer to update the view's element if the bound property ever changes
+ in the future.
+ */
+ _applyClassNameBindings: function() {
+ var classBindings = get(this, 'classNameBindings'),
+ classNames = get(this, 'classNames'),
+ elem, newClass, dasherizedClass;
+
+ if (!classBindings) { return; }
+
+ // Loop through all of the configured bindings. These will be either
+ // property names ('isUrgent') or property paths relative to the view
+ // ('content.isUrgent')
+ a_forEach(classBindings, function(binding) {
+
+ // Variable in which the old class value is saved. The observer function
+ // closes over this variable, so it knows which string to remove when
+ // the property changes.
+ var oldClass, property;
+
+ // Set up an observer on the context. If the property changes, toggle the
+ // class name.
+ var observer = function() {
+ // Get the current value of the property
+ newClass = this._classStringForProperty(binding);
+ elem = this.$();
+
+ // If we had previously added a class to the element, remove it.
+ if (oldClass) {
+ elem.removeClass(oldClass);
+ // Also remove from classNames so that if the view gets rerendered,
+ // the class doesn't get added back to the DOM.
+ classNames.removeObject(oldClass);
+ }
+
+ // If necessary, add a new class. Make sure we keep track of it so
+ // it can be removed in the future.
+ if (newClass) {
+ elem.addClass(newClass);
+ oldClass = newClass;
+ } else {
+ oldClass = null;
+ }
+ };
+
+ // Get the class name for the property at its current value
+ dasherizedClass = this._classStringForProperty(binding);
+
+ if (dasherizedClass) {
+ // Ensure that it gets into the classNames array
+ // so it is displayed when we render.
+ classNames.push(dasherizedClass);
+
+ // Save a reference to the class name so we can remove it
+ // if the observer fires. Remember that this variable has
+ // been closed over by the observer.
+ oldClass = dasherizedClass;
+ }
+
+ // Extract just the property name from bindings like 'foo:bar'
+ property = binding.split(':')[0];
+ addObserver(this, property, observer);
+ }, this);
+ },
+
+ /**
+ Iterates through the view's attribute bindings, sets up observers for each,
+ then applies the current value of the attributes to the passed render buffer.
+
+ @param {Ember.RenderBuffer} buffer
+ */
+ _applyAttributeBindings: function(buffer) {
+ var attributeBindings = get(this, 'attributeBindings'),
+ attributeValue, elem, type;
+
+ if (!attributeBindings) { return; }
+
+ a_forEach(attributeBindings, function(binding) {
+ var split = binding.split(':'),
+ property = split[0],
+ attributeName = split[1] || property;
+
+ // Create an observer to add/remove/change the attribute if the
+ // JavaScript property changes.
+ var observer = function() {
+ elem = this.$();
+ attributeValue = get(this, property);
+
+ Ember.View.applyAttributeBindings(elem, attributeName, attributeValue);
+ };
+
+ addObserver(this, property, observer);
+
+ // Determine the current value and add it to the render buffer
+ // if necessary.
+ attributeValue = get(this, property);
+ Ember.View.applyAttributeBindings(buffer, attributeName, attributeValue);
+ }, this);
+ },
+
+ /**
+ @private
+
+ Given a property name, returns a dasherized version of that
+ property name if the property evaluates to a non-falsy value.
+
+ For example, if the view has property `isUrgent` that evaluates to true,
+ passing `isUrgent` to this method will return `"is-urgent"`.
+ */
+ _classStringForProperty: function(property) {
+ var split = property.split(':'),
+ className = split[1];
+
+ property = split[0];
+
+ // TODO: Remove this `false` when the `getPath` globals support is removed
+ var val = Ember.getPath(this, property, false);
+ if (val === undefined && Ember.isGlobalPath(property)) {
+ val = Ember.getPath(window, property);
+ }
+
+ // If the value is truthy and we're using the colon syntax,
+ // we should return the className directly
+ if (!!val && className) {
+ return className;
+
+ // If value is a Boolean and true, return the dasherized property
+ // name.
+ } else if (val === true) {
+ // Normalize property path to be suitable for use
+ // as a class name. For exaple, content.foo.barBaz
+ // becomes bar-baz.
+ var parts = property.split('.');
+ return Ember.String.dasherize(parts[parts.length-1]);
+
+ // If the value is not false, undefined, or null, return the current
+ // value of the property.
+ } else if (val !== false && val !== undefined && val !== null) {
+ return val;
+
+ // Nothing to display. Return null so that the old class is removed
+ // but no new class is added.
+ } else {
+ return null;
+ }
+ },
+
+ // ..........................................................
+ // ELEMENT SUPPORT
+ //
+
+ /**
+ Returns the current DOM element for the view.
+
+ @field
+ @type DOMElement
+ */
+ element: Ember.computed(function(key, value) {
+ if (value !== undefined) {
+ return this.invokeForState('setElement', value);
+ } else {
+ return this.invokeForState('getElement');
+ }
+ }).property('_parentView').cacheable(),
+
+ /**
+ Returns a jQuery object for this view's element. If you pass in a selector
+ string, this method will return a jQuery object, using the current element
+ as its buffer.
+
+ For example, calling `view.$('li')` will return a jQuery object containing
+ all of the `li` elements inside the DOM element of this view.
+
+ @param {String} [selector] a jQuery-compatible selector string
+ @returns {Ember.CoreQuery} the CoreQuery object for the DOM node
+ */
+ $: function(sel) {
+ return this.invokeForState('$', sel);
+ },
+
+ /** @private */
+ mutateChildViews: function(callback) {
+ var childViews = get(this, '_childViews'),
+ idx = get(childViews, 'length'),
+ view;
+
+ while(--idx >= 0) {
+ view = childViews[idx];
+ callback.call(this, view, idx);
+ }
+
+ return this;
+ },
+
+ /** @private */
+ forEachChildView: function(callback) {
+ var childViews = get(this, '_childViews');
+
+ if (!childViews) { return this; }
+
+ var len = get(childViews, 'length'),
+ view, idx;
+
+ for(idx = 0; idx < len; idx++) {
+ view = childViews[idx];
+ callback.call(this, view);
+ }
+
+ return this;
+ },
+
+ /**
+ Appends the view's element to the specified parent element.
+
+ If the view does not have an HTML representation yet, `createElement()`
+ will be called automatically.
+
+ Note that this method just schedules the view to be appended; the DOM
+ element will not be appended to the given element until all bindings have
+ finished synchronizing.
+
+ This is not typically a function that you will need to call directly
+ when building your application. You might consider using Ember.ContainerView
+ instead. If you do need to use appendTo, be sure that the target element you
+ are providing is associated with an Ember.Application and does not have an
+ ancestor element that is associated with an Ember view.
+
+ @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object
+ @returns {Ember.View} receiver
+ */
+ appendTo: function(target) {
+ Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view'));
+
+ // Schedule the DOM element to be created and appended to the given
+ // element after bindings have synchronized.
+ this._insertElementLater(function() {
+ this.$().appendTo(target);
+ });
+
+ return this;
+ },
+
+ /**
+ Replaces the content of the specified parent element with this view's element.
+ If the view does not have an HTML representation yet, `createElement()`
+ will be called automatically.
+
+ Note that this method just schedules the view to be appended; the DOM
+ element will not be appended to the given element until all bindings have
+ finished synchronizing
+
+ @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object
+ @returns {Ember.View} received
+ */
+ replaceIn: function(target) {
+ Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view'));
+
+ this._insertElementLater(function() {
+ Ember.$(target).empty();
+ this.$().appendTo(target);
+ });
+
+ return this;
+ },
+
+ /**
+ @private
+
+ Schedules a DOM operation to occur during the next render phase. This
+ ensures that all bindings have finished synchronizing before the view is
+ rendered.
+
+ To use, pass a function that performs a DOM operation..
+
+ Before your function is called, this view and all child views will receive
+ the `willInsertElement` event. After your function is invoked, this view
+ and all of its child views will receive the `didInsertElement` event.
+
+ view._insertElementLater(function() {
+ this.createElement();
+ this.$().appendTo('body');
+ });
+
+ @param {Function} fn the function that inserts the element into the DOM
+ */
+ _insertElementLater: function(fn) {
+ this._lastInsert = Ember.guidFor(fn);
+ Ember.run.schedule('render', this, this.invokeForState, 'insertElement', fn);
+ },
+
+ /**
+ Appends the view's element to the document body. If the view does
+ not have an HTML representation yet, `createElement()` will be called
+ automatically.
+
+ Note that this method just schedules the view to be appended; the DOM
+ element will not be appended to the document body until all bindings have
+ finished synchronizing.
+
+ @returns {Ember.View} receiver
+ */
+ append: function() {
+ return this.appendTo(document.body);
+ },
+
+ /**
+ Removes the view's element from the element to which it is attached.
+
+ @returns {Ember.View} receiver
+ */
+ remove: function() {
+ // What we should really do here is wait until the end of the run loop
+ // to determine if the element has been re-appended to a different
+ // element.
+ // In the interim, we will just re-render if that happens. It is more
+ // important than elements get garbage collected.
+ this.destroyElement();
+ this.invokeRecursively(function(view) {
+ view.clearRenderedChildren();
+ });
+ },
+
+ /**
+ The ID to use when trying to locate the element in the DOM. If you do not
+ set the elementId explicitly, then the view's GUID will be used instead.
+ This ID must be set at the time the view is created.
+
+ @type String
+ @readOnly
+ */
+ elementId: Ember.computed(function(key, value) {
+ return value !== undefined ? value : Ember.guidFor(this);
+ }).cacheable(),
+
+ /**
+ @private
+
+ TODO: Perhaps this should be removed from the production build somehow.
+ */
+ _elementIdDidChange: Ember.beforeObserver(function() {
+ throw "Changing a view's elementId after creation is not allowed.";
+ }, 'elementId'),
+
+ /**
+ Attempts to discover the element in the parent element. The default
+ implementation looks for an element with an ID of elementId (or the view's
+ guid if elementId is null). You can override this method to provide your
+ own form of lookup. For example, if you want to discover your element
+ using a CSS class name instead of an ID.
+
+ @param {DOMElement} parentElement The parent's DOM element
+ @returns {DOMElement} The discovered element
+ */
+ findElementInParentElement: function(parentElem) {
+ var id = "#" + get(this, 'elementId');
+ return Ember.$(id)[0] || Ember.$(id, parentElem)[0];
+ },
+
+ /**
+ Creates a new renderBuffer with the passed tagName. You can override this
+ method to provide further customization to the buffer if needed. Normally
+ you will not need to call or override this method.
+
+ @returns {Ember.RenderBuffer}
+ */
+ renderBuffer: function(tagName) {
+ tagName = tagName || get(this, 'tagName');
+
+ // Explicitly check for null or undefined, as tagName
+ // may be an empty string, which would evaluate to false.
+ if (tagName === null || tagName === undefined) {
+ tagName = 'div';
+ }
+
+ return Ember.RenderBuffer(tagName);
+ },
+
+ /**
+ Creates a DOM representation of the view and all of its
+ child views by recursively calling the `render()` method.
+
+ After the element has been created, `didInsertElement` will
+ be called on this view and all of its child views.
+
+ @returns {Ember.View} receiver
+ */
+ createElement: function() {
+ if (get(this, 'element')) { return this; }
+
+ var buffer = this.renderToBuffer();
+ set(this, 'element', buffer.element());
+
+ return this;
+ },
+
+ /**
+ Called when a view is going to insert an element into the DOM.
+ */
+ willInsertElement: Ember.K,
+
+ /**
+ Called when the element of the view has been inserted into the DOM.
+ Override this function to do any set up that requires an element in the
+ document body.
+ */
+ didInsertElement: Ember.K,
+
+ /**
+ Called when the view is about to rerender, but before anything has
+ been torn down. This is a good opportunity to tear down any manual
+ observers you have installed based on the DOM state
+ */
+ willRerender: Ember.K,
+
+ /**
+ Run this callback on the current view and recursively on child views.
+
+ @private
+ */
+ invokeRecursively: function(fn) {
+ fn.call(this, this);
+
+ this.forEachChildView(function(view) {
+ view.invokeRecursively(fn);
+ });
+ },
+
+ /**
+ Invalidates the cache for a property on all child views.
+ */
+ invalidateRecursively: function(key) {
+ this.forEachChildView(function(view) {
+ view.propertyDidChange(key);
+ });
+ },
+
+ /**
+ @private
+
+ Invokes the receiver's willInsertElement() method if it exists and then
+ invokes the same on all child views.
+
+ NOTE: In some cases this was called when the element existed. This no longer
+ works so we let people know. We can remove this warning code later.
+ */
+ _notifyWillInsertElement: function() {
+ this.invokeRecursively(function(view) {
+ view.fire('willInsertElement');
+ });
+ },
+
+ /**
+ @private
+
+ Invokes the receiver's didInsertElement() method if it exists and then
+ invokes the same on all child views.
+ */
+ _notifyDidInsertElement: function() {
+ this.invokeRecursively(function(view) {
+ view.fire('didInsertElement');
+ });
+ },
+
+ /**
+ @private
+
+ Invokes the receiver's willRerender() method if it exists and then
+ invokes the same on all child views.
+ */
+ _notifyWillRerender: function() {
+ this.invokeRecursively(function(view) {
+ view.fire('willRerender');
+ });
+ },
+
+ /**
+ Destroys any existing element along with the element for any child views
+ as well. If the view does not currently have a element, then this method
+ will do nothing.
+
+ If you implement willDestroyElement() on your view, then this method will
+ be invoked on your view before your element is destroyed to give you a
+ chance to clean up any event handlers, etc.
+
+ If you write a willDestroyElement() handler, you can assume that your
+ didInsertElement() handler was called earlier for the same element.
+
+ Normally you will not call or override this method yourself, but you may
+ want to implement the above callbacks when it is run.
+
+ @returns {Ember.View} receiver
+ */
+ destroyElement: function() {
+ return this.invokeForState('destroyElement');
+ },
+
+ /**
+ Called when the element of the view is going to be destroyed. Override
+ this function to do any teardown that requires an element, like removing
+ event listeners.
+ */
+ willDestroyElement: function() {},
+
+ /**
+ @private
+
+ Invokes the `willDestroyElement` callback on the view and child views.
+ */
+ _notifyWillDestroyElement: function() {
+ this.invokeRecursively(function(view) {
+ view.fire('willDestroyElement');
+ });
+ },
+
+ /** @private (nodoc) */
+ _elementWillChange: Ember.beforeObserver(function() {
+ this.forEachChildView(function(view) {
+ Ember.propertyWillChange(view, 'element');
+ });
+ }, 'element'),
+
+ /**
+ @private
+
+ If this view's element changes, we need to invalidate the caches of our
+ child views so that we do not retain references to DOM elements that are
+ no longer needed.
+
+ @observes element
+ */
+ _elementDidChange: Ember.observer(function() {
+ this.forEachChildView(function(view) {
+ Ember.propertyDidChange(view, 'element');
+ });
+ }, 'element'),
+
+ /**
+ Called when the parentView property has changed.
+
+ @function
+ */
+ parentViewDidChange: Ember.K,
+
+ /**
+ @private
+
+ Invoked by the view system when this view needs to produce an HTML
+ representation. This method will create a new render buffer, if needed,
+ then apply any default attributes, such as class names and visibility.
+ Finally, the `render()` method is invoked, which is responsible for
+ doing the bulk of the rendering.
+
+ You should not need to override this method; instead, implement the
+ `template` property, or if you need more control, override the `render`
+ method.
+
+ @param {Ember.RenderBuffer} buffer the render buffer. If no buffer is
+ passed, a default buffer, using the current view's `tagName`, will
+ be used.
+ */
+ renderToBuffer: function(parentBuffer, bufferOperation) {
+ var buffer;
+
+ Ember.run.sync();
+
+ // Determine where in the parent buffer to start the new buffer.
+ // By default, a new buffer will be appended to the parent buffer.
+ // The buffer operation may be changed if the child views array is
+ // mutated by Ember.ContainerView.
+ bufferOperation = bufferOperation || 'begin';
+
+ // If this is the top-most view, start a new buffer. Otherwise,
+ // create a new buffer relative to the original using the
+ // provided buffer operation (for example, `insertAfter` will
+ // insert a new buffer after the "parent buffer").
+ if (parentBuffer) {
+ var tagName = get(this, 'tagName');
+ if (tagName === null || tagName === undefined) {
+ tagName = 'div';
+ }
+
+ buffer = parentBuffer[bufferOperation](tagName);
+ } else {
+ buffer = this.renderBuffer();
+ }
+
+ this.buffer = buffer;
+ this.transitionTo('inBuffer', false);
+
+ this.lengthBeforeRender = get(get(this, '_childViews'), 'length');
+
+ this.beforeRender(buffer);
+ this.render(buffer);
+ this.afterRender(buffer);
+
+ this.lengthAfterRender = get(get(this, '_childViews'), 'length');
+
+ return buffer;
+ },
+
+ beforeRender: function(buffer) {
+ this.applyAttributesToBuffer(buffer);
+ },
+
+ afterRender: Ember.K,
+
+ /**
+ @private
+ */
+ applyAttributesToBuffer: function(buffer) {
+ // Creates observers for all registered class name and attribute bindings,
+ // then adds them to the element.
+ this._applyClassNameBindings();
+
+ // Pass the render buffer so the method can apply attributes directly.
+ // This isn't needed for class name bindings because they use the
+ // existing classNames infrastructure.
+ this._applyAttributeBindings(buffer);
+
+
+ a_forEach(get(this, 'classNames'), function(name){ buffer.addClass(name); });
+ buffer.id(get(this, 'elementId'));
+
+ var role = get(this, 'ariaRole');
+ if (role) {
+ buffer.attr('role', role);
+ }
+
+ if (get(this, 'isVisible') === false) {
+ buffer.style('display', 'none');
+ }
+ },
+
+ // ..........................................................
+ // STANDARD RENDER PROPERTIES
+ //
+
+ /**
+ Tag name for the view's outer element. The tag name is only used when
+ an element is first created. If you change the tagName for an element, you
+ must destroy and recreate the view element.
+
+ By default, the render buffer will use a `
` tag for views.
+
+ @type String
+ @default null
+ */
+
+ // We leave this null by default so we can tell the difference between
+ // the default case and a user-specified tag.
+ tagName: null,
+
+ /**
+ The WAI-ARIA role of the control represented by this view. For example, a
+ button may have a role of type 'button', or a pane may have a role of
+ type 'alertdialog'. This property is used by assistive software to help
+ visually challenged users navigate rich web applications.
+
+ The full list of valid WAI-ARIA roles is available at:
+ http://www.w3.org/TR/wai-aria/roles#roles_categorization
+
+ @type String
+ @default null
+ */
+ ariaRole: null,
+
+ /**
+ Standard CSS class names to apply to the view's outer element. This
+ property automatically inherits any class names defined by the view's
+ superclasses as well.
+
+ @type Array
+ @default ['ember-view']
+ */
+ classNames: ['ember-view'],
+
+ /**
+ A list of properties of the view to apply as class names. If the property
+ is a string value, the value of that string will be applied as a class
+ name.
+
+ // Applies the 'high' class to the view element
+ Ember.View.create({
+ classNameBindings: ['priority']
+ priority: 'high'
+ });
+
+ If the value of the property is a Boolean, the name of that property is
+ added as a dasherized class name.
+
+ // Applies the 'is-urgent' class to the view element
+ Ember.View.create({
+ classNameBindings: ['isUrgent']
+ isUrgent: true
+ });
+
+ If you would prefer to use a custom value instead of the dasherized
+ property name, you can pass a binding like this:
+
+ // Applies the 'urgent' class to the view element
+ Ember.View.create({
+ classNameBindings: ['isUrgent:urgent']
+ isUrgent: true
+ });
+
+ This list of properties is inherited from the view's superclasses as well.
+
+ @type Array
+ @default []
+ */
+ classNameBindings: [],
+
+ /**
+ A list of properties of the view to apply as attributes. If the property is
+ a string value, the value of that string will be applied as the attribute.
+
+ // Applies the type attribute to the element
+ // with the value "button", like
+ Ember.View.create({
+ attributeBindings: ['type'],
+ type: 'button'
+ });
+
+ If the value of the property is a Boolean, the name of that property is
+ added as an attribute.
+
+ // Renders something like
+
+ Removing a view
+
+ aContainer.get('childViews') // [aContainer.aView, aContainer.bView]
+ aContainer.get('childViews').removeObject(aContainer.get('bView'))
+ aContainer.get('childViews') // [aContainer.aView]
+
+ Will result in the following HTML
+
+
+
A
+
+
+
+ Similarly, adding a child view is accomplished by adding `Ember.View` instances to the
+ container's `childViews` property.
+
+ Given an empty `` the following code
+
+ aContainer = Ember.ContainerView.create({
+ classNames: ['the-container'],
+ childViews: ['aView', 'bView'],
+ aView: Ember.View.create({
+ template: Ember.Handlebars.compile("A")
+ }),
+ bView: Ember.View.create({
+ template: Ember.Handlebars.compile("B")
+ })
+ })
+
+ aContainer.appendTo('body')
+
+ Results in the HTML
+
+
+
A
+
B
+
+
+ Adding a view
+
+ AnotherViewClass = Ember.View.extend({
+ template: Ember.Handlebars.compile("Another view")
+ })
+
+ aContainer.get('childViews') // [aContainer.aView, aContainer.bView]
+ aContainer.get('childViews').pushObject(AnotherViewClass.create())
+ aContainer.get('childViews') // [aContainer.aView, ]
+
+ Will result in the following HTML
+
+
+
A
+
Another view
+
+
+
+ Direct manipulation of childViews presence or absence in the DOM via calls to
+ `remove` or `removeFromParent` or calls to a container's `removeChild` may not behave
+ correctly.
+
+ Calling `remove()` on a child view will remove the view's HTML, but it will remain as part of its
+ container's `childView`s property.
+
+ Calling `removeChild()` on the container will remove the passed view instance from the container's
+ `childView`s but keep its HTML within the container's rendered view.
+
+ Calling `removeFromParent()` behaves as expected but should be avoided in favor of direct
+ manipulation of a container's `childViews` property.
+
+ aContainer = Ember.ContainerView.create({
+ classNames: ['the-container'],
+ childViews: ['aView', 'bView'],
+ aView: Ember.View.create({
+ template: Ember.Handlebars.compile("A")
+ }),
+ bView: Ember.View.create({
+ template: Ember.Handlebars.compile("B")
+ })
+ })
+
+ aContainer.appendTo('body')
+
+ Results in the HTML
+
+
+
A
+
B
+
+
+ Calling `aContainer.get('aView').removeFromParent()` will result in the following HTML
+
+
+
B
+
+
+ And the `Ember.View` instance stored in `aContainer.aView` will be removed from `aContainer`'s
+ `childViews` array.
+
+ ## Templates and Layout
+ A `template`, `templateName`, `defaultTemplate`, `layout`, `layoutName` or `defaultLayout`
+ property on a container view will not result in the template or layout being rendered.
+ The HTML contents of a `Ember.ContainerView`'s DOM representation will only be the rendered HTML
+ of its child views.
+
+ ## Binding a View to Display
+
+ If you would like to display a single view in your ContainerView, you can set its `currentView`
+ property. When the `currentView` property is set to a view instance, it will be added to the
+ ContainerView's `childViews` array. If the `currentView` property is later changed to a
+ different view, the new view will replace the old view. If `currentView` is set to `null`, the
+ last `currentView` will be removed.
+
+ This functionality is useful for cases where you want to bind the display of a ContainerView to
+ a controller or state manager. For example, you can bind the `currentView` of a container to
+ a controller like this:
+
+ // Controller
+ App.appController = Ember.Object.create({
+ view: Ember.View.create({
+ templateName: 'person_template'
+ })
+ });
+
+ // Handlebars template
+ {{view Ember.ContainerView currentViewBinding="App.appController.view"}}
+
+ @extends Ember.View
+*/
+
+Ember.ContainerView = Ember.View.extend({
+
+ init: function() {
+ var childViews = get(this, 'childViews');
+ Ember.defineProperty(this, 'childViews', childViewsProperty);
+
+ this._super();
+
+ var _childViews = get(this, '_childViews');
+
+ forEach(childViews, function(viewName, idx) {
+ var view;
+
+ if ('string' === typeof viewName) {
+ view = get(this, viewName);
+ view = this.createChildView(view);
+ set(this, viewName, view);
+ } else {
+ view = this.createChildView(viewName);
+ }
+
+ _childViews[idx] = view;
+ }, this);
+
+ // Make the _childViews array observable
+ Ember.A(_childViews);
+
+ // Sets up an array observer on the child views array. This
+ // observer will detect when child views are added or removed
+ // and update the DOM to reflect the mutation.
+ get(this, 'childViews').addArrayObserver(this, {
+ willChange: 'childViewsWillChange',
+ didChange: 'childViewsDidChange'
+ });
+ },
+
+ /**
+ Instructs each child view to render to the passed render buffer.
+
+ @param {Ember.RenderBuffer} buffer the buffer to render to
+ @private
+ */
+ render: function(buffer) {
+ this.forEachChildView(function(view) {
+ view.renderToBuffer(buffer);
+ });
+ },
+
+ /**
+ When the container view is destroyed, tear down the child views
+ array observer.
+
+ @private
+ */
+ willDestroy: function() {
+ get(this, 'childViews').removeArrayObserver(this, {
+ willChange: 'childViewsWillChange',
+ didChange: 'childViewsDidChange'
+ });
+
+ this._super();
+ },
+
+ /**
+ When a child view is removed, destroy its element so that
+ it is removed from the DOM.
+
+ The array observer that triggers this action is set up in the
+ `renderToBuffer` method.
+
+ @private
+ @param {Ember.Array} views the child views array before mutation
+ @param {Number} start the start position of the mutation
+ @param {Number} removed the number of child views removed
+ **/
+ childViewsWillChange: function(views, start, removed) {
+ if (removed === 0) { return; }
+
+ var changedViews = views.slice(start, start+removed);
+ this.initializeViews(changedViews, null, null);
+
+ this.invokeForState('childViewsWillChange', views, start, removed);
+ },
+
+ /**
+ When a child view is added, make sure the DOM gets updated appropriately.
+
+ If the view has already rendered an element, we tell the child view to
+ create an element and insert it into the DOM. If the enclosing container view
+ has already written to a buffer, but not yet converted that buffer into an
+ element, we insert the string representation of the child into the appropriate
+ place in the buffer.
+
+ @private
+ @param {Ember.Array} views the array of child views afte the mutation has occurred
+ @param {Number} start the start position of the mutation
+ @param {Number} removed the number of child views removed
+ @param {Number} the number of child views added
+ */
+ childViewsDidChange: function(views, start, removed, added) {
+ var len = get(views, 'length');
+
+ // No new child views were added; bail out.
+ if (added === 0) return;
+
+ var changedViews = views.slice(start, start+added);
+ this.initializeViews(changedViews, this, get(this, 'templateData'));
+
+ // Let the current state handle the changes
+ this.invokeForState('childViewsDidChange', views, start, added);
+ },
+
+ initializeViews: function(views, parentView, templateData) {
+ forEach(views, function(view) {
+ set(view, '_parentView', parentView);
+
+ if (!get(view, 'templateData')) {
+ set(view, 'templateData', templateData);
+ }
+ });
+ },
+
+ /**
+ Schedules a child view to be inserted into the DOM after bindings have
+ finished syncing for this run loop.
+
+ @param {Ember.View} view the child view to insert
+ @param {Ember.View} prev the child view after which the specified view should
+ be inserted
+ @private
+ */
+ _scheduleInsertion: function(view, prev) {
+ if (prev) {
+ prev.domManager.after(prev, view);
+ } else {
+ this.domManager.prepend(this, view);
+ }
+ },
+
+ currentView: null,
+
+ _currentViewWillChange: Ember.beforeObserver(function() {
+ var childViews = get(this, 'childViews'),
+ currentView = get(this, 'currentView');
+
+ if (currentView) {
+ childViews.removeObject(currentView);
+ }
+ }, 'currentView'),
+
+ _currentViewDidChange: Ember.observer(function() {
+ var childViews = get(this, 'childViews'),
+ currentView = get(this, 'currentView');
+
+ if (currentView) {
+ childViews.pushObject(currentView);
+ }
+ }, 'currentView')
+});
+
+// Ember.ContainerView extends the default view states to provide different
+// behavior for childViewsWillChange and childViewsDidChange.
+Ember.ContainerView.states = {
+ parent: Ember.View.states,
+
+ inBuffer: {
+ childViewsDidChange: function(parentView, views, start, added) {
+ var buffer = parentView.buffer,
+ startWith, prev, prevBuffer, view;
+
+ // Determine where to begin inserting the child view(s) in the
+ // render buffer.
+ if (start === 0) {
+ // If views were inserted at the beginning, prepend the first
+ // view to the render buffer, then begin inserting any
+ // additional views at the beginning.
+ view = views[start];
+ startWith = start + 1;
+ view.renderToBuffer(buffer, 'prepend');
+ } else {
+ // Otherwise, just insert them at the same place as the child
+ // views mutation.
+ view = views[start - 1];
+ startWith = start;
+ }
+
+ for (var i=startWith; i` and the following code:
+
+
+ someItemsView = Ember.CollectionView.create({
+ classNames: ['a-collection'],
+ content: ['A','B','C'],
+ itemViewClass: Ember.View.extend({
+ template: Ember.Handlebars.compile("the letter: {{content}}")
+ })
+ })
+
+ someItemsView.appendTo('body')
+
+ Will result in the following HTML structure
+
+
+
the letter: A
+
the letter: B
+
the letter: C
+
+
+
+ ## Automatic matching of parent/child tagNames
+ Setting the `tagName` property of a `CollectionView` to any of
+ "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result
+ in the item views receiving an appropriately matched `tagName` property.
+
+
+ Given an empty `` and the following code:
+
+ anUndorderedListView = Ember.CollectionView.create({
+ tagName: 'ul',
+ content: ['A','B','C'],
+ itemViewClass: Ember.View.extend({
+ template: Ember.Handlebars.compile("the letter: {{content}}")
+ })
+ })
+
+ anUndorderedListView.appendTo('body')
+
+ Will result in the following HTML structure
+
+
+
the letter: A
+
the letter: B
+
the letter: C
+
+
+ Additional tagName pairs can be provided by adding to `Ember.CollectionView.CONTAINER_MAP `
+
+ Ember.CollectionView.CONTAINER_MAP['article'] = 'section'
+
+
+ ## Empty View
+ You can provide an `Ember.View` subclass to the `Ember.CollectionView` instance as its
+ `emptyView` property. If the `content` property of a `CollectionView` is set to `null`
+ or an empty array, an instance of this view will be the `CollectionView`s only child.
+
+ aListWithNothing = Ember.CollectionView.create({
+ classNames: ['nothing']
+ content: null,
+ emptyView: Ember.View.extend({
+ template: Ember.Handlebars.compile("The collection is empty")
+ })
+ })
+
+ aListWithNothing.appendTo('body')
+
+ Will result in the following HTML structure
+
+
')
+ })
+
+ viewStates = Ember.StateManager.create({
+ showingPeople: Ember.ViewState.create({
+ view: ContactListView
+ }),
+ showingPhotos: Ember.ViewState.create({
+ view: PhotoListView
+ })
+ })
+
+ viewStates.goToState('showingPeople')
+
+ The above code will change the rendered HTML from
+
+
+
+ to
+
+
+
+
People
+
+
+
+ Changing the current state via `goToState` from `showingPeople` to
+ `showingPhotos` will remove the `showingPeople` view and add the `showingPhotos` view:
+
+ viewStates.goToState('showingPhotos')
+
+ will change the rendered HTML to
+
+
+
+
Photos
+
+
+
+
+ When entering nested `ViewState`s, each state's view will be draw into the the StateManager's
+ `rootView` or `rootElement` as siblings.
+
+
+ ContactListView = Ember.View.extend({
+ classNames: ['my-contacts-css-class'],
+ template: Ember.Handlebars.compile('
+
+
+
+ ViewState views are added and removed from their StateManager's view via their
+ `enter` and `exit` methods. If you need to override these methods, be sure to call
+ `_super` to maintain the adding and removing behavior:
+
+ viewStates = Ember.StateManager.create({
+ aState: Ember.ViewState.create({
+ view: Ember.View.extend({}),
+ enter: function(manager, transition){
+ // calling _super ensures this view will be
+ // properly inserted
+ this._super(manager, transition);
+
+ // now you can do other things
+ }
+ })
+ })
+
+ ## Managing Multiple Sections of A Page With States
+ Multiple StateManagers can be combined to control multiple areas of an application's rendered views.
+ Given the following HTML body:
+
+
+
" ],
+ map: [ 1, "" ],
+ _default: [ 0, "", "" ]
+ };
+
+ /**
+ * Given a parent node and some HTML, generate a set of nodes. Return the first
+ * node, which will allow us to traverse the rest using nextSibling.
+ *
+ * We need to do this because innerHTML in IE does not really parse the nodes.
+ **/
+ var firstNodeFor = function(parentNode, html) {
+ var arr = wrapMap[parentNode.tagName.toLowerCase()] || wrapMap._default;
+ var depth = arr[0], start = arr[1], end = arr[2];
+
+ if (needsShy) { html = ''+html; }
+
+ var element = document.createElement('div');
+ element.innerHTML = start + html + end;
+
+ for (var i=0; i<=depth; i++) {
+ element = element.firstChild;
+ }
+
+ // Look for to remove it.
+ if (needsShy) {
+ var shyElement = element;
+
+ // Sometimes we get nameless elements with the shy inside
+ while (shyElement.nodeType === 1 && !shyElement.nodeName) {
+ shyElement = shyElement.firstChild;
+ }
+
+ // At this point it's the actual unicode character.
+ if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") {
+ shyElement.nodeValue = shyElement.nodeValue.slice(1);
+ }
+ }
+
+ return element;
+ };
+
+ /**
+ * In some cases, Internet Explorer can create an anonymous node in
+ * the hierarchy with no tagName. You can create this scenario via:
+ *
+ * div = document.createElement("div");
+ * div.innerHTML = "
­
hi
";
+ * div.firstChild.firstChild.tagName //=> ""
+ *
+ * If our script markers are inside such a node, we need to find that
+ * node and use *it* as the marker.
+ **/
+ var realNode = function(start) {
+ while (start.parentNode.tagName === "") {
+ start = start.parentNode;
+ }
+
+ return start;
+ };
+
+ /**
+ * When automatically adding a tbody, Internet Explorer inserts the
+ * tbody immediately before the first
. Other browsers create it
+ * before the first node, no matter what.
+ *
+ * This means the the following code:
+ *
+ * div = document.createElement("div");
+ * div.innerHTML = "
+
+
+ ### parentView setting
+
+ The `parentView` property of the new `Ember.View` instance created through `{{view}}`
+ will be set to the `Ember.View` instance of the template where `{{view}}` was called.
+
+ aView = Ember.View.create({
+ template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}")
+ })
+
+ aView.appendTo('body')
+
+ Will result in HTML structure:
+
+
+
+ my parent: ember1
+
+
+
+ ### Setting CSS id and class attributes
+
+ The HTML `id` attribute can be set on the `{{view}}`'s resulting element with the `id` option.
+ This option will _not_ be passed to `Ember.View.create`.
+
+
+
+ Results in the following HTML structure:
+
+
+
+ hello.
+
+
+
+ The HTML `class` attribute can be set on the `{{view}}`'s resulting element with
+ the `class` or `classNameBindings` options. The `class` option
+ will directly set the CSS `class` attribute and will not be passed to
+ `Ember.View.create`. `classNameBindings` will be passed to `create` and use
+ `Ember.View`'s class name binding functionality:
+
+
+
+ Results in the following HTML structure:
+
+
+
+ hello.
+
+
+
+ ### Supplying a different view class
+ `{{view}}` can take an optional first argument before its supplied options to specify a
+ path to a custom view class.
+
+
+
+ The first argument can also be a relative path. Ember will search for the view class
+ starting at the `Ember.View` of the template where `{{view}}` was used as the root object:
+
+ MyApp = Ember.Application.create({})
+ MyApp.OuterView = Ember.View.extend({
+ innerViewClass: Ember.View.extend({
+ classNames: ['a-custom-view-class-as-property']
+ }),
+ template: Ember.Handlebars.compile('{{#view "innerViewClass"}} hi {{/view}}')
+ })
+
+ MyApp.OuterView.create().appendTo('body')
+
+Will result in the following HTML:
+
+
+
+ ### Blockless Use
+ If you provide an `itemViewClass` option that has its own `template` you can omit
+ the block.
+
+ The following template:
+
+
+
+ And application code
+
+ App = Ember.Application.create()
+ App.items = [
+ Ember.Object.create({name: 'Dave'}),
+ Ember.Object.create({name: 'Mary'}),
+ Ember.Object.create({name: 'Sara'})
+ ]
+
+ App.AnItemView = Ember.View.extend({
+ template: Ember.Handlebars.compile("Greetings {{content.name}}")
+ })
+
+ Will result in the HTML structure below
+
+
+
Greetings Dave
+
Greetings Mary
+
Greetings Sara
+
+
+ ### Specifying a CollectionView subclass
+ By default the `{{collection}}` helper will create an instance of `Ember.CollectionView`.
+ You can supply a `Ember.CollectionView` subclass to the helper by passing it
+ as the first argument:
+
+
+
+
+ ### Forwarded `item.*`-named Options
+ As with the `{{view}}`, helper options passed to the `{{collection}}` will be set on
+ the resulting `Ember.CollectionView` as properties. Additionally, options prefixed with
+ `item` will be applied to the views rendered for each item (note the camelcasing):
+
+
+
+ Will result in the following HTML structure:
+
+
+
+ Clicking "click me" will trigger the `anActionName` method of the `aView` object with a
+ `jQuery.Event` object as its argument. The `jQuery.Event` object will be extended to include
+ a `view` property that is set to the original view interacted with (in this case the `aView` object).
+
+
+ ### Specifying an Action Target
+ A `target` option can be provided to change which object will receive the method call. This option must be
+ a string representing a path to an object:
+
+
+
+ Clicking "click me" in the rendered HTML of the above template will trigger the
+ `anActionName` method of the object at `MyApplication.someObject`. The first argument
+ to this method will be a `jQuery.Event` extended to include a `view` property that is
+ set to the original view interacted with.
+
+ A path relative to the template's `Ember.View` instance can also be used as a target:
+
+
+
+ Clicking "click me" in the rendered HTML of the above template will trigger the
+ `anActionName` method of the view's parent view.
+
+ The `{{action}}` helper is `Ember.StateManager` aware. If the target of
+ the action is an `Ember.StateManager` instance `{{action}}` will use the `send`
+ functionality of StateManagers. The documentation for `Ember.StateManager` has additional
+ information about this use.
+
+ If an action's target does not implement a method that matches the supplied action name
+ an error will be thrown.
+
+
+
+ With the following application code
+
+ AView = Ember.View.extend({
+ templateName; 'a-template',
+ // note: no method 'aMethodNameThatIsMissing'
+ anActionName: function(event){}
+ })
+
+ aView = AView.create()
+ aView.appendTo('body')
+
+ Will throw `Uncaught TypeError: Cannot call method 'call' of undefined` when "click me" is clicked.
+
+
+ ### Specifying DOM event type
+ By default the `{{action}}` helper registers for DOM `click` events. You can supply an
+ `on` option to the helper to specify a different DOM event name:
+
+
+
+ See `Ember.EventDispatcher` for a list of acceptable DOM event names.
+
+ Because `{{action}}` depends on Ember's event dispatch system it will only function if
+ an `Ember.EventDispatcher` instance is available. An `Ember.EventDispatcher` instance
+ will be created when a new `Ember.Application` is created. Having an instance of
+ `Ember.Application` will satisfy this requirement.
+
+ ### Specifying a context
+ By default the `{{action}}` helper passes the current Handlebars context along in the
+ `jQuery.Event` object. You may specify an alternative object to pass as the context by
+ providing a property path:
+
+
+
+ @name Handlebars.helpers.action
+ @param {String} actionName
+ @param {Hash} options
+*/
+EmberHandlebars.registerHelper('action', function(actionName, options) {
+ var hash = options.hash || {},
+ eventName = hash.on || "click",
+ view = options.data.view,
+ target, context, controller;
+
+ if (view.isVirtual) { view = view.get('parentView'); }
+
+ if (hash.target) {
+ target = getPath(this, hash.target, options);
+ } else if (controller = options.data.keywords.controller) {
+ target = get(controller, 'target');
+ }
+
+ target = target || view;
+
+ context = hash.context ? getPath(this, hash.context, options) : options.contexts[0];
+
+ var actionId = ActionHelper.registerAction(actionName, eventName, target, view, context);
+ return new EmberHandlebars.SafeString('data-ember-action="' + actionId + '"');
+});
+
+})();
+
+
+
+(function() {
+var get = Ember.get, set = Ember.set;
+
+/**
+
+ When used in a Handlebars template that is assigned to an `Ember.View` instance's
+ `layout` property Ember will render the layout template first, inserting the view's
+ own rendered output at the `{{ yield }}` location.
+
+ An empty `` and the following application code:
+
+ AView = Ember.View.extend({
+ classNames: ['a-view-with-layout'],
+ layout: Ember.Handlebars.compile('
{{ yield }}
'),
+ template: Ember.Handlebars.compile('I am wrapped')
+ })
+
+ aView = AView.create()
+ aView.appendTo('body')
+
+ Will result in the following HTML output:
+
+
+
\ No newline at end of file
+
diff --git a/src/templates/settings.tpl b/src/templates/settings.tpl
index 26dc531..dbcca10 100644
--- a/src/templates/settings.tpl
+++ b/src/templates/settings.tpl
@@ -1,6 +1,5 @@
-
-
Zabbix Notifier: Settings
+
Zabbix Watcher: Settings
@@ -39,4 +38,4 @@
-
\ No newline at end of file
+
diff --git a/src/templates/templates.js b/src/templates/templates.js
index 6fdafab..8a17c27 100644
--- a/src/templates/templates.js
+++ b/src/templates/templates.js
@@ -1,135 +1,91 @@
(function() {
- var template = Handlebars.template, templates = Ember.TEMPLATES = Handlebars.templates || {};
-templates['notification'] = template(function (Handlebars,depth0,helpers,partials,data) {
- helpers = helpers || Handlebars.helpers;
- var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+ var templates = Ember.TEMPLATES = Handlebars.templates || {};
-function program1(depth0,data) {
-
- var buffer = "", stack1;
- buffer += "\n