blob: 2bc552b9931d4b3c79afd8c52e8b49ecc1682b95 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
define(function(require){
var App = require('App');
var vBreadCrumbs = require('views/common/BreadCrumbs');
var XAEnums = require('utils/XAEnums');
var XAUtil = require('utils/XAUtils');
require('Backbone.BootstrapModal')
require('backgrid');
require('jquery-toggles');
Backbone.history.getHash = function(window) {
$('.latestResponse').html('<b>Last Response Time : </b>' + Globalize.format(new Date(), "MM/dd/yyyy hh:mm:ss tt"));
var pathStripper = /#.*$/;
var match = (window || this).location.href.match(/#(.*)$/);
return match ? this.decodeFragment(match[1].replace(pathStripper, '')) : '';
};
window.onbeforeunload = function(e) {
if (window._preventNavigation) {
var message = 'Are you sure you want to refresh the page? Unsaved changes will be lost.';
if (typeof e == 'undefined') {
e = window.event;
}
if (e) {
e.returnValue = message;
}
return message;
}
};
/**
HtmlCell renders any html code
@class Backgrid.HtmlCell
@extends Backgrid.Cell
*/
var HtmlCell = Backgrid.HtmlCell = Backgrid.Cell.extend({
/** @property */
className: "html-cell",
render: function () {
this.$el.empty();
var rawValue = this.model.get(this.column.get("name"));
var formattedValue = this.formatter.fromRaw(rawValue, this.model);
this.$el.append(formattedValue);
this.delegateEvents();
return this;
}
});
/**
SwitchCell renders Switch Button
@class Backgrid.SwitchCell
@extends Backgrid.Cell
*/
var SwitchCell = Backgrid.SwitchCell = Backgrid.Cell.extend({
/** @property */
className: "switch-cell",
initialize: function (options) {
UriCell.__super__.initialize.apply(this, arguments);
this.switchStatus = options.switchStatus || false;
this.click = this.column.get('click') || false;
this.drag = this.column.get('drag') || false;
this.onText = this.column.get('onText') || 'ON';
this.offText = this.column.get('offText') || 'OFF';
if (this.column.get("cellClass")) this.$el.addClass(this.column.get("cellClass"));
},
render: function () {
this.$el.empty();
if(this.model.get(this.column.get("name")) != undefined){
var rawValue = (this.model.get(this.column.get("name")));
this.switchStatus = this.formatter.fromRaw(rawValue, this.model);
}
this.$el.append('<div class="toggle-xa"><div class="toggle"></div></div>');
this.$el.find('.toggle').toggles({on : this.switchStatus,click :this.click,drag:this.drag,
text : {on :this.onText ,off : this.offText}
});
this.delegateEvents();
return this;
}
});
var UriCell = Backgrid.UriCell = Backgrid.Cell.extend({
className: "uri-cell",
title: null,
target: "_blank",
initialize: function (options) {
UriCell.__super__.initialize.apply(this, arguments);
this.title = options.title || this.title;
this.target = options.target || this.target;
},
render: function () {
this.$el.empty();
var rawValue = this.model.get(this.column.get("name"));
var href = _.isFunction(this.column.get("href")) ? this.column.get('href')(this.model) : this.column.get('href');
var klass = this.column.get("klass");
var formattedValue = this.formatter.fromRaw(rawValue, this.model);
this.$el.append($("<a>", {
tabIndex: -1,
href: href,
title: this.title || formattedValue,
'class' : klass
}).text(formattedValue));
if(this.column.has("iconKlass")){
var iconKlass = this.column.get("iconKlass");
var iconTitle = this.column.get("iconTitle");
this.$el.find('a').append('<i class="'+iconKlass+'" title="'+iconTitle+'"></i>');
}
this.delegateEvents();
return this;
}
});
/**
Renders a checkbox for Provision Table Cell.
@class Backgrid.SelectCell
@extends Backgrid.Cell
*/
Backgrid.SelectCell = Backgrid.Cell.extend({
/** @property */
className: "select-cell",
/** @property */
tagName: "td",
/** @property */
events: {
//"keydown input[type=checkbox]": "onKeydown",
"change input[type=checkbox]": "onChange",
"click input[type=checkbox]": "enterEditMode"
},
/**
Initializer. If the underlying model triggers a `select` event, this cell
will change its checked value according to the event's `selected` value.
@param {Object} options
@param {Backgrid.Column} options.column
@param {Backbone.Model} options.model
*/
initialize: function (options) {
this.column = options.column;
if (!(this.column instanceof Backgrid.Column)) {
this.column = new Backgrid.Column(this.column);
}
if(!this.column.has("enabledVal")){
this.column.set("enabledVal", "true"); // it is not a boolean value for EPM
this.column.set("disabledVal", "false");
}
var column = this.column, model = this.model, $el = this.$el;
this.listenTo(column, "change:renderable", function (column, renderable) {
$el.toggleClass("renderable", renderable);
});
if (Backgrid.callByNeed(column.renderable(), column, model)) $el.addClass("renderable");
this.listenTo(model, "change:" + column.get("name"), function () {
if (!$el.hasClass("editor")) this.render();
});
this.listenTo(model, "backgrid:select", function (model, selected) {
this.$el.find("input[type=checkbox]").prop("checked", selected).change();
});
},
/**
Focuses the checkbox.
*/
enterEditMode: function () {
this.$el.find("input[type=checkbox]").focus();
},
/**
Unfocuses the checkbox.
*/
exitEditMode: function () {
this.$el.find("input[type=checkbox]").blur();
},
/**
When the checkbox's value changes, this method will trigger a Backbone
`backgrid:selected` event with a reference of the model and the
checkbox's `checked` value.
*/
onChange: function () {
var checked = this.$el.find("input[type=checkbox]").prop("checked");
//this.$el.parent().toggleClass("selected", checked);
if(checked)
this.model.set(this.column.get("name"), XAEnums.ActivationStatus.ACT_STATUS_ACTIVE.value);
else
this.model.set(this.column.get("name"), XAEnums.ActivationStatus.ACT_STATUS_DISABLED.value);
this.model.trigger("backgrid:selected", this.model, checked);
},
/**
Renders a checkbox in a table cell.
*/
render: function () {
var model = this.model, column = this.column;
var val = (model.get(column.get("name")) === column.get("enabledVal") ||
model.get(column.get("name")) === XAEnums.ActivationStatus.ACT_STATUS_ACTIVE.value) ? true : false;
// this.$el.empty().append('<input tabindex="-1" type="checkbox" />');
this.$el.empty();
// this.$el.find("input[type=checkbox]").prop('checked', val);
this.$el.append($("<input>", {
tabIndex: -1,
type: "checkbox",
checked: val
}));
this.delegateEvents();
return this;
}
});
/**
* SELECT2
*
* Renders Select2 - jQuery based replacement for select boxes
*
* Requires an 'options.values' value on the schema.
* Can be an array of options, a function that calls back with the array of options, a string of HTML
* or a Backbone collection. If a collection, the models must implement a toString() method
*/
var Form = require('backbone-forms');
require('select2');
Form.editors.Select2 = Form.editors.Select.extend({
initialize : function(options){
this.pluginAttr = _.extend( {'width' : 'resolve'}, options.schema.pluginAttr || {});
Form.editors.Select.prototype.initialize.call(this,options);
},
render: function() {
var self = this;
// this.setOptions(this.schema.options);
setTimeout(function () {
self.$el.select2(self.pluginAttr);
},0);
return this;
}
});
/**
* TOGGLE SWITCH
* https://github.com/simontabor/jquery-toggles
*
*/
Form.editors.Switch = Form.editors.Base.extend({
ui : {
},
events: {
'click': function(event) {
this.trigger('change', this);
},
'focus': function(event) {
this.trigger('focus', this);
},
'blur': function(event) {
this.trigger('blur', this);
}
},
initialize: function(options) {
Form.editors.Base.prototype.initialize.call(this, options);
this.template = _.template('<div class="toggle-xa"><div class="toggle"></div></div>');
//this.$el.attr('type', 'checkbox');
this.switchOn = _.has(this.schema,'switchOn') ? this.schema.switchOn : false;
this.onText = _.has(this.schema,'onText') ? this.schema.onText : 'ON';
this.offText = _.has(this.schema,'offText') ? this.schema.offText : 'OFF';
this.width = _.has(this.schema,'width') ? this.schema.width : 50;
this.height = _.has(this.schema,'height') ? this.schema.height : 20;
},
/**
* Adds the editor to the DOM
*/
render: function() {
this.$el.html( this.template );
this.$el.find('.toggle').toggles({
on:this.switchOn,
text : {on : this.onText, off : this.offText },
width: this.width,
height: this.height
});
return this;
},
getValue: function() {
return this.$el.find('.toggle-slide').hasClass('active')? true: false;
//return this.$el.find('.active').text() == "ON" ? true : false;
},
setValue: function(switchOn) {
this.$el.find('.toggle').toggles({on:switchOn,text : {on : this.onText, off : this.offText }});
/*if(switchOn){
this.$el.find('.active').removeClass('active');
this.$el.find('.toggle-on').addClass('active');
}else{
this.$el.find('.active').removeClass('active');
this.$el.find('.toggle-off').addClass('active');
}*/
return true;
},
focus: function() {
if (this.hasFocus) return;
this.$el.focus();
},
blur: function() {
if (!this.hasFocus) return;
this.$el.blur();
}
});
//costume text filed
Form.editors.TextFieldWithIcon = Form.editors.Base.extend({
tagName: 'span',
defaultValue: '',
previousValue: '',
events: {
'keyup': 'determineChanges',
'keypress': function(event) {
var self = this;
setTimeout(function() {
self.determineChanges();
}, 0);
},
'focus [data-id="textFiledInput"]': "focus",
'blur [data-id="textFiledInput"]': "blur"
},
initialize: function(options) {
Form.editors.Base.prototype.initialize.call(this, options);
this.template = _.template('<input type="text" class="textFiledInputPadding" data-id="textFiledInput"><span><i class="fa-fw fa fa-info-circle customTextFiledIcon" data-id="infoTextFiled"></i></span>');
var schema = this.schema;
//Allow customising text type (email, phone etc.) for HTML5 browsers
var type = 'text';
if (schema && schema.editorAttrs && schema.editorAttrs.type) type = schema.editorAttrs.type;
if (schema && schema.dataType) type = schema.dataType;
this.$el.attr('type', type);
this.$el.html( this.template );
},
/**
* Adds the editor to the DOM
*/
render: function() {
var that = this, attrs = { 'name': this.key.replace(/\./g, '_')};
if(this.schema.editorAttrs){
attrs = _.extend(attrs , this.schema.editorAttrs);
}
this.$el.find('input').attr(attrs);
_.each(attrs,function(val, key){
if(key != 'id'){
that.$el.removeAttr(key);
}
});
this.$el.removeAttr('type');
this.setValue(this.value);
XAUtil.errorsInfoPopover(this.$el.find('[data-id="infoTextFiled"]'),this.schema.errorMsg);
return this;
},
determineChanges: function(event) {
var currentValue = this.$el.find('input').val();
var changed = (currentValue !== this.previousValue);
if (changed) {
this.previousValue = currentValue;
this.trigger('change', this);
}
},
/**
* Returns the current editor value
* @return {String}
*/
getValue: function() {
return this.$el.find('input').val();
},
/**
* Sets the value of the form element
* @param {String}
*/
setValue: function(value) {
this.$el.find('input').val(value);
},
focus: function() {
if (this.hasFocus) return;
},
blur: function() {
var trimmedVal = $.trim(this.$el.find('input').val());
this.$el.find('input').val(trimmedVal);
},
select: function() {
this.$el.find('input').select();
}
});
/**
* Password editor
*/
Form.editors.PasswordFiled = Form.editors.TextFieldWithIcon.extend({
initialize: function(options) {
Form.editors.TextFieldWithIcon.prototype.initialize.call(this, options);
this.$el.find('input').attr('type', 'password');
},
});
/**
* #RANGER RESOURCE
* https://github.com/simontabor/jquery-toggles
*
*/
Form.editors.Resource = Form.editors.Base.extend({
ui : {
'resource' : '[data-js="resource"]',
'excludeSupport' : '[data-js="include"]',
'recursiveSupport' : '[data-js="recursive"]',
'resourceType' : '[data-js="resourceType"]',
},
events: {
'click': function(event) {
},
'change': function(event) {
this.trigger('change', this);
},
},
initialize: function(options) {
Form.editors.Base.prototype.initialize.call(this, options);
//default options
this.excludeSupport = false;
this.recursiveSupport = false;
this.resourcesAtSameLevel = false;
this.initilializePathPlugin = false;
this.resourceOpts = {};
_.extend(this, _.pick(this.schema,'excludeSupport','recursiveSupport','resourceOpts','resourcesAtSameLevel','sameLevelOpts',
'initilializePathPlugin', 'validators','name','formView'));
//(edit mode)set values for sameLevel if first option is not selected
if(!_.isNull(this.value) && !_.isUndefined(this.value)
&& !_.isUndefined(this.value.resourceType)){
var def = _.findWhere(XAUtil.policyTypeResources(this.form.rangerServiceDefModel , this.model.get('policyType')), {'name': this.value.resourceType});
this.recursiveSupport = def.recursiveSupported;
this.excludeSupport = def.excludesSupported;
}
this.template = this.getTemplate();
},
initializeElements : function() {
this.$resource = this.$el.find(this.ui.resource)
this.$excludeSupport = this.$el.find(this.ui.excludeSupport)
this.$recursiveSupport = this.$el.find(this.ui.recursiveSupport)
this.$resourceType = this.$el.find(this.ui.resourceType)
if(_.isUndefined(this.value) || _.isNull(this.value)){
this.value = {};
}
},
/**
* Adds the editor to the DOM
*/
render: function() {
//render template
this.$el.html( this.template );
this.initializeElements();
this.renderResource();
this.renderToggles();
this.renderSameLevelResource();
return this;
},
renderResource : function(def) {
var that = this;
var Vent = require('modules/Vent');
if(!_.isNull(this.value) && !_.isEmpty(this.value)){
if (!_.isUndefined(this.key) && (this.key == "path" || this.key == "relativepath")) {
this.value.values = this.value.values.join('||');
// Set initial value for resources.
if (_.isUndefined(def)) {
this.$resource.val((this.value.values));
}
} else {
this.value.values = _.map(this.value.values, function(val){ return _.escape(val)});
// Set initial value for resources.
if (_.isUndefined(def)) {
this.$resource.val(JSON.stringify(this.value.values));
}
}
//to preserve resources values to text field
if(!_.isUndefined(this.value.resourceType)){
this.preserveResourceValues[this.value.resourceType] = this.value.values;
}else{
this.preserveResourceValues[this.name] = this.value.values;
}
}
//check dirtyField for input
this.$resource.on('change', function(e) {
if(_.isUndefined(that.resourceOpts.select2Opts)){
// that.checkDirtyFieldForSelect2($(e.currentTarget), that, this.value);
}
});
//Handle resource at same level option for lookupSupport true/false condition
if(def){
if(def.lookupSupported){
var opts = {};
var singleValueInput = XAUtil.isSinglevValueInput(def);
opts['singleValueInput'] = singleValueInput;
if(_.has(def, 'validationRegEx') && !_.isEmpty(def.validationRegEx)){
opts['regExpValidation'] = {'type': 'regexp', 'regexp':new RegExp(def.validationRegEx), 'message' : def.validationMessage};
}
if(!_.isUndefined(this.form.serviceName)) {
opts['lookupURL'] = "service/plugins/services/lookupResource/"+this.form.serviceName;
} else {
opts['lookupURL'] = "service/plugins/services/lookupResource/"+this.form.rangerService.get('name');
}
opts['type'] = def.name;
this.resourceOpts['select2Opts'] = that.form.getPlugginAttr(true, opts);
}else{
this.resourceOpts.select2Opts['containerCssClass'] = def.name;
delete this.resourceOpts.select2Opts['ajax'];
delete this.resourceOpts.select2Opts['tags'];
this.resourceOpts.select2Opts['data'] = [];
if(singleValueInput){
this.resourceOpts['select2Opts']['maximumSelectionSize'] = 1;
}
}
}
//create select2 if select2Opts is specified
if(!_.isUndefined(this.resourceOpts.select2Opts)){
this.$resource.select2(this.resourceOpts.select2Opts).on('change',function(e){
that.preserveResourceValues[that.$resourceType.val()] = e.currentTarget.value;
//check dirty field value for select2 resource field
that.checkDirtyFieldForSelect2($(e.currentTarget), that, this.value);
});
}
},
renderToggles : function() {
var XAUtil = require('utils/XAUtils');
var that = this, isExcludes = false, isRecursive = true;
if(this.resourcesAtSameLevel && this.sameLevelOpts[0] == "none" && _.isEmpty(this.value)){
this.excludeSupport = false;
this.recursiveSupport = false;
}
if(this.excludeSupport){
if(!_.isNull(this.value)){
this.value.isExcludes = _.isUndefined(this.value.isExcludes) ? false : this.value.isExcludes;
isExcludes = this.value.isExcludes
}
this.$excludeSupport.css('visibility', 'visible');
this.$excludeSupport.toggles({
on: !isExcludes,
text : {on : 'Include', off : 'Exclude' },
width: 80,
}).on('toggle', function (e, active) {
that.value.isExcludes = !active;
XAUtil.checkDirtyFieldForToggle($(e.currentTarget))
});
} else {
this.$excludeSupport.css('visibility', 'hidden');
}
if(this.recursiveSupport){
if(!_.isNull(this.value)){
this.value.isRecursive = _.isUndefined(this.value.isRecursive) ? true : this.value.isRecursive;
isRecursive = this.value.isRecursive;
}
this.$recursiveSupport.show();
this.$recursiveSupport.removeClass('recursive-toggle-1 recursive-toggle-2');
this.$recursiveSupport.addClass(this.excludeSupport ? 'recursive-toggle-2' : 'recursive-toggle-1')
this.$recursiveSupport.toggles({
on: isRecursive,
text : {on : 'Recursive', off : 'Non-recursive' },
width: 120,
}).on('toggle', function (e, active) {
that.value.isRecursive = active;
XAUtil.checkDirtyFieldForToggle($(e.currentTarget))
});
} else {
this.$recursiveSupport.hide();
}
},
renderSameLevelResource : function() {
var Vent = require('modules/Vent');
var that = this, dirtyFieldValue = null;
var XAUtil = require('utils/XAUtils'), localization = require('utils/XALangSupport');
if(!_.isUndefined(this.$resourceType) && this.$resourceType.length > 0){
if(!_.isNull(this.value) && !_.isEmpty(this.value)){
this.$resourceType.val(this.value.resourceType);
}
this.$resourceType.on('change', function(e,onChangeResources) {
if(!_.isUndefined(that.preserveResourceValues[e.currentTarget.value])){
var val = _.isEmpty(that.preserveResourceValues[e.currentTarget.value]) ? '' : that.preserveResourceValues[e.currentTarget.value];
that.$resource.val(JSON.stringify(val))
}else{
that.$resource.select2('val', "");
}
//reset values
that.value.isExcludes = false;
that.value.isRecursive = false;
that.$excludeSupport.trigger('toggleOn');
($(e.currentTarget).addClass('dirtyField'))
//resource are shown if parent is selected or showned
that.$el.parents('.form-group').attr('data-name', 'field-'+this.value);
//remove error class
that.$el.removeClass('error');
// if noneFlag is true not trigger parentChildHideShow
if(!onChangeResources){
that.formView.trigger('policyForm:parentChildHideShow',true, e.currentTarget.value , e);
}
if(!_.isUndefined(this.value)
&& ( XAUtil.capitaliseFirstLetter(this.value) === XAEnums.ResourceType.RESOURCE_UDF.label) ){
XAUtil.alertPopup({ msg :localization.tt('msg.udfPolicyViolation') });
}
if(!_.isUndefined(this.value)
&& ( XAUtil.capitaliseFirstLetter(this.value) === XAEnums.ResourceType.RESOURCE_GLOBAL.label) ){
XAUtil.alertPopup({ msg :localization.tt('msg.udfPolicyViolation') });
}
// if value is "none" hide recursive/exclude toggles
if(this.value == "none"){
that.recursiveSupport = false;
that.excludeSupport = false;
that.renderToggles();
}
//set flags for newly selected resource and re-render
var def = _.findWhere(XAUtil.policyTypeResources(that.form.rangerServiceDefModel , that.model.get('policyType')), {'name': this.value});
if(def){
that.recursiveSupport = def.recursiveSupported;
if(that.recursiveSupport) that.value.isRecursive = true;
that.excludeSupport = def.excludesSupported;
that.renderToggles();
//Handle resource at same level option for lookupSupport true/false condition
that.renderResource(def);
}
//trigger resource event for showing respective access permissions
var resourceItemIndex = that.form.model && that.form.model.collection.indexOf(that.form.model);
Vent.trigger('resourceType:change', { changeType : 'resourceType', value : e.currentTarget.value, resourceName: e.currentTarget.value, event:e, resourceItemIndex : resourceItemIndex });
});
}
},
getValue: function(fieldName) {
var that = this;
//checkParent
if(this.$el.parents('.form-group').hasClass('hideResource')){
return null;
}
if (that.key == "path" || that.key == "relativepath") {
this.value['resource'] = this.$resource.tagit("assignedTags");
} else {
this.value['resource'] = this.$resource.select2('data');
}
//validation
if(!_.isUndefined(this.validators)){
if(($.inArray('required',this.validators) != -1)){
if(_.isEmpty(this.value.resource))
return null;
}
}
if(!_.isUndefined(this.$resourceType) && this.$resourceType.length > 0){
this.value['resourceType'] = this.$resourceType.val();
}
return this.value;
},
setValue: function(val) {
return true;
},
checkDirtyFieldForSelect2 : function($el,that,value) {
var defaultResourceValue = _.isUndefined(that.value.values) ? [] : that.value.values;
if($el.hasClass('dirtyField')){
var tmpValue={};
tmpValue.values = _.isEmpty(value) ? [] : value.split(',');
tmpValue.isExcludes = that.value.isExcludes;
tmpValue.isRecursive = that.value.isRecursive;
if(_.isEqual(tmpValue, dirtyFieldValue)){
$el.removeClass('dirtyField');
}
}else if(!$el.hasClass('dirtyField')){
$el.addClass('dirtyField');
if(!_.isNull(that.value)){
that.value.values = defaultResourceValue;
if(_.isUndefined(that.value.isExcludes)){
that.value.isExcludes = that.excludeSupport ? true : false;
}
if(_.isUndefined(that.value.isRecursive)){
that.value.isRecursive = that.RecursiveSupport ? true : false;
}
}
dirtyFieldValue = that.value
}
},
getTemplate : function() {
var that = this , resourcesType , optionsHtml="" , selectTemplate = '', excludeSupportToggleDiv='', recursiveSupportToggleDiv='',
recursiveTogglePosition = '', includeTogglePosition = '';
this.preserveResourceValues = {} ;
if(this.resourcesAtSameLevel){
_.each(this.sameLevelOpts, function(option){ return optionsHtml += "<option value='"+option+"'>"+option+"</option>"; },this);
selectTemplate = '<select data-js="resourceType" class="btn dropdown-toggle sameLevelDropdown" >\
'+optionsHtml+'\
</select>';
}
_.each(this.form.rangerServiceDefModel.get('resources') , function(m){
if(that.name === m.name){
resourcesType = m.type ;
}
})
if(resourcesType == "path"){
recursiveTogglePosition = (!this.excludeSupport) ? "recursive-toggle-hdfs-1" : "recursive-toggle-hdfs-2";
includeTogglePosition = "include-toggle-1";
}else{
recursiveTogglePosition = (!this.excludeSupport) ? "recursive-toggle-1" : "recursive-toggle-2";
includeTogglePosition = "include-toggle";
}
excludeSupportToggleDiv = '<div class="toggle-xa '+includeTogglePosition+'" data-js="include" style ="height: 20px; width: 80px;"><div class="toggle"></div></div>';
recursiveSupportToggleDiv = '<div class="toggle-xa recursive-toggle '+recursiveTogglePosition+'"" data-js="recursive" style="height: 20px; width: 120px;"><div class="toggle"></div></div>';
return _.template(selectTemplate+'<input data-js="resource" type="text">'+
excludeSupportToggleDiv+''+recursiveSupportToggleDiv);
},
});
// bootstrap-editable ============================================
(function ($) {
"use strict";
/**
********************** custom type created for tag based policies
List of taglistcheck.
Internally value stored as javascript array of values.
@class tagchecklist
@extends list
@final
@example
<a href="#" id="options" data-type="tagchecklist" data-pk="1" data-url="/post" data-title="Select options"></a>
**/
var TagChecklist = function (options) {
this.init('tagchecklist', options, TagChecklist.defaults);
};
$.fn.editableutils.inherit(TagChecklist, $.fn.editabletypes.list);
$.extend(TagChecklist.prototype, {
renderList: function() {
var $label='', $div='', that = this;
this.$tpl.empty();
this.editModPopup = true;
if(!$.isArray(this.sourceData)) {
return;
}
this.servicePerms = _.groupBy(this.sourceData,function(obj){
var val = obj.value;
return val.substr(0,val.indexOf(":"));
});
var $selectComp = $('<select>').attr('data-id','selectComp')
.attr('multiple','multiple')
.attr('placeholder','Select component');
var optionList = _.keys(this.servicePerms);
_.each(optionList, function (val, el) {
$selectComp.append("<option>" + val + "</option>");
});
var $table = $('<table>', {'class':'table table-policy-condition table-perms margin-top-6' });
var $tbody = $('<tbody><tr><th><input type="checkbox" data-id="selectAllComponent" /> Component</th><td><strong>Permissions</strong></td></tr></tbody>');
$selectComp.append($table)
$('<div class="mb-1">').append($selectComp).appendTo(this.$tpl);
$table.append($tbody).appendTo(this.$tpl);
this.$tpl.find('[data-id="selectComp"]').select2(this.options.select2option).on('change',function(e){
if(!_.isUndefined(e.added)){
that.addTr(e.added.text)
//Table header selectAll perm event
that.addEventToThCheckbox();
//Table data permission event
that.addEventToTdCheckbox();
}else{
that.$tpl.find('tr[data-id="'+e.removed.text+'"]').remove()
//uncheck selectAllCompChxbox if there is no component is selected
if(_.isEmpty(e.val)) that.$tpl.find('[data-id="selectAllComponent"]').prop('checked',false)
}
});
//selectall component event
this.$tpl.find('[data-id="selectAllComponent"]').on('click',function(chx){
var table = $(chx.currentTarget).parents('.table-perms')
var selectAllInputChx = table.parents('.table-perms').find('th input')
selectAllInputChx.splice(0,1)
if($(chx.currentTarget).is(':checked')){
table.find('input').prop('checked',true)
}else{
table.find('input').prop('checked',false)
}
});
},
value2str: function(value) {
return $.isArray(value) ? value.sort().join($.trim(this.options.separator)) : '';
},
//parse separated string
str2value: function(str) {
var reg, value = null;
if(typeof str === 'string' && str.length) {
reg = new RegExp('\\s*'+$.trim(this.options.separator)+'\\s*');
value = str.split(reg);
} else if($.isArray(str)) {
value = str;
} else {
value = [str];
}
return value;
},
//set checked on required checkboxes
value2input: function(value) {
var that = this;
if(_.contains(value,"on")){
value = _.without(value,"on")
}
if(this.editModPopup){
var selectedComp = _.map(value,function(val){
return val.split(':')[0];
});
this.value = _.unique(selectedComp);
this.$tpl.find('[data-id="selectComp"]').select2('val',_.unique(selectedComp))
_.each(_.unique(selectedComp), function(compName){
this.addTr(compName);
},this)
this.addEventToThCheckbox();
this.addEventToTdCheckbox();
_.each(value,function(val){
this.$tpl.find('input[data-js="'+val+'"]').prop('checked',true)
},this);
//checked selectall for perticular component
this.setSelectAllPerCompChxbox();
//check parentSelectAllComponent
this.setSelectAllCompChxbox();
this.editModPopup = false;
}
},
input2value: function() {
this.$input = this.$tpl.find('input[type="checkbox"]')
var checked = [];
this.$input.filter(':checked').each(function(i, el) {
checked.push($(el).val());
});
return checked;
},
//collect text of checked boxes
value2htmlFinal: function(value, element) {
var html = [],
checked = $.fn.editableutils.itemsByValue(value, this.sourceData),
escape = this.options.escape;
if(checked.length) {
$.each(checked, function(i, v) {
var text = escape ? $.fn.editableutils.escape(v.text) : v.text;
html.push(text);
});
$(element).html(html.join('<br>'));
} else {
$(element).empty();
}
},
addTr : function(compName){
var $tr = $('<tr data-id="'+compName+'">'), $th = $('<th>'), $label = '<label><input type="checkbox" data-id="selectall" data-type="'+compName+'"></label>'+compName;
var $tmp = $th.append($label);
var $td = $('<td>');
var permissions = this.servicePerms[compName]
_.each(permissions, function(perm){
$label = $('<label>').append($('<input>', {
type: 'checkbox',
value: perm.value,
'data-js' : perm.value
}))
.append($('<span>').text(' '+perm.text));
$td.append($label)
});
$tr.append($th)
$tr.append($td)
this.$tpl.find('tbody').append($tr)
},
addEventToThCheckbox : function(){
var that = this;
this.$tpl.find('th input').on('click',function(chx){
var type = $(chx.currentTarget).attr('data-type')
var tr = $(chx.currentTarget).parents('tr[data-id="'+type+'"]')
if($(chx.currentTarget).is(':checked')){
tr.find('td input').prop('checked',true)
//to check for selectAll component level
var selectAllInputChx = tr.parents('.table-perms').find('th input'),selectAll = true;
selectAllInputChx.splice(0,1);
_.each(selectAllInputChx, function(ele){
if(!$(ele).is(':checked')){
selectAll = false;
return;
}
})
if(selectAll){
that.$tpl.find('[data-id="selectAllComponent"]').prop('checked',true)
}
}else{
tr.find('td input').prop('checked',false)
that.$tpl.find('[data-id="selectAllComponent"]').prop('checked',false)
}
})
},
addEventToTdCheckbox : function(){
var that = this;
this.$tpl.find('td input').on('click',function(chx){
var dataJs = $(chx.currentTarget).attr('data-js').split(':')
var type = dataJs[0]
var tr = $(chx.currentTarget).parents('tr[data-id="'+type+'"]')
if($(chx.currentTarget).is(':checked')){
//to check for selectAll component level
var permInputChx = tr.find('td input'),selectAll = true;
_.each(permInputChx, function(ele){
if(!$(ele).is(':checked')){
selectAll = false;
return;
}
})
if(selectAll){
tr.find('th input').prop('checked',true)
that.setSelectAllCompChxbox();
}
}else{
tr.find('th input').prop('checked',false)
that.$tpl.find('[data-id="selectAllComponent"]').prop('checked',false)
}
})
},
setSelectAllCompChxbox : function(){
var that = this;
var selectAllComp = true,tr = this.$tpl.find('table tr');
if(tr.length > 1){
_.each(this.$tpl.find('table tr'), function(el, i){
if(i != 0){
var componentChxbox = $(el).find('th input');
_.each(componentChxbox, function(ele){
if(!$(ele).is(':checked')){
selectAllComp = false;
return;
}
})
}
})
}else{
selectAllComp = false;
}
if(selectAllComp){
that.$tpl.find('[data-id="selectAllComponent"]').prop('checked',true);
}else{
that.$tpl.find('[data-id="selectAllComponent"]').prop('checked',false);
}
},
setSelectAllPerCompChxbox : function(){
var that = this;
_.each(this.$tpl.find('table tr'), function(el){
var componentChxbox = $(el).find('td input'), selectAll = true;
_.each(componentChxbox, function(ele){
if(!$(ele).is(':checked')){
selectAll = false;
return;
}
})
if(selectAll){
$(el).find('th input').prop('checked',true);
}else{
$(el).find('th input').prop('checked',false);
}
})
}
});
TagChecklist.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
/**
@property tpl
@default <div></div>
**/
tpl:'<div class="editable-checklist"></div>',
/**
@property inputclass
@type string
@default null
**/
inputclass: null,
/**
Separator of values when reading from `data-value` attribute
@property separator
@type string
@default ','
**/
separator: ',',
select2option : {}
});
$.fn.editabletypes.tagchecklist = TagChecklist;
/**
radiolist
Internally value stored as javascript array of values.
@class radiolist
@extends list
@final
@example
<a href="#" id="options" data-type="radiolist" data-pk="1" data-url="/post" data-title="Select options"></a>
**/
var Radiolist = function(options) {
this.init('radiolist', options, Radiolist.defaults);
};
$.fn.editableutils.inherit(Radiolist, $.fn.editabletypes.checklist);
$.extend(Radiolist.prototype, {
renderList : function() {
var $label;
this.$tpl.empty();
if (!$.isArray(this.sourceData)) {
return;
}
for (var i = 0; i < this.sourceData.length; i++) {
$label = $('<label>', {'class':this.options.inputclass}).append($('<input>', {
type : 'radio',
name : this.options.name,
value : this.sourceData[i].value,
'class' : 'margin-right-5'
})).append($('<span>').text(this.sourceData[i].text));
// Add radio buttons to template
this.$tpl.append($('<div>').append($label));
}
this.$input = this.$tpl.find('input[type="radio"]');
},
input2value : function() {
return this.$input.filter(':checked').val();
},
str2value: function(str) {
return str || null;
},
value2input: function(value) {
this.$input.val([value]);
},
value2str: function(value) {
return value || '';
},
});
Radiolist.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
/**
@property tpl
@default <div></div>
**/
tpl : '<div class="editable-radiolist"></div>',
/**
@property inputclass, attached to the <label> wrapper instead of the input element
@type string
@default null
**/
inputclass : '',
name : 'defaultname'
});
$.fn.editabletypes.radiolist = Radiolist;
}(window.jQuery));
//Scroll to top functionality on all views -- if the scroll height is > 500 px.
$(window).scroll(function() {
if ($(this).scrollTop() > 300) {
$('#back-top').show();
$('#back-top').tooltip();
} else {
$('#back-top').hide();
}
});
$('#back-top').click(function() {
$('body,html').animate({
scrollTop : 0
}, 800);
return false;
});
/*
* Backbone.View override for implementing Breadcrumbs
*/
Backbone.View = (function(View) {
// Define the new constructor
Backbone.View = function(options) {
// Call the original constructor
View.apply(this, arguments);
// Add the render callback
if(this.breadCrumbs){
var breadCrumbsArr = [];
if(_.isFunction(this.breadCrumbs))
breadCrumbsArr = this.breadCrumbs();
else
breadCrumbsArr = this.breadCrumbs;
if(App.rBreadcrumbs.currentView)
App.rBreadcrumbs.close();
App.rBreadcrumbs.show(new vBreadCrumbs({breadcrumb:breadCrumbsArr}));
}
};
// 1lone static properties
_.extend(Backbone.View, View);
// Clone prototype
Backbone.View.prototype = (function(Prototype) {
Prototype.prototype = View.prototype;
return new Prototype;
})(function() {});
// Update constructor in prototype
Backbone.View.prototype.constructor = Backbone.View;
return Backbone.View;
})(Backbone.View);
/*
* Override val() of jquery to trim values
*/
(function ($) {
var originalVal = $.fn.val;
$.fn.val = function(value) {
if (_.isUndefined(value))
return originalVal.call(this);
else {
return originalVal.call(this,(_.isString(value))? $.trim(value):value);
}
};
})(jQuery);
/*************** Block UI ************************/
/*! Copyright 2011, Ben Lin (http://dreamerslab.com/)
* Licensed under the MIT License (LICENSE.txt).
*
* Version: 1.1.1
*
* Requires: jQuery 1.2.6+
* https://github.com/dreamerslab/jquery.msg/
*/
;(function($,window){var get_win_size=function(){if(window.innerWidth!=undefined)return[window.innerWidth,window.innerHeight];else{var B=document.body;var D=document.documentElement;return[Math.max(D.clientWidth,B.clientWidth),Math.max(D.clientHeight,B.clientHeight)]}};$.fn.center=function(opt){var $w=$(window);var scrollTop=$w.scrollTop();return this.each(function(){var $this=$(this);var configs=$.extend({against:"window",top:false,topPercentage:0.5,resize:true},opt);var centerize=function(){var against=configs.against;var against_w_n_h;var $against;if(against==="window")against_w_n_h=get_win_size();else if(against==="parent"){$against=$this.parent();against_w_n_h=[$against.width(),$against.height()];scrollTop=0}else{$against=$this.parents(against);against_w_n_h=[$against.width(),$against.height()];scrollTop=0}var x=(against_w_n_h[0]-$this.outerWidth())*0.5;var y=(against_w_n_h[1]-$this.outerHeight())*configs.topPercentage+scrollTop;if(configs.top)y=configs.top+scrollTop;$this.css({"left":x,"top":y})};centerize();if(configs.resize===true)$w.resize(centerize)})}})(jQuery,window);
/* Copyright 2011, Ben Lin (http://dreamerslab.com/)
* Licensed under the MIT License (LICENSE.txt).
*
* Version: 1.0.7
*
* Requires:
* jQuery 1.3.0+,
* jQuery Center plugin 1.0.0+ https://github.com/dreamerslab/jquery.center
*/
;(function(d,e){var a={},c=0,f,b=[function(){}];d.msg=function(){var g,k,j,l,m,i,h;j=[].shift.call(arguments);l={}.toString.call(j);m=d.extend({afterBlock:function(){},autoUnblock:true,center:{topPercentage:0.4},css:{},clickUnblock:true,content:"Please wait...",fadeIn:200,fadeOut:300,bgPath:"",klass:"black-on-white",method:"appendTo",target:"body",timeOut:2400,z:1000},a);l==="[object Object]"&&d.extend(m,j);i={unblock:function(){g=d("#jquery-msg-overlay").fadeOut(m.fadeOut,function(){b[m.msgID](g);g.remove();});clearTimeout(f);}};h={unblock:function(o,n){var p=o===undefined?0:o;m.msgID=n===undefined?c:n;setTimeout(function(){i.unblock();},p);},replace:function(n){if({}.toString.call(n)!=="[object String]"){throw"$.msg('replace'); error: second argument has to be a string";}d("#jquery-msg-content").empty().html(n).center(m.center);},overwriteGlobal:function(o,n){a[o]=n;}};c--;m.msgID=m.msgID===undefined?c:m.msgID;b[m.msgID]=m.beforeUnblock===undefined?function(){}:m.beforeUnblock;if(l==="[object String]"){h[j].apply(h,arguments);}else{g=d('<div id="jquery-msg-overlay" class="'+m.klass+'" style="position:absolute; z-index:'+m.z+"; top:0px; right:0px; left:0px; height:"+d(e).height()+'px;"><img src="'+m.bgPath+'blank.gif" id="jquery-msg-bg" style="width: 100%; height: 100%; top: 0px; left: 0px;"/><div id="jquery-msg-content" class="jquery-msg-content" style="position:absolute;">'+m.content+"</div></div>");g[m.method](m.target);k=d("#jquery-msg-content").center(m.center).css(m.css).hide();g.hide().fadeIn(m.fadeIn,function(){k.fadeIn("fast").children().addBack().bind("click",function(n){n.stopPropagation();});m.afterBlock.call(h,g);m.clickUnblock&&g.bind("click",function(n){n.stopPropagation();i.unblock();});if(m.autoUnblock){f=setTimeout(i.unblock,m.timeOut);}});}return this;};})(jQuery,document);
/*
* BASICS
**********
dirtyFields is a jQuery plugin that makes a user aware of which form elements have been updated on an HTML form and can reset the form values back to their previous state.
The main website for the plugin (which includes documentation, demos, and a download file) is currently at:
http://www.thoughtdelimited.org/dirtyFields/index.cfm
*
*/
(function(e){function t(t,n,r){var i=n.data("dF").dirtyFieldsDataProperty;var s=e.inArray(t,i);if(r=="dirty"&&s==-1){i.push(t);n.data("dF").dirtyFieldsDataProperty=i}else if(r=="clean"&&s>-1){i.splice(s,1);n.data("dF").dirtyFieldsDataProperty=i}}function n(t){if(t.data("dF").dirtyFieldsDataProperty.length>0){t.addClass(t.data("dF").dirtyFormClass);if(e.isFunction(t.data("dF").formChangeCallback)){t.data("dF").formChangeCallback.call(t,true,t.data("dF").dirtyFieldsDataProperty)}}else{t.removeClass(t.data("dF").dirtyFormClass);if(e.isFunction(t.data("dF").formChangeCallback)){t.data("dF").formChangeCallback.call(t,false,t.data("dF").dirtyFieldsDataProperty)}}}function r(t,n,r,i){if(i.data("dF").denoteDirtyFields){var s=i.data("dF").fieldOverrides;var o=n.attr("id");var u=false;for(var a in s){if(o==a){if(r=="changed"){e("#"+s[a]).addClass(i.data("dF").dirtyFieldClass)}else{e("#"+s[a]).removeClass(i.data("dF").dirtyFieldClass)}u=true}}if(u==false){var f=i.data("dF")[t];var l=f.split("-");switch(l[0]){case"next":if(r=="changed"){n.next(l[1]).addClass(i.data("dF").dirtyFieldClass)}else{n.next(l[1]).removeClass(i.data("dF").dirtyFieldClass)}break;case"previous":if(r=="changed"){n.prev(l[1]).addClass(i.data("dF").dirtyFieldClass)}else{n.prev(l[1]).removeClass(i.data("dF").dirtyFieldClass)}break;case"closest":if(r=="changed"){n.closest(l[1]).addClass(i.data("dF").dirtyFieldClass)}else{n.closest(l[1]).removeClass(i.data("dF").dirtyFieldClass)}break;case"self":if(r=="changed"){n.addClass(i.data("dF").dirtyFieldClass)}else{n.removeClass(i.data("dF").dirtyFieldClass)}break;default:if(l[0]=="id"||l[0]=="name"){switch(l[1]){case"class":if(r=="changed"){e("."+n.attr(l[0]),i).addClass(i.data("dF").dirtyFieldClass)}else{e("."+n.attr(l[0]),i).removeClass(i.data("dF").dirtyFieldClass)}break;case"title":if(r=="changed"){e("*[title='"+n.attr(l[0])+"']",i).addClass(i.data("dF").dirtyFieldClass)}else{e("*[title='"+n.attr(l[0])+"']",i).removeClass(i.data("dF").dirtyFieldClass)}break;case"for":if(r=="changed"){e("label[for='"+n.attr(l[0])+"']",i).addClass(i.data("dF").dirtyFieldClass)}else{e("label[for='"+n.attr(l[0])+"']",i).removeClass(i.data("dF").dirtyFieldClass)}break}}break}}}}function i(i,s){var o=i.attr("name");var u=false;if(s.data("dF").trimText){var a=jQuery.trim(i.val())}else{var a=i.val()}if(i.hasClass(s.data("dF").ignoreCaseClass)){var a=a.toUpperCase();var f=i.data(s.data("dF").startingValueDataProperty).toUpperCase()}else{var f=i.data(s.data("dF").startingValueDataProperty)}if(a!=f){r("textboxContext",i,"changed",s);t(o,s,"dirty");u=true}else{r("textboxContext",i,"unchanged",s);t(o,s,"clean")}if(e.isFunction(s.data("dF").fieldChangeCallback)){s.data("dF").fieldChangeCallback.call(i,i.data(s.data("dF").startingValueDataProperty),u)}if(s.data("dF").denoteDirtyForm){n(s)}}function s(i,s){var o=i.attr("name");var u=false;if(s.data("dF").denoteDirtyOptions==false&&i.attr("multiple")!=true){if(i.hasClass(s.data("dF").ignoreCaseClass)){var a=i.val().toUpperCase();var f=i.data(s.data("dF").startingValueDataProperty).toUpperCase()}else{var a=i.val();var f=i.data(s.data("dF").startingValueDataProperty)}if(a!=f){r("selectContext",i,"changed",s);t(o,s,"dirty");u=true}else{r("selectContext",i,"unchanged",s);t(o,s,"clean")}}else{var l=false;i.children("option").each(function(t){var n=e(this);var r=n.is(":selected");if(r!=n.data(s.data("dF").startingValueDataProperty)){if(s.data("dF").denoteDirtyOptions){n.addClass(s.data("dF").dirtyOptionClass)}l=true}else{if(s.data("dF").denoteDirtyOptions){n.removeClass(s.data("dF").dirtyOptionClass)}}});if(l){r("selectContext",i,"changed",s);t(o,s,"dirty");u=true}else{r("selectContext",i,"unchanged",s);t(o,s,"clean")}}if(e.isFunction(s.data("dF").fieldChangeCallback)){s.data("dF").fieldChangeCallback.call(i,i.data(s.data("dF").startingValueDataProperty),u)}if(s.data("dF").denoteDirtyForm){n(s)}}function o(i,s){var o=i.attr("name");var u=false;var a=i.attr("type");e(":"+a+"[name='"+o+"']",s).each(function(t){var n=e(this);var i=n.is(":checked");if(i!=n.data(s.data("dF").startingValueDataProperty)){r("checkboxRadioContext",n,"changed",s);u=true}else{r("checkboxRadioContext",n,"unchanged",s)}});if(u){t(o,s,"dirty")}else{t(o,s,"clean")}if(e.isFunction(s.data("dF").fieldChangeCallback)){s.data("dF").fieldChangeCallback.call(i,i.data(s.data("dF").startingValueDataProperty),u)}if(s.data("dF").denoteDirtyForm){n(s)}}e.fn.dirtyFields=function(t){var n=e.extend({},e.fn.dirtyFields.defaults,t);return this.each(function(){var t=e(this);t.data("dF",n);t.data("dF").dirtyFieldsDataProperty=new Array;e("input[type='text'],input[type='file'],input[type='password'],textarea",t).not("."+t.data("dF").exclusionClass).each(function(n){e.fn.dirtyFields.configureField(e(this),t,"text")});e("select",t).not("."+t.data("dF").exclusionClass).each(function(n){e.fn.dirtyFields.configureField(e(this),t,"select")});e(":checkbox,:radio",t).not("."+t.data("dF").exclusionClass).each(function(n){e.fn.dirtyFields.configureField(e(this),t,"checkRadio")});e.fn.dirtyFields.setStartingValues(t)})};e.fn.dirtyFields.defaults={checkboxRadioContext:"next-span",denoteDirtyOptions:false,denoteDirtyFields:true,denoteDirtyForm:false,dirtyFieldClass:"dirtyField",dirtyFieldsDataProperty:"dirtyFields",dirtyFormClass:"dirtyForm",dirtyOptionClass:"dirtyOption",exclusionClass:"dirtyExclude",fieldChangeCallback:"",fieldOverrides:{none:"none"},formChangeCallback:"",ignoreCaseClass:"dirtyIgnoreCase",preFieldChangeCallback:"",selectContext:"id-for",startingValueDataProperty:"startingValue",textboxContext:"id-for",trimText:false};e.fn.dirtyFields.configureField=function(t,n,r,u){if(!t.hasClass(n.data("dF").exclusionClass)){if(typeof u!="undefined"){n.data("dF").fieldOverrides[t.attr("id")]=u}switch(r){case"text":t.change(function(){if(e.isFunction(n.data("dF").preFieldChangeCallback)){if(n.data("dF").preFieldChangeCallback.call(t,t.data(n.data("dF").startingValueDataProperty))==false){return false}}i(t,n)});break;case"select":t.change(function(){if(e.isFunction(n.data("dF").preFieldChangeCallback)){if(n.data("dF").preFieldChangeCallback.call(t,t.data(n.data("dF").startingValueDataProperty))==false){return false}}s(t,n)});break;case"checkRadio":t.change(function(){if(e.isFunction(n.data("dF").preFieldChangeCallback)){if(n.data("dF").preFieldChangeCallback.call(t,t.data(n.data("dF").startingValueDataProperty))==false){return false}}o(t,n)});break}}};e.fn.dirtyFields.formSaved=function(t){e.fn.dirtyFields.setStartingValues(t);e.fn.dirtyFields.markContainerFieldsClean(t)};e.fn.dirtyFields.markContainerFieldsClean=function(t){var n=new Array;t.data("dF").dirtyFieldsDataProperty=n;e("."+t.data("dF").dirtyFieldClass,t).removeClass(t.data("dF").dirtyFieldClass);if(t.data("dF").denoteDirtyOptions){e("."+t.data("dF").dirtyOptionClass,t).removeClass(t.data("dF").dirtyOptionClass)}if(t.data("dF").denoteDirtyForm){t.removeClass(t.data("dF").dirtyFormClass)}};e.fn.dirtyFields.setStartingValues=function(t,n){e("input[type='text'],input[type='file'],input[type='password'],:checkbox,:radio,textarea",t).not("."+t.data("dF").exclusionClass).each(function(n){var r=e(this);if(r.attr("type")=="radio"||r.attr("type")=="checkbox"){e.fn.dirtyFields.setStartingCheckboxRadioValue(r,t)}else{e.fn.dirtyFields.setStartingTextValue(r,t)}});e("select",t).not("."+t.data("dF").exclusionClass).each(function(n){e.fn.dirtyFields.setStartingSelectValue(e(this),t)})};e.fn.dirtyFields.setStartingTextValue=function(t,n){return t.not("."+n.data("dF").exclusionClass).each(function(){var t=e(this);t.data(n.data("dF").startingValueDataProperty,t.val())})};e.fn.dirtyFields.setStartingCheckboxRadioValue=function(t,n){return t.not("."+n.data("dF").exclusionClass).each(function(){var t=e(this);var r;if(t.is(":checked")){t.data(n.data("dF").startingValueDataProperty,true)}else{t.data(n.data("dF").startingValueDataProperty,false)}})};e.fn.dirtyFields.setStartingSelectValue=function(t,n){return t.not("."+n.data("dF").exclusionClass).each(function(){var t=e(this);if(n.data("dF").denoteDirtyOptions==false&&t.attr("multiple")!=true){t.data(n.data("dF").startingValueDataProperty,t.val())}else{var r=new Array;t.children("option").each(function(t){var i=e(this);if(i.is(":selected")){i.data(n.data("dF").startingValueDataProperty,true);r.push(i.val())}else{i.data(n.data("dF").startingValueDataProperty,false)}});t.data(n.data("dF").startingValueDataProperty,r)}})};e.fn.dirtyFields.rollbackTextValue=function(t,n,r){if(typeof r=="undefined"){r=true}return t.not("."+n.data("dF").exclusionClass).each(function(){var t=e(this);t.val(t.data(n.data("dF").startingValueDataProperty));if(r){i(t,n)}})};e.fn.dirtyFields.updateTextState=function(t,n){return t.not("."+n.data("dF").exclusionClass).each(function(){i(e(this),n)})};e.fn.dirtyFields.rollbackCheckboxRadioState=function(t,n,r){if(typeof r=="undefined"){r=true}return t.not("."+n.data("dF").exclusionClass).each(function(){var t=e(this);if(t.data(n.data("dF").startingValueDataProperty)){t.attr("checked",true)}else{t.attr("checked",false)}if(r){o(t,n)}})};e.fn.dirtyFields.updateCheckboxRadioState=function(t,n){return t.not("."+n.data("dF").exclusionClass).each(function(){o(e(this),n)})};e.fn.dirtyFields.rollbackSelectState=function(t,n,r){if(typeof r=="undefined"){r=true}return t.not("."+n.data("dF").exclusionClass).each(function(){var t=e(this);if(n.data("dF").denoteDirtyOptions==false&&t.attr("multiple")!=true){t.val(t.data(n.data("dF").startingValueDataProperty))}else{t.children("option").each(function(t){var r=e(this);if(r.data(n.data("dF").startingValueDataProperty)){r.attr("selected",true)}else{r.attr("selected",false)}})}if(r){s(t,n)}})};e.fn.dirtyFields.updateSelectState=function(t,n){return t.not("."+n.data("dF").exclusionClass).each(function(){s(e(this),n)})};e.fn.dirtyFields.rollbackForm=function(t){e("input[type='text'],input[type='file'],input[type='password'],:checkbox,:radio,textarea",t).not("."+t.data("dF").exclusionClass).each(function(n){$object=e(this);if($object.attr("type")=="radio"||$object.attr("type")=="checkbox"){e.fn.dirtyFields.rollbackCheckboxRadioState($object,t,false)}else{e.fn.dirtyFields.rollbackTextValue($object,t,false)}});e("select",t).not("."+t.data("dF").exclusionClass).each(function(n){e.fn.dirtyFields.rollbackSelectState(e(this),t,false)});e.fn.dirtyFields.markContainerFieldsClean(t)};e.fn.dirtyFields.updateFormState=function(t){e("input[type='text'],input[type='file'],input[type='password'],:checkbox,:radio,textarea",t).not("."+t.data("dF").exclusionClass).each(function(n){$object=e(this);if($object.attr("type")=="radio"||$object.attr("type")=="checkbox"){e.fn.dirtyFields.updateCheckboxRadioState($object,t)}else{e.fn.dirtyFields.updateTextState($object,t)}});e("select",t).not("."+t.data("dF").exclusionClass).each(function(n){$object=e(this);e.fn.dirtyFields.updateSelectState($object,t)})};e.fn.dirtyFields.getDirtyFieldNames=function(e){return e.data("dF").dirtyFieldsDataProperty};})(jQuery)
var SelectRowCell = Backgrid.Extension.SelectRowCell = Backbone.View.extend({
/** @property */
className: "select-row-cell",
/** @property */
tagName: "td",
/** @property */
events: {
"keydown input[type=checkbox]": "onKeydown",
"change input[type=checkbox]": "onChange",
"click input[type=checkbox]": "enterEditMode"
},
/**
Initializer. If the underlying model triggers a `select` event, this cell
will change its checked value according to the event's `selected` value.
@param {Object} options
@param {Backgrid.Column} options.column
@param {Backbone.Model} options.model
*/
initialize: function (options) {
this.column = options.column;
if (!(this.column instanceof Backgrid.Column)) {
this.column = new Backgrid.Column(this.column);
}
var column = this.column, model = this.model, $el = this.$el;
this.listenTo(column, "change:renderable", function (column, renderable) {
$el.toggleClass("renderable", renderable);
});
if (Backgrid.callByNeed(column.renderable(), column, model)) $el.addClass("renderable");
this.listenTo(model, "backgrid:select", function (model, selected) {
this.checkbox().prop("checked", selected).change();
});
},
/**
Returns the checkbox.
*/
checkbox: function () {
return this.$el.find("input[type=checkbox]");
},
/**
Focuses the checkbox.
*/
enterEditMode: function () {
this.checkbox().focus();
},
/**
Unfocuses the checkbox.
*/
exitEditMode: function () {
this.checkbox().blur();
},
/**
Process keyboard navigation.
*/
onKeydown: function (e) {
var command = new Backgrid.Command(e);
if (command.passThru()) return true; // skip ahead to `change`
if (command.cancel()) {
e.stopPropagation();
this.checkbox().blur();
}
else if (command.save() || command.moveLeft() || command.moveRight() ||
command.moveUp() || command.moveDown()) {
e.preventDefault();
e.stopPropagation();
this.model.trigger("backgrid:edited", this.model, this.column, command);
}
},
/**
When the checkbox's value changes, this method will trigger a Backbone
`backgrid:selected` event with a reference of the model and the
checkbox's `checked` value.
*/
onChange: function () {
var checked = this.checkbox().prop("checked");
this.$el.parent().toggleClass("selected", checked);
if(checked){
this.model.select();
}else{
this.model.deselect();
}
this.model.trigger("backgrid:selected", this.model, checked);
},
/**
Renders a checkbox in a table cell.
*/
render: function () {
var val;
if(_.has(this, 'model'))
val = (this.model.get(this.column.get('name'))) ? 'checked' : '';
this.$el.empty().append('<input tabindex="-1" type="checkbox" '+ val +'/>');
this.delegateEvents();
return this;
}
});
/**
Renders a checkbox to select all rows on the current page.
@class Backgrid.Extension.SelectAllHeaderCell
@extends Backgrid.Extension.SelectRowCell
*/
var SelectAllHeaderCell = Backgrid.Extension.SelectAllHeaderCell = SelectRowCell.extend({
/** @property */
className: "select-all-header-cell",
/** @property */
tagName: "th",
/**
Initializer. When this cell's checkbox is checked, a Backbone
`backgrid:select` event will be triggered for each model for the current
page in the underlying collection. If a `SelectRowCell` instance exists
for the rows representing the models, they will check themselves. If any
of the SelectRowCell instances trigger a Backbone `backgrid:selected`
event with a `false` value, this cell will uncheck its checkbox. In the
event of a Backbone `backgrid:refresh` event, which is triggered when the
body refreshes its rows, which can happen under a number of conditions
such as paging or the columns were reset, this cell will still remember
the previously selected models and trigger a Backbone `backgrid:select`
event on them such that the SelectRowCells can recheck themselves upon
refreshing.
@param {Object} options
@param {Backgrid.Column} options.column
@param {Backbone.Collection} options.collection
*/
initialize: function (options) {
this.column = options.column;
if (!(this.column instanceof Backgrid.Column)) {
this.column = new Backgrid.Column(this.column);
}
var collection = this.collection;
var selectedModels = this.selectedModels = {};
this.listenTo(collection.fullCollection || collection,
"backgrid:selected", function (model, selected, clearSelectedModels) {
if(clearSelectedModels) selectedModels = {};
if (selected) selectedModels[model.id || model.cid] = 1;
else {
delete selectedModels[model.id || model.cid];
this.checkbox().prop("checked", false);
}
if (_.keys(selectedModels).length === (collection.fullCollection|| collection).length) {
this.checkbox().prop("checked", true);
}
});
this.listenTo(collection.fullCollection || collection, "remove", function (model) {
delete selectedModels[model.id || model.cid];
if ((collection.fullCollection || collection).length === 0) {
this.checkbox().prop("checked", false);
}
});
this.listenTo(collection, "backgrid:refresh", function () {
if ((collection.fullCollection || collection).length === 0) {
this.checkbox().prop("checked", false);
}
else {
var checked = this.checkbox().prop("checked");
for (var i = 0; i < collection.length; i++) {
var model = collection.at(i);
if (checked || selectedModels[model.id || model.cid]) {
model.trigger("backgrid:select", model, true);
}
}
}
});
var column = this.column, $el = this.$el;
this.listenTo(column, "change:renderable", function (column, renderable) {
$el.toggleClass("renderable", renderable);
});
if (Backgrid.callByNeed(column.renderable(), column, collection)) $el.addClass("renderable");
},
/**
Propagates the checked value of this checkbox to all the models of the
underlying collection by triggering a Backbone `backgrid:select` event on
the models on the current page, passing each model and the current
`checked` value of the checkbox in each event.
A `backgrid:selected` event will also be triggered with the current
`checked` value on all the models regardless of whether they are on the
current page.
This method triggers a 'backgrid:select-all' event on the collection
afterwards.
*/
onChange: function () {
var checked = this.checkbox().prop("checked");
var collection = this.collection;
collection.each(function (model) {
model.trigger("backgrid:select", model, checked);
});
if (collection.fullCollection) {
collection.fullCollection.each(function (model) {
if (!collection.get(model.cid)) {
model.trigger("backgrid:selected", model, checked);
}
});
}
this.collection.trigger("backgrid:select-all", this.collection, checked);
}
});
/**
Convenient method to retrieve a list of selected models. This method only
exists when the `SelectAll` extension has been included. Selected models
are retained across pagination.
@member Backgrid.Grid
@return {Array.<Backbone.Model>}
*/
Backgrid.Grid.prototype.getSelectedModels = function () {
var selectAllHeaderCell;
var headerCells = this.header.row.cells;
for (var i = 0, l = headerCells.length; i < l; i++) {
var headerCell = headerCells[i];
if (headerCell instanceof SelectAllHeaderCell) {
selectAllHeaderCell = headerCell;
break;
}
}
var result = [];
if (selectAllHeaderCell) {
var selectedModels = selectAllHeaderCell.selectedModels;
var collection = this.collection.fullCollection || this.collection;
for (var modelId in selectedModels) {
result.push(collection.get(modelId));
}
}
return result;
};
/**
Convenient method to deselect the selected models. This method is only
available when the `SelectAll` extension has been included.
@member Backgrid.Grid
*/
Backgrid.Grid.prototype.clearSelectedModels = function () {
var selectedModels = this.getSelectedModels();
for (var i = 0, l = selectedModels.length; i < l; i++) {
var model = selectedModels[i];
model.trigger("backgrid:select", model, false);
}
};
Backbone.Picky = (function(Backbone, _) {
var Picky = {};
// Picky.SingleSelect
// ------------------
// A single-select mixin for Backbone.Collection, allowing a single
// model to be selected within a collection. Selection of another
// model within the collection causes the previous model to be
// deselected.
Picky.SingleSelect = function(collection) {
this.collection = collection;
};
_.extend(Picky.SingleSelect.prototype, {
// Select a model, deselecting any previously
// selected model
select: function(model) {
if (model && this.selected === model) {
return;
}
this.deselect();
this.selected = model;
this.selected.select();
this.trigger("select:one", model);
},
// Deselect a model, resulting in no model
// being selected
deselect: function(model) {
if (!this.selected) {
return;
}
model = model || this.selected;
if (this.selected !== model) {
return;
}
this.selected.deselect();
this.trigger("deselect:one", this.selected);
delete this.selected;
}
});
// Picky.MultiSelect
// -----------------
// A mult-select mixin for Backbone.Collection, allowing a collection to
// have multiple items selected, including `selectAll` and `selectNone`
// capabilities.
Picky.MultiSelect = function(collection) {
this.collection = collection;
this.selected = {};
};
_.extend(Picky.MultiSelect.prototype, {
// Select a specified model, make sure the
// model knows it's selected, and hold on to
// the selected model.
select: function(model) {
if (this.selected[model.id]) {
return;
}
this.selected[model.id] = model;
model.select();
calculateSelectedLength(this);
},
// Deselect a specified model, make sure the
// model knows it has been deselected, and remove
// the model from the selected list.
deselect: function(model) {
if (!this.selected[model.id]) {
return;
}
delete this.selected[model.id];
model.deselect();
calculateSelectedLength(this);
},
// Select all models in this collection
selectAll: function() {
this.each(function(model) {
model.select();
});
calculateSelectedLength(this);
},
// Deselect all models in this collection
selectNone: function() {
if (this.selectedLength === 0) {
return;
}
this.each(function(model) {
model.deselect();
});
calculateSelectedLength(this);
},
// Toggle select all / none. If some are selected, it
// will select all. If all are selected, it will select
// none. If none are selected, it will select all.
toggleSelectAll: function() {
if (this.selectedLength === this.length) {
this.selectNone();
} else {
this.selectAll();
}
}
});
// Picky.Selectable
// ----------------
// A selectable mixin for Backbone.Model, allowing a model to be selected,
// enabling it to work with Picky.MultiSelect or on it's own
Picky.Selectable = function(model) {
this.model = model;
};
_.extend(Picky.Selectable.prototype, {
// Select this model, and tell our
// collection that we're selected
select: function() {
if (this.selected) {
return;
}
this.selected = true;
this.trigger("selected", this);
if (this.collection) {
this.collection.select(this);
}
},
// Deselect this model, and tell our
// collection that we're deselected
deselect: function() {
if (!this.selected) {
return;
}
this.selected = false;
this.trigger("deselected", this);
if (this.collection) {
this.collection.deselect(this);
}
},
// Change selected to the opposite of what
// it currently is
toggleSelected: function() {
if (this.selected) {
this.deselect();
} else {
this.select();
}
}
});
// Helper Methods
// --------------
// Calculate the number of selected items in a collection
// and update the collection with that length. Trigger events
// from the collection based on the number of selected items.
var calculateSelectedLength = function(collection) {
collection.selectedLength = _.size(collection.selected);
var selectedLength = collection.selectedLength;
var length = collection.length;
if (selectedLength === length) {
collection.trigger("select:all", collection);
return;
}
if (selectedLength === 0) {
collection.trigger("select:none", collection);
return;
}
if (selectedLength > 0 && selectedLength < length) {
collection.trigger("select:some", collection);
return;
}
};
return Picky;
})(Backbone, _);
});