styling, tagging
This commit is contained in:
parent
466141adad
commit
67ed4565d4
1
Gemfile
1
Gemfile
@ -21,6 +21,7 @@ gem 'unicorn'
|
||||
gem 'bootstrap-sass', '<3.1'
|
||||
gem 'devise'
|
||||
gem 'jbuilder'
|
||||
gem 'bootstrap-tagsinput-rails'
|
||||
|
||||
|
||||
group :development do
|
||||
|
@ -50,6 +50,8 @@ GEM
|
||||
debug_inspector (>= 0.0.1)
|
||||
bootstrap-sass (3.0.3.0)
|
||||
sass (~> 3.2)
|
||||
bootstrap-tagsinput-rails (0.3.2.0)
|
||||
railties (>= 3.1)
|
||||
builder (3.2.2)
|
||||
capybara (2.1.0)
|
||||
mime-types (>= 1.16)
|
||||
@ -229,6 +231,7 @@ DEPENDENCIES
|
||||
better_errors
|
||||
binding_of_caller
|
||||
bootstrap-sass (< 3.1)
|
||||
bootstrap-tagsinput-rails
|
||||
capybara-webkit (>= 1.0.0)
|
||||
coffee-rails
|
||||
database_cleaner
|
||||
|
@ -12,4 +12,5 @@
|
||||
//
|
||||
//= require jquery
|
||||
//= require jquery_ujs
|
||||
//= require bootstrap-tagsinput
|
||||
//= require_tree .
|
||||
|
@ -3,5 +3,7 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@import 'bootstrap';
|
||||
@import 'bootstrap-tagsinput.css';
|
||||
@import 'campaign';
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -56,4 +56,27 @@ select {
|
||||
span {
|
||||
padding-left: 2em;
|
||||
}
|
||||
span.list-item {
|
||||
display:block;
|
||||
}
|
||||
}
|
||||
span.tag.label.label-info {
|
||||
display: block;
|
||||
margin-bottom:1px;
|
||||
|
||||
.tag-item {
|
||||
width:90%;
|
||||
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 */
|
||||
white-space: -o-pre-wrap; /* Opera 7 */
|
||||
white-space: pre-wrap; /* css-3 */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
word-break: break-all;
|
||||
white-space: normal;
|
||||
}
|
||||
}
|
||||
|
||||
div.bootstrap-tagsinput {
|
||||
width:100%;
|
||||
}
|
||||
|
||||
|
@ -24,17 +24,24 @@
|
||||
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-1"></div>
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-4 col-md-offset-1">
|
||||
<%= frm.input :advertisername,
|
||||
label: 'Advertiser Name',
|
||||
hint: "The name of the company." %>
|
||||
<%= frm.input :clientid,
|
||||
label: 'Client ID',
|
||||
hint: 'An identifier provided by BetterVideo.' %>
|
||||
<%= frm.input :listingcode,
|
||||
label: 'Listing Code',
|
||||
hint: "This should be the same as Intake Form's Form Hash." %>
|
||||
<%= frm.input :clientid,
|
||||
label: 'Client ID',
|
||||
hint: 'An identifier provided by BetterVideo.' %>
|
||||
<%= frm.input :description,
|
||||
label: 'Description',
|
||||
as: 'text',
|
||||
hint: "Used for script research." %>
|
||||
<%= frm.input :productsandservices,
|
||||
label: 'Products & Services',
|
||||
hint: "Used in script and video research." %>
|
||||
|
||||
<%= frm.input :websiteurl,
|
||||
label: 'Website URL',
|
||||
hint: "Used for script research and may be used in the video if they are including websites in the video graphics." %>
|
||||
@ -79,16 +86,11 @@
|
||||
label: 'Company Colors',
|
||||
hint: "Comma Separated.",
|
||||
placeholder: "ex: red, white, blue" %>
|
||||
<%= frm.input :description,
|
||||
label: 'Description',
|
||||
as: 'text',
|
||||
hint: "Used for script research." %>
|
||||
|
||||
<%= frm.input :facebookurl,
|
||||
label: 'Facebook URL',
|
||||
hint: "Used for script and photo research." %>
|
||||
<%= frm.input :productsandservices,
|
||||
label: 'Products & Services',
|
||||
hint: "Used in script and video research." %>
|
||||
|
||||
<%= frm.input :targetaudience,
|
||||
label: 'Target Audience',
|
||||
hint: "Used in script and video research." %>
|
||||
@ -97,7 +99,7 @@
|
||||
hint: "Toll-free phone number of business. Used in video creation." %>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<div class="col-md-3">
|
||||
<div class="videolisting">
|
||||
<%= frm.simple_fields_for(:videolistings, html: {class: 'form-horizontal'}) do |vidlist| %>
|
||||
<div class="<%= cycle('odd','even') %>">
|
||||
@ -130,15 +132,14 @@
|
||||
label: 'Notes',
|
||||
hint: "Notes and/or instructions sent in this field are used by the editors to aid in production of the video." %>
|
||||
<%= vidlist.input :asseturls,
|
||||
as: 'text',
|
||||
label: 'Asset Urls',
|
||||
placeholder: "ex: http://www.company.com/logo.png, http://www.company.com/background.png",
|
||||
hint: "A comma separated list of URLs of any assets." %>
|
||||
hint: "A list of all file assets.",
|
||||
input_html: {data: {role: "tagsinput" }}%>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1"></div>
|
||||
|
||||
</div>
|
||||
|
||||
|
@ -33,6 +33,9 @@
|
||||
</span>
|
||||
<span class="videolisting-item">
|
||||
<strong>asseturls</strong>
|
||||
<span><%= videolisting.asseturls %></span>
|
||||
|
||||
<% videolisting.asseturls_array.each do |asset| %>
|
||||
<span class="list-item"><%= asset[:asseturl] %></span>
|
||||
<% end %>
|
||||
</span>
|
||||
</li>
|
@ -2,99 +2,115 @@
|
||||
<%= link_to 'Back', campaigns_path %>
|
||||
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-12">
|
||||
<h1>
|
||||
<%= @campaign.advertisername %>
|
||||
</h1>
|
||||
<p id="notice"><%= notice %></p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<div class="col-md-2"></div>
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-4 col-md-offset-1 col-xs-6">
|
||||
<span class="campaign-item">
|
||||
<strong>Clientid</strong>
|
||||
<span><%= @campaign.clientid %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Listingcode</strong>
|
||||
<strong>Listing Code</strong>
|
||||
<span><%= @campaign.listingcode %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Billingcode</strong>
|
||||
<span><%= @campaign.billingcode %></span>
|
||||
<strong>Client Id</strong>
|
||||
<span><%= @campaign.clientid %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Address1</strong>
|
||||
<span><%= @campaign.address %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>City</strong>
|
||||
<span><%= @campaign.city %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>State</strong>
|
||||
<span><%= @campaign.state %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Zip</strong>
|
||||
<span><%= @campaign.zip %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Email Address</strong>
|
||||
<span><%= @campaign.emailaddress %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Customer First Name</strong>
|
||||
<span><%= @campaign.customerfirstname %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Customer Last Name</strong>
|
||||
<span><%= @campaign.customerlastname %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Businessphone</strong>
|
||||
<span><%= @campaign.businessphone %></span>
|
||||
</span>
|
||||
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Description</strong>
|
||||
<span><%= @campaign.description %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Contactphone</strong>
|
||||
<span><%= @campaign.contactphone %></span>
|
||||
<strong>Products & Services</strong>
|
||||
<span><%= @campaign.productsandservices %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Websiteurl</strong>
|
||||
<span><%= @campaign.websiteurl %></span>
|
||||
</span>
|
||||
|
||||
<span class="campaign-item">
|
||||
<strong>Client Services Rep</strong>
|
||||
<span><%= @campaign.vpa %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Billing Code</strong>
|
||||
<span><%= @campaign.billingcode %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Email Address</strong>
|
||||
<span><%= @campaign.emailaddress %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Business Phone</strong>
|
||||
<span><%= number_to_phone @campaign.businessphone %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Contact Phone</strong>
|
||||
<span><%= number_to_phone @campaign.contactphone %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Customer First Name</strong>
|
||||
<span><%= @campaign.customerfirstname %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Customer Last Name</strong>
|
||||
<span><%= @campaign.customerlastname %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Address</strong>
|
||||
<span><%= @campaign.address %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>City</strong>
|
||||
<span><%= @campaign.city %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>State</strong>
|
||||
<span><%= @campaign.state %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Zip</strong>
|
||||
<span><%= @campaign.zip %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Awards</strong>
|
||||
<span><%= @campaign.awards %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Background</strong>
|
||||
<span><%= @campaign.background %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Categories</strong>
|
||||
<span><%= @campaign.categories %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Company Colors</strong>
|
||||
<span><%= @campaign.companycolors %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Facebook URL</strong>
|
||||
<span><%= @campaign.facebookurl %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Target Audience</strong>
|
||||
<span><%= @campaign.targetaudience %></span>
|
||||
</span>
|
||||
<span class="campaign-item">
|
||||
<strong>Tollfree Phone</strong>
|
||||
<span><%= @campaign.tollfreephone %></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="col-md-4 col-xs-6">
|
||||
<ul>
|
||||
<% @campaign.videolistings.each do |videolisting| %>
|
||||
<%= render 'videolisting', videolisting: videolisting %>
|
||||
<% end %>
|
||||
|
||||
<% if @campaign.videolistings.empty? %>
|
||||
<li>No Video Listings for this Campaign.</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-md-2"></div>
|
||||
|
||||
</div>
|
@ -9,8 +9,8 @@
|
||||
</head>
|
||||
<body class="<%= body_class %>">
|
||||
<div class="col-md-12">
|
||||
<%= render 'layouts/header' %>
|
||||
<%= render 'flashes' -%>
|
||||
<%= render 'layouts/header' %>
|
||||
<%= yield %>
|
||||
</div>
|
||||
<%= render 'javascript' %>
|
||||
|
80
vendor/assets/javascripts/bootstrap-tagsinput-angular.js
vendored
Executable file
80
vendor/assets/javascripts/bootstrap-tagsinput-angular.js
vendored
Executable file
@ -0,0 +1,80 @@
|
||||
angular.module('bootstrap-tagsinput', [])
|
||||
.directive('bootstrapTagsinput', [function() {
|
||||
|
||||
function getItemProperty(scope, property) {
|
||||
if (!property)
|
||||
return undefined;
|
||||
|
||||
if (angular.isFunction(scope.$parent[property]))
|
||||
return scope.$parent[property];
|
||||
|
||||
return function(item) {
|
||||
return item[property];
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'EA',
|
||||
scope: {
|
||||
model: '=ngModel'
|
||||
},
|
||||
template: '<select multiple></select>',
|
||||
replace: false,
|
||||
link: function(scope, element, attrs) {
|
||||
$(function() {
|
||||
if (!angular.isArray(scope.model))
|
||||
scope.model = [];
|
||||
|
||||
var select = $('select', element);
|
||||
|
||||
select.tagsinput({
|
||||
typeahead : {
|
||||
source : angular.isFunction(scope.$parent[attrs.typeaheadSource]) ? scope.$parent[attrs.typeaheadSource] : null
|
||||
},
|
||||
itemValue: getItemProperty(scope, attrs.itemvalue),
|
||||
itemText : getItemProperty(scope, attrs.itemtext),
|
||||
tagClass : angular.isFunction(scope.$parent[attrs.tagclass]) ? scope.$parent[attrs.tagclass] : function(item) { return attrs.tagclass; }
|
||||
});
|
||||
|
||||
for (var i = 0; i < scope.model.length; i++) {
|
||||
select.tagsinput('add', scope.model[i]);
|
||||
}
|
||||
|
||||
select.on('itemAdded', function(event) {
|
||||
if (scope.model.indexOf(event.item) === -1)
|
||||
scope.model.push(event.item);
|
||||
});
|
||||
|
||||
select.on('itemRemoved', function(event) {
|
||||
var idx = scope.model.indexOf(event.item);
|
||||
if (idx !== -1)
|
||||
scope.model.splice(idx, 1);
|
||||
});
|
||||
|
||||
// create a shallow copy of model's current state, needed to determine
|
||||
// diff when model changes
|
||||
var prev = scope.model.slice();
|
||||
scope.$watch("model", function() {
|
||||
var added = scope.model.filter(function(i) {return prev.indexOf(i) === -1;}),
|
||||
removed = prev.filter(function(i) {return scope.model.indexOf(i) === -1;}),
|
||||
i;
|
||||
|
||||
prev = scope.model.slice();
|
||||
|
||||
// Remove tags no longer in binded model
|
||||
for (i = 0; i < removed.length; i++) {
|
||||
select.tagsinput('remove', removed[i]);
|
||||
}
|
||||
|
||||
// Refresh remaining tags
|
||||
select.tagsinput('refresh');
|
||||
|
||||
// Add new items in model as tags
|
||||
for (i = 0; i < added.length; i++) {
|
||||
select.tagsinput('add', added[i]);
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
}
|
||||
};
|
||||
}]);
|
405
vendor/assets/javascripts/bootstrap-tagsinput.js
vendored
Executable file
405
vendor/assets/javascripts/bootstrap-tagsinput.js
vendored
Executable file
@ -0,0 +1,405 @@
|
||||
(function ($) {
|
||||
"use strict";
|
||||
|
||||
var defaultOptions = {
|
||||
tagClass: function(item) {
|
||||
return 'label label-info';
|
||||
},
|
||||
itemValue: function(item) {
|
||||
return item ? item.toString() : item;
|
||||
},
|
||||
itemText: function(item) {
|
||||
return this.itemValue(item);
|
||||
},
|
||||
freeInput : true
|
||||
};
|
||||
|
||||
function TagsInput(element, options) {
|
||||
this.itemsArray = [];
|
||||
|
||||
this.$element = $(element);
|
||||
this.$element.hide();
|
||||
|
||||
this.isSelect = (element.tagName === 'SELECT');
|
||||
this.multiple = (this.isSelect && element.hasAttribute('multiple'));
|
||||
this.objectItems = options && options.itemValue;
|
||||
|
||||
this.$container = $('<div class="bootstrap-tagsinput"></div>');
|
||||
this.$input = $('<input size="40" type="text" />').appendTo(this.$container);
|
||||
|
||||
this.$element.after(this.$container);
|
||||
|
||||
this.build(options);
|
||||
}
|
||||
|
||||
TagsInput.prototype = {
|
||||
constructor: TagsInput,
|
||||
|
||||
add: function(item, dontPushVal) {
|
||||
var self = this;
|
||||
|
||||
// Ignore falsey values, except false
|
||||
if (item !== false && !item)
|
||||
return;
|
||||
|
||||
// Throw an error when trying to add an object while the itemValue option was not set
|
||||
if (typeof item === "object" && !self.objectItems)
|
||||
throw("Can't add objects when itemValue option is not set");
|
||||
|
||||
// Ignore strings only containg whitespace
|
||||
if (item.toString().match(/^\s*$/))
|
||||
return;
|
||||
|
||||
// If SELECT but not multiple, remove current tag
|
||||
if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
|
||||
self.remove(self.itemsArray[0]);
|
||||
|
||||
if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
|
||||
var items = item.split(',');
|
||||
if (items.length > 1) {
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
this.add(items[i], true);
|
||||
}
|
||||
|
||||
if (!dontPushVal)
|
||||
self.pushVal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore items allready added
|
||||
var itemValue = self.options.itemValue(item),
|
||||
itemText = self.options.itemText(item),
|
||||
tagClass = self.options.tagClass(item);
|
||||
|
||||
if ($.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0])
|
||||
return;
|
||||
|
||||
// register item in internal array and map
|
||||
self.itemsArray.push(item);
|
||||
|
||||
// add a tag element
|
||||
var $tag = $('<span class="tag ' + htmlEncode(tagClass) + '"><span class="tag-item">' + htmlEncode(itemText) + '</span><span class="remove" data-role="remove"></span></span>');
|
||||
$tag.data('item', item);
|
||||
self.$input.before($tag);
|
||||
|
||||
// add <option /> if item represents a value not present in one of the <select />'s options
|
||||
if (self.isSelect && !$('option[value="' + escape(itemValue) + '"]')[0]) {
|
||||
var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
|
||||
$option.data('item', item);
|
||||
$option.attr('value', itemValue);
|
||||
self.$element.append($option);
|
||||
}
|
||||
|
||||
if (!dontPushVal)
|
||||
self.pushVal();
|
||||
|
||||
self.$element.trigger($.Event('itemAdded', { item: item }));
|
||||
},
|
||||
|
||||
remove: function(item, dontPushVal) {
|
||||
var self = this;
|
||||
|
||||
if (self.objectItems) {
|
||||
if (typeof item === "object")
|
||||
item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } )[0];
|
||||
else
|
||||
item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } )[0];
|
||||
}
|
||||
|
||||
if (item) {
|
||||
$('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
|
||||
$('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
|
||||
self.itemsArray.splice(self.itemsArray.indexOf(item), 1);
|
||||
}
|
||||
|
||||
if (!dontPushVal)
|
||||
self.pushVal();
|
||||
|
||||
self.$element.trigger($.Event('itemRemoved', { item: item }));
|
||||
},
|
||||
|
||||
removeAll: function() {
|
||||
var self = this;
|
||||
|
||||
$('.tag', self.$container).remove();
|
||||
$('option', self.$element).remove();
|
||||
|
||||
while(self.itemsArray.length > 0)
|
||||
self.itemsArray.pop();
|
||||
|
||||
self.pushVal();
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
var self = this;
|
||||
$('.tag', self.$container).each(function() {
|
||||
var $tag = $(this),
|
||||
item = $tag.data('item'),
|
||||
itemValue = self.options.itemValue(item),
|
||||
itemText = self.options.itemText(item),
|
||||
tagClass = self.options.tagClass(item);
|
||||
|
||||
// Update tag's class and inner text
|
||||
$tag.attr('class', null);
|
||||
$tag.addClass('tag ' + htmlEncode(tagClass));
|
||||
$tag.contents().filter(function() {
|
||||
return this.nodeType == 3;
|
||||
})[0].nodeValue = htmlEncode(itemText);
|
||||
|
||||
if (self.isSelect) {
|
||||
var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
|
||||
option.attr('value', itemValue);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Returns the items added as tags
|
||||
items: function() {
|
||||
return this.itemsArray;
|
||||
},
|
||||
|
||||
// Assembly value by retrieving the value of each item, and set it on the element.
|
||||
pushVal: function() {
|
||||
var self = this,
|
||||
val = $.map(self.items(), function(item) {
|
||||
return self.options.itemValue(item).toString();
|
||||
});
|
||||
|
||||
self.$element.val(val, true).trigger('change');
|
||||
},
|
||||
|
||||
build: function(options) {
|
||||
var self = this;
|
||||
|
||||
self.options = $.extend({}, defaultOptions, options);
|
||||
var typeahead = self.options.typeahead || {};
|
||||
|
||||
// When itemValue is set, freeInput should always be false
|
||||
if (self.objectItems)
|
||||
self.options.freeInput = false;
|
||||
|
||||
makeOptionItemFunction(self.options, 'itemValue');
|
||||
makeOptionItemFunction(self.options, 'itemText');
|
||||
makeOptionItemFunction(self.options, 'tagClass');
|
||||
|
||||
// for backwards compatibility, self.options.source is deprecated
|
||||
if (self.options.source)
|
||||
typeahead.source = self.options.source;
|
||||
|
||||
if (typeahead.source && $.fn.typeahead) {
|
||||
makeOptionFunction(typeahead, 'source');
|
||||
|
||||
self.$input.typeahead({
|
||||
source: function (query, process) {
|
||||
function processItems(items) {
|
||||
var texts = [];
|
||||
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var text = self.options.itemText(items[i]);
|
||||
map[text] = items[i];
|
||||
texts.push(text);
|
||||
}
|
||||
process(texts);
|
||||
}
|
||||
|
||||
this.map = {};
|
||||
var map = this.map,
|
||||
data = typeahead.source(query);
|
||||
|
||||
if ($.isFunction(data.success)) {
|
||||
// support for Angular promises
|
||||
data.success(processItems);
|
||||
} else {
|
||||
// support for functions and jquery promises
|
||||
$.when(data)
|
||||
.then(processItems);
|
||||
}
|
||||
},
|
||||
updater: function (text) {
|
||||
self.add(this.map[text]);
|
||||
},
|
||||
matcher: function (text) {
|
||||
return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
|
||||
},
|
||||
sorter: function (texts) {
|
||||
return texts.sort();
|
||||
},
|
||||
highlighter: function (text) {
|
||||
var regex = new RegExp( '(' + this.query + ')', 'gi' );
|
||||
return text.replace( regex, "<strong>$1</strong>" );
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
self.$container.on('click', $.proxy(function(event) {
|
||||
self.$input.focus();
|
||||
}, self));
|
||||
|
||||
self.$container.on('keydown', 'input', $.proxy(function(event) {
|
||||
var $input = $(event.target);
|
||||
switch (event.which) {
|
||||
// BACKSPACE
|
||||
case 8:
|
||||
if (doGetCaretPosition($input[0]) === 0) {
|
||||
var prev = $input.prev();
|
||||
if (prev) {
|
||||
self.remove(prev.data('item'));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// DELETE
|
||||
case 46:
|
||||
if (doGetCaretPosition($input[0]) === 0) {
|
||||
var next = $input.next();
|
||||
if (next) {
|
||||
self.remove(next.data('item'));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
// LEFT ARROW
|
||||
case 37:
|
||||
// Try to move the input before the previous tag
|
||||
var $prevTag = $input.prev();
|
||||
if ($input.val().length === 0 && $prevTag[0]) {
|
||||
$prevTag.before($input);
|
||||
$input.focus();
|
||||
}
|
||||
break;
|
||||
// LEFT ARROW
|
||||
case 39:
|
||||
// Try to move the input before the previous tag
|
||||
var $nextTag = $input.next();
|
||||
if ($input.val().length === 0 && $nextTag[0]) {
|
||||
$nextTag.after($input);
|
||||
$input.focus();
|
||||
}
|
||||
break;
|
||||
// ENTER
|
||||
case 13:
|
||||
if (self.options.freeInput) {
|
||||
self.add($input.val());
|
||||
$input.val('');
|
||||
event.preventDefault();
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
$input.attr('size', Math.max(40, $input.val().length));
|
||||
}, self));
|
||||
|
||||
// Remove icon clicked
|
||||
self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
|
||||
self.remove($(event.target).closest('.tag').data('item'));
|
||||
}, self));
|
||||
|
||||
if (self.$element[0].tagName === 'INPUT') {
|
||||
self.add(self.$element.val());
|
||||
} else {
|
||||
$('option', self.$element).each(function() {
|
||||
self.add($(this).attr('value'), true);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
var self = this;
|
||||
|
||||
// Unbind events
|
||||
self.$container.off('keypress', 'input');
|
||||
self.$container.off('click', '[50role=remove]');
|
||||
|
||||
self.$container.remove();
|
||||
self.$element.removeData('tagsinput');
|
||||
self.$element.show();
|
||||
},
|
||||
|
||||
focus: function() {
|
||||
this.$input.focus();
|
||||
}
|
||||
};
|
||||
|
||||
// Register JQuery plugin
|
||||
$.fn.tagsinput = function(arg1, arg2) {
|
||||
var results = [];
|
||||
|
||||
this.each(function() {
|
||||
var tagsinput = $(this).data('tagsinput');
|
||||
|
||||
// Initialize a new tags input
|
||||
if (!tagsinput) {
|
||||
tagsinput = new TagsInput(this, arg1);
|
||||
$(this).data('tagsinput', tagsinput);
|
||||
results.push(tagsinput);
|
||||
|
||||
if (this.tagName === 'SELECT') {
|
||||
$('option', $(this)).attr('selected', 'selected');
|
||||
}
|
||||
|
||||
// Init tags from $(this).val()
|
||||
$(this).val($(this).val());
|
||||
} else {
|
||||
// Invoke function on existing tags input
|
||||
var retVal = tagsinput[arg1](arg2);
|
||||
if (retVal !== undefined)
|
||||
results.push(retVal);
|
||||
}
|
||||
});
|
||||
|
||||
if ( typeof arg1 == 'string') {
|
||||
// Return the results from the invoked function calls
|
||||
return results.length > 1 ? results : results[0];
|
||||
} else {
|
||||
return results;
|
||||
}
|
||||
};
|
||||
|
||||
$.fn.tagsinput.Constructor = TagsInput;
|
||||
|
||||
// Most options support both a string or number as well as a function as
|
||||
// option value. This function makes sure that the option with the given
|
||||
// key in the given options is wrapped in a function
|
||||
function makeOptionItemFunction(options, key) {
|
||||
if (typeof options[key] !== 'function') {
|
||||
var value = options[key];
|
||||
options[key] = function(item) { return item[value]; };
|
||||
}
|
||||
}
|
||||
function makeOptionFunction(options, key) {
|
||||
if (typeof options[key] !== 'function') {
|
||||
var value = options[key];
|
||||
options[key] = function() { return value; };
|
||||
}
|
||||
}
|
||||
// HtmlEncodes the given value
|
||||
var htmlEncodeContainer = $('<div />');
|
||||
function htmlEncode(value) {
|
||||
if (value) {
|
||||
return htmlEncodeContainer.text(value).html();
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the position of the caret in the given input field
|
||||
// http://flightschool.acylt.com/devnotes/caret-position-woes/
|
||||
function doGetCaretPosition(oField) {
|
||||
var iCaretPos = 0;
|
||||
if (document.selection) {
|
||||
oField.focus ();
|
||||
var oSel = document.selection.createRange();
|
||||
oSel.moveStart ('character', -oField.value.length);
|
||||
iCaretPos = oSel.text.length;
|
||||
} else if (oField.selectionStart || oField.selectionStart == '0') {
|
||||
iCaretPos = oField.selectionStart;
|
||||
}
|
||||
return (iCaretPos);
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
|
||||
});
|
||||
})(window.jQuery);
|
8
vendor/assets/javascripts/bootstrap-tagsinput.min.js
vendored
Executable file
8
vendor/assets/javascripts/bootstrap-tagsinput.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
1
vendor/assets/javascripts/bootstrap-tagsinput.min.js.map
vendored
Executable file
1
vendor/assets/javascripts/bootstrap-tagsinput.min.js.map
vendored
Executable file
File diff suppressed because one or more lines are too long
44
vendor/assets/stylesheets/bootstrap-tagsinput.css
vendored
Executable file
44
vendor/assets/stylesheets/bootstrap-tagsinput.css
vendored
Executable file
@ -0,0 +1,44 @@
|
||||
.bootstrap-tagsinput {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
vertical-align: middle;
|
||||
border-radius: 4px;
|
||||
max-width: 100%;
|
||||
line-height: 22px;
|
||||
}
|
||||
.bootstrap-tagsinput input {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: auto !important;
|
||||
max-width: inherit;
|
||||
}
|
||||
.bootstrap-tagsinput input:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.bootstrap-tagsinput .tag {
|
||||
margin-right: 5px;
|
||||
color: white;
|
||||
}
|
||||
.bootstrap-tagsinput .tag [data-role="remove"] {
|
||||
margin-left: 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.bootstrap-tagsinput .tag [data-role="remove"]:after {
|
||||
content: "x";
|
||||
padding: 0px 2px;
|
||||
}
|
||||
.bootstrap-tagsinput .tag [data-role="remove"]:hover {
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.bootstrap-tagsinput .tag [data-role="remove"]:hover:active {
|
||||
box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125);
|
||||
}
|
48
vendor/assets/stylesheets/bootstrap-tagsinput.less
vendored
Executable file
48
vendor/assets/stylesheets/bootstrap-tagsinput.less
vendored
Executable file
@ -0,0 +1,48 @@
|
||||
.bootstrap-tagsinput {
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
vertical-align: middle;
|
||||
border-radius: 4px;
|
||||
max-width: 100%;
|
||||
line-height: 22px;
|
||||
|
||||
input {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
width: auto !important;
|
||||
max-width: inherit;
|
||||
|
||||
&:focus {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tag {
|
||||
margin-right: 5px;
|
||||
color: white;
|
||||
|
||||
[data-role="remove"] {
|
||||
margin-left:8px;
|
||||
cursor:pointer;
|
||||
&:after{
|
||||
content: "x";
|
||||
padding:0px 2px;
|
||||
}
|
||||
&:hover {
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
&:active {
|
||||
box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user