diff --git a/dist/404.html b/dist/404.html new file mode 100644 index 000000000..ec98e3c26 --- /dev/null +++ b/dist/404.html @@ -0,0 +1,157 @@ + + + + + Page Not Found :( + + + +
+

Not found :(

+

Sorry, but the page you were trying to view does not exist.

+

It looks like this was the result of either:

+ + + +
+ + diff --git a/dist/favicon.ico b/dist/favicon.ico new file mode 100644 index 000000000..652790530 Binary files /dev/null and b/dist/favicon.ico differ diff --git a/dist/fonts/fontawesome-webfont.eot b/dist/fonts/fontawesome-webfont.eot new file mode 100644 index 000000000..a30335d74 Binary files /dev/null and b/dist/fonts/fontawesome-webfont.eot differ diff --git a/dist/fonts/fontawesome-webfont.svg b/dist/fonts/fontawesome-webfont.svg new file mode 100644 index 000000000..6fd19abcb --- /dev/null +++ b/dist/fonts/fontawesome-webfont.svg @@ -0,0 +1,640 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/fonts/fontawesome-webfont.ttf b/dist/fonts/fontawesome-webfont.ttf new file mode 100644 index 000000000..d7994e130 Binary files /dev/null and b/dist/fonts/fontawesome-webfont.ttf differ diff --git a/dist/fonts/fontawesome-webfont.woff b/dist/fonts/fontawesome-webfont.woff new file mode 100644 index 000000000..6fd4ede0f Binary files /dev/null and b/dist/fonts/fontawesome-webfont.woff differ diff --git a/dist/fonts/fontawesome-webfont.woff2 b/dist/fonts/fontawesome-webfont.woff2 new file mode 100644 index 000000000..5560193cc Binary files /dev/null and b/dist/fonts/fontawesome-webfont.woff2 differ diff --git a/dist/fonts/glyphicons-halflings-regular.eot b/dist/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 000000000..b93a4953f Binary files /dev/null and b/dist/fonts/glyphicons-halflings-regular.eot differ diff --git a/dist/fonts/glyphicons-halflings-regular.svg b/dist/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 000000000..94fb5490a --- /dev/null +++ b/dist/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/fonts/glyphicons-halflings-regular.ttf b/dist/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 000000000..1413fc609 Binary files /dev/null and b/dist/fonts/glyphicons-halflings-regular.ttf differ diff --git a/dist/fonts/glyphicons-halflings-regular.woff b/dist/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 000000000..9e612858f Binary files /dev/null and b/dist/fonts/glyphicons-halflings-regular.woff differ diff --git a/dist/fonts/glyphicons-halflings-regular.woff2 b/dist/fonts/glyphicons-halflings-regular.woff2 new file mode 100644 index 000000000..64539b54c Binary files /dev/null and b/dist/fonts/glyphicons-halflings-regular.woff2 differ diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 000000000..cf903f693 --- /dev/null +++ b/dist/index.html @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
You can't see this!
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/scripts/scripts.js b/dist/scripts/scripts.js new file mode 100644 index 000000000..9bfbd6c90 --- /dev/null +++ b/dist/scripts/scripts.js @@ -0,0 +1,14015 @@ +(function( w ){ + /* We can request an image at every possible width, but let's limit it to a reasonable number + We can set these so they correspond to our more common sizes. + */ + function tmpl(text, dict) { + for (var k in dict) { + text = text.replace("{{" + k + "}}", dict[k]); + } + return text; + } + w.picturefill = function(element) { + var ps; + if (element && element.getAttribute('data-type') === 'image') { + ps = [element]; + } + else { + if (typeof element === "undefined") { + element = w.document; + } + ps = element.getElementsByTagName( "div" ); + } + + var imageData = []; + for( var i = 0, il = ps.length; i < il; i++ ){ + var el = ps[i]; + if(el.getAttribute( "data-type" ) !== "image" ){ + continue; + } + var div = el.getElementsByTagName( "div" )[0]; + if( el.getAttribute( "data-image-id" ) !== null ){ + var id = el.getAttribute( "data-image-id" ), + crop = el.getAttribute( "data-crop" ); + var _w = div.offsetWidth, + _h = div.offsetHeight; + + if (!crop || crop === "" || crop === "auto") { + crop = computeAspectRatio(_w, _h); + } + if (el.getAttribute("data-format")) { + format = el.getAttribute("data-format"); + } + else { + format = "jpg"; + } + + var element = div; + if (id) { + $(".image-css-" + id).remove(); + $.ajax({ + url: w.BC_ADMIN_URL + '/api/' + id, + headers: { + 'X-Betty-Api-Key': w.BC_API_KEY, + 'Content-Type': undefined + }, + success: $.proxy(function (res) { + var imageData = res; + if (this.crop === "original") { + createStyle('.image[data-image-id="' + this.id + '"]>div', { + 'padding-bottom': ((res.height / res.width) * 100) + '%' + }, "image-css-" + this.id); + + var cropDetails = {x0:0, x1:res.width, y0:0, y1:res.height}; + } + else { + var cropDetails = imageData.selections[this.crop] + } + + computeStyle(this.element, imageData, cropDetails) + }, {element: element, id: id, crop:crop}), + error: $.proxy(function() { + if (this.crop === "original") { + //default to 16x9 + createStyle('.image[data-image-id="' + this.id + '"]>div', { + 'padding-bottom': '56.25%', // default to 16x9 for errors + 'background-color':'rgba(200, 0,0, .5)' + }, "image-css-" + this.id); + } + }, {id: id, crop:crop}) + }); + } + } + } + }; + + function computeStyle(element, image, selection) { + var selector = '.image[data-image-id="' + image.id + '"]>div'; + var elementWidth = $(selector).width(); + + var scale, styles, + elementHeight = (image.height / image.width) * elementWidth, + s_width = selection.x1 - selection.x0, + s_height = selection.y1 - selection.y0, + tmp_selection = selection; + + + if (!s_width || !s_height) { + /* + If we have bogus selections, make + the crop equal to the whole image + */ + s_width = elementWidth; + s_height = elementHeight; + tmp_selection = { + 'x0': 0, + 'y0': 0, + 'x1': s_width, + 'y1': s_height + }; + } + + var imageUrl = w.BC_ADMIN_URL + '/' + image.id + '/original/1200.jpg'; + scale = elementWidth / s_width; + + var rules = { + 'background-image': 'url(' + imageUrl +')', + 'background-size': scaleNumber(image.width, scale) + 'px', + 'background-position': + '-' + scaleNumber(tmp_selection.x0, scale) + 'px ' + + '-' + scaleNumber(tmp_selection.y0, scale) + 'px', + 'background-repeat': 'no-repeat' + } + createStyle(selector, rules, "image-css-" + image.id); + } + + function createStyle(selector, rules, classname) { + var styleNode = document.createElement("style"); + styleNode.type = "text/css"; + styleNode.className = classname; + var css = ""; + + var temp = "" + selector + '{'; + for (var rule in rules) { + temp += rule + ':' + rules[rule] + ';'; + } + temp += '}'; + css += temp; + + if (styleNode.styleSheet) { + styleNode.styleSheet.cssText = css; + } else { + styleNode.appendChild(document.createTextNode(css)); + } + $(document).find("head").append(styleNode); + } + + + function scaleNumber(num, by_scale) { + return Math.floor(num * by_scale); + }; + + + function computeAspectRatio(_w, _h) { + if (_w !== 0 && _h !== 0) { + var aspectRatio = Math.ceil(_w/_h * 10); + //smooth out rounding issues. + switch (aspectRatio) { + case 30: + case 31: + crop = "3x1"; + break; + case 20: + crop = "2x1"; + break; + case 14: + crop = "4x3"; + break; + case 18: + crop = "16x9"; + break; + case 8: + crop = "3x4"; + break; + case 10: + crop = "1x1"; + break; + default: + crop = "original"; + } + return crop; + } + else { + return "16x9" + } + } + + +}( this ));; +'use strict'; + +// ****** External Libraries ****** \\ + +angular.module('lodash', []).constant('_', window._); +angular.module('URLify', []).constant('URLify', window.URLify); +angular.module('jquery', []).constant('$', window.$); +angular.module('moment', []).constant('moment', window.moment); +angular.module('PNotify', []).constant('PNotify', window.PNotify); +angular.module('keypress', []).constant('keypress', window.keypress); +angular.module('Raven', []).constant('Raven', window.Raven); +angular.module('OnionEditor', []).constant('OnionEditor', window.OnionEditor); + +// ****** App Config ****** \\ + +angular.module('bulbsCmsApp', [ + 'bulbs.cms.site.config', + + 'bulbs.cms.superFeatures', + + // TODO : these dependencies need to be reorganized, localized + 'bulbs.cms.currentUser', + 'bulbs.cms.dateTimeModal', + 'bulbs.cms.dateTimeFilter', + 'bulbs.cms.editor', + 'bulbs.cms.imageCropModal', + 'bulbs.cms.liveBlog', + 'bulbs.cms.sendToEditorModal', + 'bulbs.cms.staticImage', + 'ngCookies', + 'ngResource', + 'ngRoute', + 'ui.bootstrap', + 'ui.bootstrap.datetimepicker', + 'restangular', + 'BettyCropper', + 'jquery', + 'lodash', + 'URLify', + 'moment', + 'PNotify', + 'keypress', + 'Raven', + 'firebase', + 'ipCookie', + 'bulbs.api', + + // shared + 'contentServices', + 'cms.tunic', + // components + 'bettyEditable', + 'bugReporter', + 'campaigns', + 'evergreenField', + 'hideFromRssField', + 'filterWidget', + 'filterListWidget', + 'notifications', + 'polls', + 'promotedContent', + 'statusFilter', + 'templateTypeField', + 'specialCoverage', + 'sections', + 'reports' +]) +.config(function ($locationProvider, $routeProvider, $sceProvider) { + $locationProvider.html5Mode(true); + + $routeProvider + .when('/cms/app/list/', { + templateUrl: '/views/contentlist.html', + controller: 'ContentlistCtrl', + reloadOnSearch: false + }) + .when('/cms/app/edit/:id/contributions/', { + templateUrl: '/views/contributions.html', + controller: 'ContributionsCtrl' + }) + .when('/cms/app/edit/:id/:contentType?', { + templateUrl: '/views/contentedit.html', + controller: 'ContenteditCtrl', + reloadOnSearch: false + }) + .when('/cms/app/targeting/', { + templateUrl: '/views/targeting-editor.html', + controller: 'TargetingCtrl' + }) + .when('/cms/app/cms-notifications/', { + templateUrl: '/views/cms-notifications.html', + controller: 'CmsNotificationsCtrl' + }) + .when('/cms/app/reporting/', { + templateUrl: '/views/reporting.html', + controller: 'ReportingCtrl' + }) + .when('/cms/app/pzones/', { + templateUrl: '/views/pzones.html', + controller: 'PzoneCtrl' + }) + .otherwise({ + redirectTo: '/cms/app/list/' + }); + + $sceProvider.enabled(false); +}) +.config(function ($provide, $httpProvider) { + $provide.decorator('$exceptionHandler', function ($delegate) { + return function (exception, cause) { + $delegate(exception, cause); + window.Raven.captureException(exception); + }; + }); + + $httpProvider.interceptors.push('BugReportInterceptor'); + $httpProvider.interceptors.push('PermissionsInterceptor'); + $httpProvider.interceptors.push('BadRequestInterceptor'); + $httpProvider.interceptors.push('TunicInterceptor'); +}) +.run(function ($rootScope, $http, $cookies) { + // set the CSRF token here + $http.defaults.headers.common['X-CSRFToken'] = $cookies.csrftoken; + var deleteHeaders = $http.defaults.headers.delete || {}; + deleteHeaders['X-CSRFToken'] = $cookies.csrftoken; + $http.defaults.headers.delete = deleteHeaders; +}); + +angular.module('bulbs.api', ['restangular', 'moment']); + +'use strict'; + +angular.module('bulbs.api') + .factory('AuthorService', function (Restangular) { + Restangular.setBaseUrl('/cms/api/v1/'); + Restangular.setRequestSuffix('/'); + Restangular.extendModel('author', function (obj) { + return angular.extend(obj, { + getFullName: function () { + return obj.first_name + ' ' + obj.last_name; + } + }); + }); + return Restangular.all('author'); + }); + +'use strict'; + +angular.module('bulbs.api') + .factory('ContentService', function (Restangular) { + Restangular.setBaseUrl('/cms/api/v1/'); + Restangular.setRequestSuffix('/'); + + Restangular.extendModel('content', function (obj) { + + var extendAuthor = function (author) { + return angular.extend(author, { + getFullName: function () { + return obj.contributor.first_name + ' ' + obj.contributor.last_name; + } + }); + }; + + for (var i in obj.authors) { + obj.authors[i] = extendAuthor(obj.authors[i]); + } + return obj; + }); + + Restangular.extendModel('contributions', function (obj) { + if (obj && obj.contributor) { + obj.contributor = angular.extend(obj.contributor, { + getFullName: function () { + return obj.contributor.first_name + ' ' + obj.contributor.last_name; + } + }); + } + return obj; + }); + + Restangular.extendCollection('contributions', function (collection) { + collection.save = function (data) { + return collection.post(data).then(function (contributions) { + return Restangular.restangularizeCollection('contributions', contributions); + }); + }; + return collection; + }); + + return Restangular.service('content'); + }); + +'use strict'; + +angular.module('bulbs.api') + .factory('ContributionRoleService', function (Restangular) { + return Restangular.withConfig(function (RestangularConfigurer) { + RestangularConfigurer.setBaseUrl('/cms/api/v1/contributions/'); + RestangularConfigurer.setRequestSuffix('/'); + }).service('role'); + }) + .factory('ContentReportingService', function (Restangular) { + return Restangular.withConfig(function (RestangularConfigurer) { + RestangularConfigurer.setBaseUrl('/cms/api/v1/contributions/'); + RestangularConfigurer.setRequestSuffix('/'); + }).service('contentreporting'); + }) + .factory('FreelancePayReportingService', function(Restangular, moment) { + return Restangular.withConfig(function (RestangularConfigurer) { + RestangularConfigurer.setBaseUrl('/cms/api/v1/contributions/'); + RestangularConfigurer.setRequestSuffix('/'); + }).service('freelancereporting'); + }) + .factory('ContributionReportingService', function (Restangular, moment) { + + Restangular.extendModel('reporting', function (obj) { + obj.user = angular.extend(obj.user, { + toString: function () { + return obj.user.full_name || obj.user.username; + } + }); + + obj.content = angular.extend(obj.content, { + toString: function () { + return obj.content.title + ' (' + moment(obj.content.published).format('MM/DD/YYYY h:mm a') + ')'; + }, + }); + return obj; + }); + + return Restangular.withConfig(function (RestangularConfigurer) { + RestangularConfigurer.setBaseUrl('/cms/api/v1/contributions/'); + RestangularConfigurer.setRequestSuffix('/'); + }).service('reporting'); + }); + +'use strict'; +(function () { + angular.module('BettyCropper', [ + 'bulbs.cms.site.config', + 'restangular', + 'jquery' + ]) + .factory('Selection', SelectionFactory) + .factory('BettyImage', BettyImageFactory) + .service('BettyCropper', BettyCropperService); + + function BettyCropperService($http, $interpolate, $q, CmsConfig, BettyImage, $) { + var fileInputId = '#bulbs-cms-hidden-image-file-input'; + var inputTemplate = ''; + + this.upload = upload; + this.get = get; + this.detail = get; + this.detailPatch = detailPatch; + this.updateSelection = updateSelection; + + function upload() { + var uploadImageDeferred = $q.defer(); + + angular.element(fileInputId).remove(); + var fileInput = angular.element(inputTemplate); + angular.element('body').append(fileInput); + + fileInput.click(); + + fileInput.unbind('change'); + fileInput.bind('change', function (e) { + if (e.target.files.length !== 1) { + uploadImageDeferred.reject('We need exactly one image!'); + } + var file = e.target.files[0]; + if (file.type.indexOf('image/') !== 0) { + uploadImageDeferred.reject('Not an image!'); + } + + if (file.size > 10 * 1024 * 1024) { // MAGIC! + uploadImageDeferred.reject('The file is too large!'); + } + + var imageData = new FormData(); + imageData.append('image', file); + + $http({ + method: 'POST', + url: CmsConfig.buildImageApiUrl('api/new'), + headers: { + 'X-Betty-Api-Key': CmsConfig.getImageApiKey(), + 'Content-Type': undefined, + 'X-CSRFToken': undefined + }, + data: imageData, + transformRequest: angular.identity, + transformResponse: function (data, headersGetter) { + // So, sometimes the browser doesn't think that JSON data is JSON. + if (typeof data === 'string') { + data = $.parseJSON(data); + } + var image = new BettyImage(data); + return image; + } + }).success(function (success) { + uploadImageDeferred.resolve(success); + }).error(function (error) { + uploadImageDeferred.reject(error); + }); + + }); + + return uploadImageDeferred.promise; + } + + function get(id) { + return $http({ + method: 'GET', + url: CmsConfig.buildImageApiUrl('api/' + id), + headers: { + 'X-Betty-Api-Key': CmsConfig.getImageApiKey(), + 'Content-Type': undefined, + 'X-CSRFToken': undefined + }, + transformRequest: angular.identity, + transformResponse: function (data, headersGetter) { + if (typeof data === 'string') { + data = $.parseJSON(data); + } + return new BettyImage(data); + } + }); + } + + function detailPatch(id, name, credit, selections) { + return $http({ + method: 'PATCH', + url: CmsConfig.buildImageApiUrl('api/' + id), + headers: { + 'X-Betty-Api-Key': CmsConfig.getImageApiKey(), + 'Content-Type': undefined, + 'X-CSRFToken': undefined + }, + data: { + name: name, + credit: credit, + selections: selections + }, + transformRequest: angular.identity, + transformResponse: function (data, headersGetter) { + if (typeof data === 'string') { + data = $.parseJSON(data); + } + return new BettyImage(data); + } + }); + } + + function updateSelection(id, ratio, selections) { + return $http({ + method: 'POST', + url: CmsConfig.buildImageApiUrl('api/' + id + '/' + ratio), + headers: { + 'X-Betty-Api-Key': CmsConfig.getImageApiKey(), + 'Content-Type': undefined, + 'X-CSRFToken': undefined + }, + data: selections + }); + } + } + + function BettyImageFactory($interpolate, $http, CmsConfig, Selection, $) { + function BettyImage(data) { + this.id = data.id; + this.name = data.name; + this.width = data.width; + this.height = data.height; + this.selections = {}; + for (var ratio in data.selections) { + this.selections[ratio] = new Selection(data.selections[ratio]); + } + } + + BettyImage.prototype.scaleToFit = function (width, height) { + var scale; + if (width && height) { + var fitRatio = width / height; + var thisRatio = this.width / this.height; + if (fitRatio > thisRatio) { + scale = height / this.height; + } else { + scale = width / this.width; + } + } else { + if (width) { + scale = width / this.width; + } + if (height) { + scale = height / this.height; + } + } + var scaled = { + width: Math.round(this.width * scale), + height: Math.round(this.height * scale), + scale: scale + }; + return scaled; + }; + + BettyImage.prototype.getStyles = function (width, height, ratio) { + if (height === 0) { + height = null; + } + + var selection = this.selections[ratio]; + var scaledSelection = selection.scaleToFit(width, height); + + return { + 'background-image': 'url(' + this.url('original', 1200, 'jpg') + ')', + 'background-size': Math.floor(scaledSelection.width() / selection.width() * this.width) + 'px', + 'background-position': '-' + scaledSelection.x0 + 'px -' + scaledSelection.y0 + 'px', + 'height': scaledSelection.height() + 'px', + 'width': scaledSelection.width() + 'px', + 'background-repeat': 'no-repeat', + 'position': 'relative' + }; + }; + + BettyImage.prototype.url = function (ratio, width, format) { + var exp = $interpolate( + '{{ base_url }}/{{ id }}/{{ ratio }}/{{ width }}.{{ format }}' + ); + var idStr = this.id.toString(); + var segmentedId = ''; + for (var i = 0; i < idStr.length; i++) { + if (i % 4 === 0 && i !== 0) { + segmentedId += '/'; + } + segmentedId += idStr.substr(i, 1); + } + return exp({ + base_url: CmsConfig.buildImageApiUrl(), + id: segmentedId, + ratio: ratio, + width: width, + format: format + }); + }; + + BettyImage.prototype.updateSelection = function (ratio, selection) { + var data = { + x0: selection.x0, + x1: selection.x1, + y0: selection.y0, + y1: selection.y1 + }; + if (selection.source) { + data.source = selection.source; + } + return $http({ + method: 'POST', + url: CmsConfig.buildImageApiUrl('api/' + this.id + '/' + ratio), + headers: { + 'X-Betty-Api-Key': CmsConfig.getImageApiKey(), + 'Content-Type': undefined, + 'X-CSRFToken': undefined + }, + data: data, + transformResponse: function (data, headersGetter) { + if (typeof data === 'string') { + data = $.parseJSON(data); + } + return [ratio, new Selection(data.selections[ratio])]; + } + }); + }; + + return BettyImage; + } + + function SelectionFactory() { + function Selection(data) { + this.x0 = data.x0; + this.x1 = data.x1; + this.y0 = data.y0; + this.y1 = data.y1; + this.source = data.source; + } + + Selection.prototype.width = function () { + return this.x1 - this.x0; + }; + + Selection.prototype.height = function () { + return this.y1 - this.y0; + }; + + Selection.prototype.scaleBy = function (scale) { + var scaledToFit = new Selection({ + x0: Math.round(this.x0 * scale), + x1: Math.round(this.x1 * scale), + y0: Math.round(this.y0 * scale), + y1: Math.round(this.y1 * scale) + }); + return scaledToFit; + }; + + Selection.prototype.scaleToFit = function (width, height) { + + var scale; + if (width && height) { + var fitRatio = width / height; + var thisRatio = this.width() / this.height(); + if (fitRatio > thisRatio) { + scale = height / this.height(); + } else { + scale = width / this.width(); + } + } else { + if (width) { + scale = width / this.width(); + } + if (height) { + scale = height / this.height(); + } + } + return this.scaleBy(scale); + }; + + return Selection; + } +})(); + +'use strict'; + +/** + * Autocomplete directive that should cover most autocomplete situations. + */ +angular.module('autocompleteBasic', [ + 'bulbs.cms.site.config', + 'BulbsAutocomplete', + 'BulbsAutocomplete.suggest', + 'lodash' +]) + .value('AUTOCOMPLETE_BASIC_DEBOUNCE', 200) + .directive('autocompleteBasic', [ + '_', 'CmsConfig', + function (_, CmsConfig) { + return { + controller: [ + '$scope', 'BULBS_AUTOCOMPLETE_EVENT_KEYPRESS', + 'AUTOCOMPLETE_BASIC_DEBOUNCE', + function ($scope, BULBS_AUTOCOMPLETE_EVENT_KEYPRESS, + AUTOCOMPLETE_BASIC_DEBOUNCE) { + + $scope.writables = { + searchTerm: '' + }; + + $scope.autocompleteItems = []; + + var $getItems = function () { + return $scope.searchFunction($scope.writables.searchTerm) + .then(function (data) { + return _.map(data, function (item) { + return { + name: $scope.displayFormatter(item), + value: item + }; + }); + }); + }; + + $scope.updateAutocomplete = _.debounce(function () { + if ($scope.writables.searchTerm) { + $getItems().then(function (results) { + $scope.autocompleteItems = results; + }); + } + }, AUTOCOMPLETE_BASIC_DEBOUNCE); + + $scope.delayClearAutocomplete = function () { + _.delay(function () { + $scope.clearAutocomplete(); + $scope.$digest(); + }, 200); + }; + + $scope.clearAutocomplete = function () { + $scope.writables.searchTerm = ''; + $scope.autocompleteItems = []; + }; + + $scope.clearSelectionOverlay = function () { + $scope.clearAutocomplete(); + $scope.showSelectionOverlay = false; + $scope.updateNgModel(); + $scope.onSelect({}); + }; + + $scope.handleKeypress = function ($event) { + if ($event.keyCode === 27) { + // esc, close dropdown + $scope.clearAutocomplete(); + } else if ($event.keyCode === 40 && _.isEmpty($scope.autocompleteItems)) { + // down key and no items in autocomplete, redo search + $scope.updateAutocomplete(); + } else { + $scope.$broadcast(BULBS_AUTOCOMPLETE_EVENT_KEYPRESS, $event); + } + }; + + $scope.handleSelect = function (selection) { + if (selection && $scope.updateNgModel) { + $scope.updateNgModel(selection); + $scope.showSelectionOverlay = true; + } + + $scope.clearAutocomplete(); + $scope.onSelect({selection: selection}); + }; + } + ], + link: function (scope, iElement, iAttrs, ngModelCtrl) { + var defaultFormatter = function (context) { + return context.item; + }; + + scope.valueFormatter = function (viewValue) { + return (scope.itemValueFormatter || defaultFormatter)({ item: viewValue }); + }; + + scope.displayFormatter = function (modelValue) { + return (scope.itemDisplayFormatter || defaultFormatter)({ item: modelValue }); + }; + + if (ngModelCtrl) { + + ngModelCtrl.$formatters.push(function (modelValue) { + return scope.displayFormatter(modelValue); + }); + + ngModelCtrl.$render = function () { + scope.selectedValue = ngModelCtrl.$viewValue; + }; + + ngModelCtrl.$parsers.push(function (viewValue) { + return scope.valueFormatter(viewValue); + }); + + var unbindInitialValue = scope.$watch('initialValue', function () { + scope.selectedValue = scope.initialValue; + }); + + scope.updateNgModel = function (selection) { + unbindInitialValue(); + var newValue = _.isUndefined(selection) ? null : angular.copy(selection.value); + ngModelCtrl.$setViewValue(angular.copy(newValue)); + scope.selectedValue = scope.displayFormatter(newValue); + }; + } + }, + require: '?ngModel', // optionally provide ng-model to have bind with an actual property + restrict: 'E', + scope: { + hideSearchIcon: '&', // true to hide search icon inside autocomplete + inputId: '@', // id to give input, useful if input has a label + inputPlaceholder: '@', // placeholder for input + initialValue: '=', // initial representation of selected value + itemDisplayFormatter: '&', // formatter to transform the display name of result + itemValueFormatter: '&', // formatter to transform the value of the result + onSelect: '&', // selection callback, recieves selection as argument + searchFunction: '=', // function to use for searching autocomplete results + ngDisabled: '&' + }, + templateUrl: CmsConfig.buildComponentPath('autocomplete-basic/autocomplete-basic.html') + }; + } + ]); + +'use strict'; + +angular.module('bettyEditable', [ + 'BettyCropper', + 'bulbs.cms.imageCropModal', + 'bulbs.cms.site.config', + 'ui.bootstrap.modal' +]) + .directive('bettyEditable',[ + '$http', 'CmsConfig', 'BettyCropper', 'openImageCropModal', + function ($http, CmsConfig, BettyCropper, openImageCropModal) { + return { + restrict: 'E', + templateUrl: CmsConfig.buildComponentPath('betty-editable/betty-editable.html'), + scope: { + addStyles: '@', + editable: '=?', + hideMetas: '=', + image: '=', + placeholderText: '@', + ratio: '@', + onChange: '&' + }, + controller: function ($scope, $element) { + $scope.editable = angular.isDefined($scope.editable) ? $scope.editable : true; + $scope.upload = function (e) { + BettyCropper.upload().then( + function (success) { + $scope.image = { + id: success.id, + caption: null, + alt: null + }; + $scope.bettyImage = success; + }, + function (error) { + console.log(error); + }, + function (progress) { + console.log(progress); + } + ); + }; + + $scope.edit = function (e) { + openImageCropModal($scope.image).then(function (image) { + if (image.id === null) { + $scope.image = null; + } else { + $scope.image = image; + BettyCropper.get($scope.image.id).then(function (response) { + $scope.bettyImage = response.data; + $scope.setStyles(); + }); + } + }); + }; + }, + + link: function (scope, element, attrs) { + + if (scope.bettyImage === undefined) { + scope.bettyImage = null; + } + + scope.setStyles = function () { + if (scope.bettyImage) { + scope.imageStyling = scope.bettyImage.getStyles(element.parent().width(), null, scope.ratio); + } else { + var ratioWidth = parseInt(scope.ratio.split('x')[0], 10); + var ratioHeight = parseInt(scope.ratio.split('x')[1], 10); + scope.imageStyling = { + 'background-color': '#333', + 'position': 'relative', + 'width': element.parent().width(), + 'height': Math.floor(element.parent().width() * ratioHeight / ratioWidth) + 'px', + }; + } + }; + + scope.$watch('image', function (newImage, oldImage) { + if (newImage && newImage.id) { + BettyCropper.get(newImage.id).then(function (response) { + scope.bettyImage = response.data; + if (!oldImage || parseInt(response.data.id, 10) !== oldImage.id) { + scope.onChange(); + } + }); + } else if (!angular.equals(newImage, oldImage)) { + // image cleared out + scope.onChange(); + } + }); + + scope.$watch('bettyImage', function (newImage, oldImage) { + scope.setStyles(); + }, true); + + element.resize(scope.setStyles); + + scope.removeImage = function () { + scope.image = null; + }; + + scope.editImage = function () { + openImageCropModal(scope.image) + .then(function (success) { + console.log(success); + }); + }; + + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.breadcrumb', [ + 'bulbs.cms.site.config', + 'bulbs.cms.superFeatures.api' +]) + .directive('breadcrumb', [ + '$sce', 'CmsConfig', + function ($sce, CmsConfig) { + return { + scope: { + linksList: '=' + }, + link: function (scope, element, attrs) { + scope.renderValue = function (value) { + return $sce.trustAsHtml(angular.isFunction(value) ? value() : value); + }; + }, + templateUrl: CmsConfig.buildComponentPath( + 'breadcrumb', + 'breadcrumb.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bugReporter', [ + 'bulbs.cms.config' +]) + .directive('bugReporter', [ + '$http', '$window', 'CmsConfig', + function ($http, $window, CmsConfig) { + return { + restrict: 'E', + templateUrl: CmsConfig.buildComponentPath('bug-reporter/bug-reporter-button.html'), + scope: {}, + controller: function ($scope, $element, $timeout) { + $scope.report = {}; + $scope.reportButton = { + idle: 'Submit', + busy: 'Sending', + finished: 'Sent!', + error: 'Error!' + }; + + $scope.modalVisible = false; + $scope.showThankYou = false; + + $scope.showModal = function () { + $scope.modalVisible = true; + }; + + $scope.dismissModal = function () { + $scope.modalVisible = false; + $scope.showThankYou = false; + }; + + $scope.sendToWebtech = function () { + var report = + 'When I tried to:\n\n' + $scope.report.firstRes + '\n\n' + + 'I thought this would happen:\n\n' + $scope.report.secondRes + '\n\n' + + '...but this happened instead:\n\n' + $scope.report.thirdRes + ; + var data = { + report: report, + url: $window.location.href, + user_agent: $window.navigator.userAgent + }; + return $http.post('/cms/api/v1/report-bug/', data); + }; + + $scope.sendToWebtechCbk = function (promise) { + promise + .then(function () { + $scope.showThankYou = true; + $timeout(function () { + $scope.dismissModal(); + for (var entry in $scope.report) { + $scope.report[entry] = ''; + } + }, 5000); + }); + }; + + /* + Exposing this globally for PNotify. + Will revisit when we review how to + report bugs on the CMS. + */ + $window.showBugReportModal = function () { + $scope.$apply($scope.showModal()); + }; + + }, + link: function (scope, element) { + + } + }; + } + ]); + +'use strict'; + +angular.module('campaignAutocomplete', [ + 'autocompleteBasic', + 'bulbs.cms.site.config', + 'cms.tunic.config', + 'lodash', + 'uuid4' +]) + .directive('campaignAutocomplete', [ + '$http', 'CmsConfig', 'TunicConfig', 'uuid4', '_', + function ($http, CmsConfig, TunicConfig, uuid4, _) { + return { + controller: [ + '$scope', + function ($scope) { + + $scope.itemDisplayFormatter = function (campaign) { + if (_.isObject(campaign)) { + return campaign.name + ' - ' + campaign.id; + } + }; + + $scope.itemValueFormatter = function (campaign) { + return _.isObject(campaign) ? campaign.id : null; + }; + + $scope.searchCampaigns = function (searchTerm) { + return $http + .get(TunicConfig.buildBackendApiUrl('campaign/'), { + params: { search: searchTerm } + }) + .then(function (response) { + return response.data.results; + }); + }; + } + ], + link: function (scope, iElement, iAttrs, ngModelCtrl) { + scope.uuid = uuid4.generate(); + + if (ngModelCtrl) { + + scope.ngModel = ngModelCtrl; + + ngModelCtrl.$render = function () { + if (_.isNumber(ngModelCtrl.$modelValue) && !scope.initialValue) { + $http + .get(TunicConfig.buildBackendApiUrl('campaign/' + ngModelCtrl.$modelValue + '/')) + .then(function (result) { + scope.initialValue = scope.itemDisplayFormatter(result.data); + }); + } + }; + + scope.onSelect = function (selection) { + ngModelCtrl.$commitViewValue(); + }; + } + }, + restrict: 'E', + templateUrl: CmsConfig.buildComponentPath('campaign-autocomplete/campaign-autocomplete.html'), + require: 'ngModel', + scope: { + label: '@campaignAutocompleteLabel', // label for the autocomplete imput + onSelect: '&campaignAutocompleteOnSelect' // selection handler for auto completions + } + }; + } + ]); + +'use strict'; + +angular.module('campaigns.edit.directive', [ + 'apiServices.campaign.factory', + 'BettyCropper', + 'bulbs.cms.site.config', + 'campaigns.edit.sponsorPixel', + 'lodash', + 'saveButton.directive', + 'topBar' +]) + .directive('campaignsEdit', function (CmsConfig) { + return { + controller: function (_, $location, $q, $routeParams, $scope, Campaign) { + + // populate model for use + if ($routeParams.id === 'new') { + $scope.model = Campaign.$build(); + $scope.isNew = true; + } else { + $scope.model = Campaign.$find($routeParams.id); + } + + window.onbeforeunload = function (e) { + if (!_.isEmpty($scope.model.$dirty()) || $scope.isNew || $scope.needsSave) { + // unsaved changes, show confirmation alert + return 'You have unsaved changes.'; + } + }; + + $scope.$on('$destroy', function() { + // ensure even is cleaned up when we leave + delete window.onbeforeunload; + }); + + $scope.addPixel = function () { + var pixel = { + url: '', + campaign_type: '' + }; + $scope.model.pixels.push(pixel); + }; + + $scope.deletePixel = function (pixel) { + $scope.model.pixels = _.without($scope.model.pixels, pixel); + }; + + // set up save state function + $scope.saveModel = function () { + var promise; + + if ($scope.model) { + // have model, use save promise as deferred + promise = $scope.model.$save().$asPromise().then(function (data) { + $location.path('/cms/app/campaigns/edit/' + data.id + '/'); + }); + } else { + // no model, this is an error, defer and reject + var deferred = $q.defer(); + deferred.reject(); + promise = deferred.promise; + } + + return promise; + }; + }, + restrict: 'E', + scope: { + getModelId: '&modelId' + }, + templateUrl: CmsConfig.buildComponentPath('campaigns/campaigns-edit/campaigns-edit.html'), + }; + }); + +'use strict'; + +angular.module('campaigns.edit.sponsorPixel.directive', [ + 'bulbs.cms.config' +]) + .constant('PIXEL_TYPES', [{ + name: 'Listing', + value: 'Listing' + }, { + name: 'Detail', + value: 'Detail' + }]) + .directive('campaignsEditSponsorPixel', function (CmsConfig) { + return { + controller: function($scope, PIXEL_TYPES) { + $scope.PIXEL_TYPES = PIXEL_TYPES; + }, + restrict: 'E', + scope: { + model: '=' + }, + templateUrl: CmsConfig.buildComponentPath('campaigns/campaigns-edit/campaigns-edit-sponsor-pixel/campaigns-edit-sponsor-pixel.html') + }; + }); + +'use strict'; + +angular.module('campaigns.edit.sponsorPixel', [ + 'campaigns.edit.sponsorPixel.directive' +]); + +'use strict'; + +angular.module('campaigns.edit', [ + 'bulbs.cms.site.config', + 'campaigns.edit.directive' +]) + .config(function ($routeProvider) { + $routeProvider + .when('/cms/app/campaigns/edit/:id/', { + controller: function ($routeParams, $scope, $window, CmsConfig) { + $window.document.title = CmsConfig.getCmsName() + ' | Edit Campaign'; + $scope.routeId = $routeParams.id; + }, + template: '', + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('campaigns.list', [ + 'apiServices.campaign.factory', + 'bulbs.cms.site.config', + 'listPage', + 'moment' +]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/campaigns/', { + controller: function ($scope, $window, Campaign) { + $window.document.title = CmsConfig.getCmsName() + ' | Campaign'; + $scope.modelFactory = Campaign; + }, + templateUrl: CmsConfig.buildComponentPath('campaigns/campaigns-list/campaigns-list-page.html') + }); + }); + +'use strict'; + +angular.module('campaigns', [ + 'campaigns.edit', + 'campaigns.list' +]); + +'use strict'; + +angular.module('confirmationModal', [ + 'confirmationModal.factory', + 'ui.bootstrap' +]) + .directive('confirmationModalOpener', function (ConfirmationModal) { + return { + restrict: 'A', + scope: { + modalBody: '@', + modalCancelText: '@', + modalOkText: '@', + modalOnCancel: '&', + modalOnOk: '&', + modalTitle: '@' + }, + link: function (scope, element) { + var modalInstance = null; + element.addClass('confirmation-modal-opener'); + element.on('click', function () { + modalInstance = new ConfirmationModal(scope); + }); + } + }; + }); + +'use strict'; + +angular.module('confirmationModal.factory', [ + 'bulbs.cms.site.config', + 'ui.bootstrap.modal' +]) + .factory('ConfirmationModal', function ($modal, CmsConfig) { + + var ConfirmationModal = function (scope) { + return (function (scope) { + $modal + .open({ + controller: function ($scope, $modalInstance) { + $scope.confirm = function () { + $scope.$close(); + $scope.modalOnOk(); + }; + + $scope.cancel = function () { + $scope.$dismiss(); + $scope.modalOnCancel(); + }; + }, + scope: scope, + templateUrl: CmsConfig.buildComponentPath('confirmation-modal/confirmation-modal.html') + }); + })(scope); + }; + + return ConfirmationModal; + }); + +'use strict'; + +angular.module('bulbs.cms.contentSearch', [ + 'autocompleteBasic', + 'bulbs.cms.site.config', + 'contentServices.factory', + 'lodash' +]) + .directive('contentSearch', [ + '_', 'CmsConfig', 'ContentFactory', + function (_, CmsConfig, ContentFactory) { + + return { + link: function (scope, element, attrs, ngModelCtrl) { + + scope.itemDisplayFormatter = function (content) { + if (_.isObject(content)) { + return content.title + ' - ' + content.id; + } + }; + + scope.itemValueFormatter = function (content) { + // relatively heavy handed transformation to an id, will have to + // reconsider this if another usage for this search comes up where + // we need to return the entire content piece. + return _.isObject(content) ? content.id : null; + }; + + scope.searchContent = function (searchTerm) { + return ContentFactory.all('content') + .getList({ search: searchTerm }); + }; + + if (ngModelCtrl) { + + scope.ngModel = ngModelCtrl; + + ngModelCtrl.$render = function () { + if (_.isNumber(ngModelCtrl.$modelValue) && !scope.initialValue) { + ContentFactory.one('content', ngModelCtrl.$modelValue).get() + .then(function (content) { + scope.initialValue = scope.itemDisplayFormatter(content); + }); + } + }; + + scope.autocompleteOnSelect = function () { + ngModelCtrl.$commitViewValue(); + }; + } else { + scope.autocompleteOnSelect = function (selection) { + scope.onSelect({ selection: scope.itemValueFormatter(selection.value) }); + }; + } + }, + require: '?ngModel', + restrict: 'E', + scope: { + inputId: '@', + onSelect: '&', + ngDisabled: '&' + }, + templateUrl: CmsConfig.buildComponentPath( + 'content-search', + 'content-search.html' + ) + }; + } + ]); + + +'use strict'; + +angular.module('bulbs.cms.currentUser.api', [ + 'bulbs.cms.site.config', + 'lodash' +]) + .service('CurrentUserApi', [ + '_', '$http', '$q', 'CmsConfig', + function (_, $http, $q, CmsConfig) { + + var currentUser; + var pendingGetCurrentUserRequest; + + var parsePayload = function (payload) { + var data = _.cloneDeep(payload); + + data.displayName = + data.first_name && data.last_name ? + (data.first_name + ' ' + data.last_name) : + (data.email || data.username); + + return data; + }; + + var getCurrentUser = function () { + if (!pendingGetCurrentUserRequest) { + pendingGetCurrentUserRequest = $http.get(CmsConfig.buildApiUrlRoot('me/')) + .then(function (response) { + currentUser = parsePayload(response.data); + return currentUser; + }) + .finally(function () { + pendingGetCurrentUserRequest = null; + }); + } + + return pendingGetCurrentUserRequest; + }; + + return { + getCurrentUserWithCache: function () { + if (currentUser) { + return $q.when(currentUser); + } + return getCurrentUser(); + }, + logout: function () { + return $http.get(CmsConfig.buildApiUrlRoot('me', 'logout/')) + .then(function () { + currentUser = null; + }); + } + }; + }] + ); + +'use strict'; + +angular.module('bulbs.cms.currentUser', [ + 'bulbs.cms.currentUser.api', + 'bulbs.cms.user.nameDisplayFilter' +]); + +'use strict'; + +angular.module('bulbs.cms.user.nameDisplayFilter', []) + .filter('userNameDisplay', [ + function () { + + return function (user) { + if (!user) { return ''; } + if (user.full_name) { + return user.full_name; + } else if (user.first_name && user.last_name) { + return user.first_name + ' ' + user.last_name; + } else { + return user.username; + } + }; + }] + ); + +'use strict'; + +angular.module('bulbs.cms.customSearch.config', [ + 'lodash' +]) + .provider('CustomSearchConfig', [ + '_', + function (_) { + + var CustomSearchConfigError = BulbsCmsConfigError.build('CustomSearchConfig'); + var checkOrError = function (value, test, errorMsg) { + if (test(value)) { + return value; + } + throw new CustomSearchConfigError(errorMsg); + }; + + // condition fields to display + var conditionFieldMappings = []; + // condition types to display + var conditionTypes = []; + // time periods to display + var timePeriodMappings = []; + // maximum number of milliseconds to wait for a request to complete + var requestCapMs = 150; + + this.addConditionField = function (name, endpoint, + valueStructureName, valueStructureValue) { + + conditionFieldMappings.push({ + name: checkOrError( + name, _.isString, + 'condition field name must be a string!' + ), + endpoint: checkOrError( + endpoint, _.isString, + 'condition field endpoint must be a string!' + ), + value_structure: { + name: checkOrError( + valueStructureName, _.isString, + 'condition field value structure name must be a string!' + ), + value: checkOrError( + valueStructureValue, _.isString, + 'condition field value structure value must be a string!' + ) + } + }); + return this; + }; + + this.addConditionType = function (name, value) { + conditionTypes.push({ + name: checkOrError( + name, _.isString, + 'condition type name must be a string!' + ), + value: checkOrError( + value, _.isString, + 'condition type value must be a string!' + ) + }); + return this; + }; + + this.addTimePeriod = function (name, value) { + timePeriodMappings.push({ + name: checkOrError( + name, _.isString, + 'time period name must be a string!' + ), + value: checkOrError( + value, _.isString, + 'time period value must be a string!' + ) + }); + return this; + }; + + this.setRequestCapMs = function (value) { + requestCapMs = checkOrError( + value, _.isNumber, + 'request cap milliseconds must be a number!' + ); + return this; + }; + + this.$get = [ + function () { + return { + getConditionFields: _.constant(conditionFieldMappings), + getConditionTypes: _.constant(conditionTypes), + getRequestCapMs: _.constant(requestCapMs), + getTimePeriods: _.constant(timePeriodMappings) + }; + } + ]; + } + ]); + +'use strict'; + +angular.module('customSearch.contentItem.directive', [ + 'bulbs.cms.site.config' +]) + .directive('customSearchContentItem', function (CmsConfig) { + return { + restrict: 'E', + scope: { + model: '=', + controllerService: '=', + onUpdate: '&' + }, + templateUrl: CmsConfig.buildComponentPath('custom-search/custom-search-content-item/custom-search-content-item.html') + }; + }); + +'use strict'; + +angular.module('customSearch.contentItem', [ + 'customSearch.contentItem.directive' +]); + +'use strict'; + +angular.module('customSearch.directive', [ + 'bulbs.cms.contentSearch', + 'bulbs.cms.site.config', + 'customSearch.contentItem', + 'customSearch.group', + 'customSearch.service' +]) + .directive('customSearch', function (CmsConfig) { + return { + controller: function (_, $scope, CustomSearchService) { + + $scope.customSearchService = new CustomSearchService(); + + $scope.resetFilters = function () { + $scope.customSearchService.setPage(1); + $scope.customSearchService.setQuery(''); + $scope.addedFilterOn = false; + $scope.removedFilterOn = false; + }; + + $scope.$conditionalContentRetrieve = function () { + if ($scope.addedFilterOn) { + // included filter is on, use retrieval by included + $scope.customSearchService.$filterContentByIncluded(); + } else if ($scope.removedFilterOn) { + // excluded filter is on, use retrieval by excluded + $scope.customSearchService.$filterContentByExcluded(); + } else { + // no query entered, any request should go to page one + $scope.customSearchService.$retrieveContent(); + } + }; + + $scope.$contentRetrieve = function () { + $scope.customSearchService.$retrieveContent(); + $scope.onUpdate(); + }; + }, + link: function(scope, iElement, iAttrs, ngModelCtrl) { + + ngModelCtrl.$formatters.push(function (modelValue) { + scope.customSearchService.data(modelValue); + scope.customSearchService.$retrieveContent(); + }); + + }, + require: 'ngModel', + restrict: 'E', + scope: { + onUpdate: '&' + }, + templateUrl: CmsConfig.buildComponentPath('custom-search/custom-search.html') + }; + }); + +'use strict'; + +angular.module('customSearch.group.condition.directive', [ + 'bulbs.cms.site.config', + 'BulbsAutocomplete.suggest', + 'BulbsAutocomplete', + 'contentServices.factory' +]) + .directive('customSearchGroupCondition', function (CmsConfig) { + return { + controller: function (_, $q, $scope, BULBS_AUTOCOMPLETE_EVENT_KEYPRESS, + ContentFactory, CustomSearchConfig) { + + $scope.conditionTypes = CustomSearchConfig.getConditionTypes(); + $scope.fieldTypes = CustomSearchConfig.getConditionFields(); + + $scope.writables = { + searchTerm: '' + }; + + $scope.autocompleteItems = []; + + $scope.data = $scope.controllerService.groupsConditionsGet($scope.groupIndex, $scope.conditionIndex); + + var $getItems = function () { + return ContentFactory.all($scope.data.field) + .getList({search: $scope.writables.searchTerm}) + .then(function (items) { + var field = _.find($scope.fieldTypes, function (type) { + return type.endpoint === $scope.data.field; + }); + + return _.map(items, function (item) { + return { + name: item[field.value_structure.name], + value: item[field.value_structure.value] + }; + }); + }); + }; + + $scope.updateAutocomplete = function () { + if ($scope.writables.searchTerm) { + $getItems().then(function (results) { + $scope.autocompleteItems = results; + }); + } + }; + + $scope.delayClearAutocomplete = function () { + _.delay(function () { + $scope.clearAutocomplete(); + $scope.$digest(); + }, 200); + }; + + $scope.clearAutocomplete = function () { + $scope.writables.searchTerm = ''; + $scope.autocompleteItems = []; + }; + + $scope.handleKeypress = function ($event) { + if ($event.keyCode === 27) { + // esc, close dropdown + $scope.clearAutocomplete(); + } else { + $scope.$broadcast(BULBS_AUTOCOMPLETE_EVENT_KEYPRESS, $event); + } + }; + }, + restrict: 'E', + scope: { + controllerService: '=', + groupIndex: '=', + conditionIndex: '=', + onUpdate: '&', + remove: '&' + }, + templateUrl: CmsConfig.buildComponentPath('custom-search/custom-search-group/custom-search-group-condition/custom-search-group-condition.html') + }; + }); + +'use strict'; + +angular.module('customSearch.group.condition', [ + 'customSearch.group.condition.directive' +]); + +'use strict'; + +angular.module('customSearch.group.directive', [ + 'bulbs.cms.site.config', + 'uuid4' +]) + .directive('customSearchGroup', function (CmsConfig) { + return { + controller: function ($scope, CustomSearchConfig, uuid4) { + $scope.data = $scope.controllerService.groupsGet($scope.groupIndex); + $scope.timePeriods = CustomSearchConfig.getTimePeriods(); + $scope.uuid = uuid4.generate(); + + $scope.$update = function () { + $scope.controllerService.$groupsUpdateResultCountFor($scope.groupIndex).then(function () { + $scope.onUpdate(); + }); + }; + + $scope.controllerService.$groupsUpdateResultCountFor($scope.groupIndex); + }, + restrict: 'E', + scope: { + controllerService: '=', + groupIndex: '=', + remove: '&', + onUpdate: '&' + }, + templateUrl: CmsConfig.buildComponentPath('custom-search/custom-search-group/custom-search-group.html') + }; + }); + +'use strict'; + +angular.module('customSearch.group', [ + 'customSearch.group.directive', + 'customSearch.group.condition' +]); + +'use strict'; + +angular.module('customSearch.service', [ + 'bulbs.cms.site.config', + 'apiServices.customSearch.factory' +]) + .factory('CustomSearchService', function (_, CustomSearch, CustomSearchConfig) { + + var defaultData = { + groups: [], + includedIds: [], + excludedIds: [], + pinnedIds: [] + }; + + /** + * Create custom search service. + * + * @returns service wrapper around given endpoint. + */ + var CustomSearchService = function (data) { + + this.data(data); + + this.$page = 1; + this.$query = ''; + + this.content = {}; + }; + + CustomSearchService.prototype.data = function (data) { + + if (_.isUndefined(data)) { + this._data = defaultData; + } else { + this._data = _.defaults(data, defaultData); + } + + return this._data; + }; + + CustomSearchService.prototype._$getContent = _.debounce(function (queryData) { + var self = this; + return CustomSearch.$retrieveContent(queryData) + .then(function (data) { + self.content = data; + }); + }, CustomSearchConfig.getRequestCapMs()); + + CustomSearchService.prototype.$filterContentByIncluded = function () { + var contentQuery = { + includedIds: this._data.includedIds, + page: this.$page, + query: this.$query + }; + return this._$getContent(contentQuery); + }; + + CustomSearchService.prototype.$filterContentByExcluded = function () { + var contentQuery = { + includedIds: this._data.excludedIds, + page: this.$page, + query: this.$query + }; + return this._$getContent(contentQuery); + }; + + CustomSearchService.prototype.$retrieveContent = function () { + var contentQuery = _.assign({ + page: this.$page, + query: this.$query, + preview: true + }, this._data); + return this._$getContent(contentQuery); + }; + + CustomSearchService.prototype.$groupsUpdateResultCountFor = function (index) { + var self = this; + return (function (index) { + return CustomSearch.$retrieveGroupCount(self._data.groups[index]) + .then(function (count) { + self._data.groups[index].$result_count = count; + }); + })(index); + }; + + CustomSearchService.prototype.groupsResultCountGet = function (index) { + return this._data.groups[index].$result_count || 0; + }; + + CustomSearchService.prototype.groupsList = function () { + return this._data.groups; + }; + + CustomSearchService.prototype.groupsAdd = function (data) { + if (_.isUndefined(data)) { + data = {}; + } + + data = _.defaults(data, { + conditions: [], + time: null, + $result_count: 0 + }); + + this._data.groups.push(data); + return data; + }; + + CustomSearchService.prototype.groupsGet = function (index) { + return this._data.groups[index]; + }; + + CustomSearchService.prototype.groupsRemove = function (index) { + return this._data.groups.splice(index, 1).length > 0; + }; + + CustomSearchService.prototype.groupsClear = function () { + this._data.groups = []; + }; + + CustomSearchService.prototype.groupsConditionsAdd = function (groupIndex, data) { + if (_.isUndefined(data)) { + data = {}; + } + + data = _.defaults(data, { + field: CustomSearchConfig.getConditionFields()[0].endpoint, + type: CustomSearchConfig.getConditionTypes()[0].value, + values: [] + }); + + this._data.groups[groupIndex].conditions.push(data); + return data; + }; + + CustomSearchService.prototype.groupsConditionsGet = function (groupIndex, conditionIndex) { + return this._data.groups[groupIndex].conditions[conditionIndex]; + }; + + CustomSearchService.prototype.groupsConditionsList = function (groupIndex) { + return this._data.groups[groupIndex].conditions; + }; + + CustomSearchService.prototype.groupsConditionsRemove = function (groupIndex, conditionIndex) { + return this._data.groups[groupIndex].conditions.splice(conditionIndex, 1).length > 0; + }; + + CustomSearchService.prototype.groupsTimePeriodSet = function (groupIndex) { + var value = CustomSearchConfig.getTimePeriods()[0].value; + this._data.groups[groupIndex].time = value; + return value; + }; + + CustomSearchService.prototype.groupsTimePeriodGet = function (groupIndex) { + return this._data.groups[groupIndex].time || null; + }; + + CustomSearchService.prototype.groupsTimePeriodRemove = function (groupIndex) { + this._data.groups[groupIndex].time = null; + }; + + CustomSearchService.prototype.groupsConditionsValuesAdd = function (groupIndex, conditionIndex, value) { + var values = this._data.groups[groupIndex].conditions[conditionIndex].values; + var matches = _.find(values, function (existingValue) { + return existingValue.name === value.name && existingValue.value === value.value; + }); + + if (!matches) { + values.push(value); + } + }; + + CustomSearchService.prototype.groupsConditionsValuesClear = function (groupIndex, conditionIndex) { + this._data.groups[groupIndex].conditions[conditionIndex].values = []; + }; + + CustomSearchService.prototype.groupsConditionsValuesList = function (groupIndex, conditionIndex) { + return this._data.groups[groupIndex].conditions[conditionIndex].values; + }; + + CustomSearchService.prototype.groupsConditionsValuesRemove = function (groupIndex, conditionIndex, valueIndex) { + return this._data.groups[groupIndex].conditions[conditionIndex].values.splice(valueIndex, 1).length > 0; + }; + + CustomSearchService.prototype.includesList = function () { + return this._data.includedIds; + }; + + CustomSearchService.prototype.includesAdd = function (id) { + // add id, ensure uniqueness + this._data.includedIds.push(id); + this._data.includedIds = _.uniq(this._data.includedIds); + + // remove from exclude list + this.excludesRemove(id); + }; + + CustomSearchService.prototype.includesRemove = function (id) { + this._data.includedIds = _.without(this._data.includedIds, id); + }; + + CustomSearchService.prototype.includesHas = function (id) { + return _.includes(this._data.includedIds, id); + }; + + CustomSearchService.prototype.excludesList = function () { + return this._data.excludedIds; + }; + + CustomSearchService.prototype.excludesAdd = function (id) { + // exclude id, ensure unqiueness + this._data.excludedIds.push(id); + this._data.excludedIds = _.uniq(this._data.excludedIds); + + // remove from include list and pinned list + this.includesRemove(id); + this.pinsRemove(id); + }; + + CustomSearchService.prototype.excludesRemove = function (id) { + this._data.excludedIds = _.without(this._data.excludedIds, id); + }; + + CustomSearchService.prototype.excludesHas = function (id) { + return _.includes(this._data.excludedIds, id); + }; + + CustomSearchService.prototype.pinsList = function () { + return this._data.pinnedIds; + }; + + CustomSearchService.prototype.pinsAdd = function (id) { + // pin id, ensure unqiueness + this._data.pinnedIds.push(id); + this._data.pinnedIds = _.uniq(this._data.pinnedIds); + + // remove from exclude list + this.excludesRemove(id); + }; + + CustomSearchService.prototype.pinsRemove = function (id) { + this._data.pinnedIds = _.without(this._data.pinnedIds, id); + }; + + CustomSearchService.prototype.pinsHas = function (id) { + return _.includes(this._data.pinnedIds, id); + }; + + CustomSearchService.prototype.getPage = function () { + return this.$page; + }; + + CustomSearchService.prototype.setPage = function (page) { + this.$page = page; + }; + + CustomSearchService.prototype.getQuery = function () { + return this.$query; + }; + + CustomSearchService.prototype.setQuery = function (query) { + this.$query = query; + }; + + return CustomSearchService; + }); + +'use strict'; + +angular.module('customSearch', [ + 'customSearch.directive' +]); + +'use strict'; + +angular.module('bulbs.cms.dateTimeFilter', [ + 'bulbs.cms.site.config', + 'lodash', + 'moment', +]) + .filter('dateTimeFormat', [ + '_', 'moment', 'CmsConfig', + function (_, moment, CmsConfig) { + function isInvalidDateValue (dateValue) { + return ( + !_.isString(dateValue) && + !_.isDate(dateValue) && + !moment.isMoment(dateValue) + ); + } + + return function (date, format) { + if (isInvalidDateValue(date)) { + return ''; + } + + if (!_.isString(format)) { + date = moment(date); + format = CmsConfig.getDateTimeFormatHumanReadable(); + } + + return moment.tz(date, CmsConfig.getTimezoneName()).format(format); + }; + }, + ]); + +'use strict'; + +angular.module('bulbs.cms.dateTimeModal.controller', [ + 'bulbs.cms.site.config', + 'moment', + 'ui.bootstrap.modal', +]) + .controller('DatetimeSelectionModalCtrl', [ + '$scope', '$modalInstance', 'CmsConfig', 'moment', + function ($scope, $modalInstance, CmsConfig, moment) { + $scope.nowInTimezone = function () { + return $scope.dateInTimezone(moment()); + }; + + $scope.dateInTimezone = function (date) { + return date.clone().tz(CmsConfig.getTimezoneName()); + }; + + $scope.TIMEZONE_LABEL = moment.tz(CmsConfig.getTimezoneName()).format('z'); + $scope.dateTime = $scope.modDatetime ? $scope.dateInTimezone($scope.modDatetime) : $scope.nowInTimezone(); + $scope.time = $scope.dateTime.toDate(); + $scope.date = $scope.dateTime.clone(); + + $scope.$watch('time', function () { + var time = moment($scope.time); + var newTime = moment() + .year($scope.date.year()) + .dayOfYear($scope.date.dayOfYear()) + .hours(time.hours()) + .minutes(time.minutes()); + $scope.dateTime = $scope.dateInTimezone(newTime); + }); + + $scope.$watch('date', function () { + var date = $scope.date.clone(); + var time = moment($scope.time); + var newDate = moment() + .year($scope.date.year()) + .dayOfYear(date.dayOfYear()) + .hours(time.hours()) + .minutes(time.minutes()); + $scope.dateTime = $scope.dateInTimezone(newDate); + }); + + $scope.setDate = function (selectedDate) { + $scope.date = $scope.dateInTimezone(moment(selectedDate)); + }; + + $scope.isDateTimeInvalid = function () { + return !$scope.dateTime.isValid(); + }; + + $scope.setDateToday = function () { + var today = $scope.nowInTimezone(); + $scope.date = $scope.dateTime.clone().dayOfYear(today.dayOfYear()); + }; + + $scope.setDateTomorrow = function () { + var tomorrow = moment().add(1, 'day'); + $scope.date = $scope.dateInTimezone(tomorrow.clone()); + }; + + $scope.setTimeNow = function () { + var now = $scope.nowInTimezone(); + $scope.date = now.clone(); + $scope.time = now.clone().toDate(); + }; + + $scope.setTimeMidnight = function () { + var midnightTonight = $scope.nowInTimezone().startOf('day'); + $scope.date = midnightTonight.clone(); + $scope.time = midnightTonight.clone().toDate(); + }; + + $scope.clearDatetime = function () { + $modalInstance.close(null); + }; + + $scope.chooseDatetime = function () { + if ($scope.dateTime.isValid()) { + $modalInstance.close($scope.dateTime); + } + }; + }, + ]); + +'use strict'; + +/** + * Directive to apply as an attribute to an element, that when clicked, will open a datetime selection modal. Modal + * functionality is dependent on all dates being moment objects. + */ +angular.module('bulbs.cms.dateTimeModal.opener', [ + 'bulbs.cms.dateTimeModal.controller', + 'bulbs.cms.site.config', + 'ui.bootstrap.modal' +]) + .directive('datetimeSelectionModalOpener', [ + '$modal', '$q', 'CmsConfig', + function ($modal, $q, CmsConfig) { + return { + restrict: 'A', + scope: { + modalClearText: '@', + modalOkText: '@', + modDatetime: '=?ngModel', + modalTitle: '@', + modalOnBeforeClose: '&', + modalOnClear: '&', + modalOnClose: '&' + }, + link: function (scope, element) { + var modalInstance = null; + element.addClass('datetime-selection-modal-opener'); + element.on('click', function () { + + modalInstance = $modal + .open({ + templateUrl: CmsConfig.buildComponentPath( + 'date-time-modal', + 'date-time-modal.html' + ), + controller: 'DatetimeSelectionModalCtrl', + scope: scope + }); + + modalInstance.result + .then(function (newDate) { + return $q.when(scope.modalOnBeforeClose({ newDate: newDate })) + .then(function (result) { + + if (result !== false) { + scope.modDatetime = newDate; + if (newDate) { + scope.modalOnClose({ newDate: newDate }); + } else { + scope.modalOnClear(); + } + } + }); + }); + }); + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dateTimeModal', [ + 'bulbs.cms.dateTimeModal.opener' +]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.api', [ + 'bulbs.cms.site.config' +]) + .service('DynamicContentApi', [ + '$http', 'CmsConfig', + function ($http, CmsConfig) { + return { + retrieveSchema: function (url) { + return $http({ + method: 'OPTIONS', + url: url + }); + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.boolean', [ + 'bulbs.cms.site.config', + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.dynamicContent.form.input.errors' +]) + .directive('dynamicContentFormFieldBoolean', [ + 'CmsConfig', + function (CmsConfig) { + return { + require: ['ngModel', '^^form'], + restrict: 'E', + scope: { + uuid: '@', + name: '@', + ngModel: '=', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-boolean', + 'dynamic-content-form-field-boolean.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.color', [ + 'bulbs.cms.site.config', + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.dynamicContent.form.input.errors' +]) + .directive('dynamicContentFormFieldColor', [ + 'CmsConfig', + function (CmsConfig) { + return { + require: ['ngModel', '^^form'], + restrict: 'E', + scope: { + uuid: '@', + name: '@', + ngModel: '=', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-color', + 'dynamic-content-form-field-color.html' + ), + link: function (scope, element, attr, ctrls) { + var formField = ctrls[1][scope.name]; + formField.$validators.rgbhex = function (modelValue) { + if (!modelValue) { + return true; + } + else { + return /^#[0-9a-fA-F]{6}$/.test(modelValue); + } + }; + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.dateTime', [ + 'bulbs.cms.dateTimeFilter', + 'bulbs.cms.dateTimeModal', + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.dynamicContent.form.input.errors', + 'bulbs.cms.site.config' +]) + .directive('dynamicContentFormFieldDateTime', [ + 'CmsConfig', + function (CmsConfig) { + return { + controller: [ + '$scope', + function ($scope) { + $scope.setDate = function (newDate) { + $scope.ngModel[$scope.name] = newDate.format(); + }; + + $scope.clearDate = function () { + $scope.ngModel[$scope.name] = null; + }; + } + ], + require: ['ngModel', '^^form'], + restrict: 'E', + scope: { + uuid: '@', + name: '@', + ngModel: '=', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-date-time', + 'dynamic-content-form-field-date-time.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.image', [ + 'bettyEditable', + 'bulbs.cms.site.config', + 'bulbs.cms.staticImage', + 'bulbs.cms.dynamicContent.form.input.errors', + 'bulbs.cms.dynamicContent.form.input.label' +]) + .directive('dynamicContentFormFieldImage', [ + 'CmsConfig', + function (CmsConfig) { + + return { + require: ['ngModel', '^^form'], + restrict: 'E', + scope: { + name: '@', + ngModel: '=', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-image', + 'dynamic-content-form-field-image.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.input.errors', [ + 'bulbs.cms.site.config' +]) + .directive('dynamicContentFormFieldInputErrors', [ + 'CmsConfig', + function (CmsConfig) { + return { + link: function (scope, element, attrs, formCtrl) { + scope.form = formCtrl; + }, + require: '^^form', + restrict: 'E', + scope: { + name: '@', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-input-errors', + 'dynamic-content-form-field-input-errors.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.input.label', [ + 'bulbs.cms.site.config', + 'lodash' +]) + .directive('dynamicContentFormFieldInputLabel', [ + '_', 'CmsConfig', + function (_, CmsConfig) { + return { + link: function (scope, element, attrs, formCtrl) { + scope.form = formCtrl; + + scope.isEmpty = _.isEmpty; + }, + require: '^^form', + restrict: 'E', + scope: { + inputId: '@', + name: '@', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-input-label', + 'dynamic-content-form-field-input-label.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.integer', [ + 'bulbs.cms.site.config', + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.dynamicContent.form.input.errors' +]) + .directive('dynamicContentFormFieldInteger', [ + 'CmsConfig', + function (CmsConfig) { + return { + require: ['ngModel', '^^form'], + restrict: 'E', + scope: { + uuid: '@', + name: '@', + ngModel: '=', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-integer', + 'dynamic-content-form-field-integer.html' + ), + link: function (scope, element, attr, ctrls) { + var formField = ctrls[1][scope.name]; + formField.$validators.integer = function (modelValue) { + if (!modelValue) { + return true; + } else { + return parseInt(modelValue, 10) === modelValue; + } + }; + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.invalid', [ + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.site.config' +]) + .directive('dynamicContentFormFieldInvalid', [ + 'CmsConfig', + function (CmsConfig) { + + return { + require: '^^form', + restrict: 'E', + scope: { + name: '@', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-invalid', + 'dynamic-content-form-field-invalid.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.list', [ + 'bulbs.cms.dynamicContent.form.field.object', + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.dynamicContent.form.types', + 'bulbs.cms.site.config', + 'bulbs.cms.utils', + 'lodash' +]) + .directive('dynamicContentFormFieldList', [ + 'CmsConfig', 'FIELD_TYPES_META', 'Utils', + function (CmsConfig, FIELD_TYPES_META, Utils) { + return { + controller: [ + '_', '$scope', + function (_, $scope) { + if (_.isUndefined($scope.ngModel[$scope.name])) { + $scope.ngModel[$scope.name] = []; + } + + $scope.itemOrderingMemory = []; + $scope.redoOrdering = function () { + $scope.itemOrderingMemory = $scope.ngModel[$scope.name].map(function (v, i) { + return i + 1; + }); + }; + $scope.redoOrdering(); + + $scope.newItem = function () { + if ($scope.readOnly) { + return; + } + $scope.ngModel[$scope.name].push({}); + $scope.redoOrdering(); + }; + + $scope.moveItem = function (fromIndex, toIndex) { + Utils.moveTo($scope.ngModel[$scope.name], fromIndex, toIndex, true); + + $scope.redoOrdering(); + }; + + $scope.removeItem = function (index) { + Utils.removeFrom($scope.ngModel[$scope.name], index); + + $scope.redoOrdering(); + }; + } + ], + link: function (scope, elements, attrs, ctrls) { + scope.form = ctrls[1]; + + if (scope.ngModel[scope.name].length === 0) { + scope.newItem(); + } + }, + require: ['ngModel', '^^form'], + restrict: 'E', + scope: { + name: '@', + schema: '=', + ngModel: '=', + readOnly: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-list', + 'dynamic-content-form-field-list.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.object', [ + 'bulbs.cms.dynamicContent.form.field.boolean', + 'bulbs.cms.dynamicContent.form.field.color', + 'bulbs.cms.dynamicContent.form.field.dateTime', + 'bulbs.cms.dynamicContent.form.field.image', + 'bulbs.cms.dynamicContent.form.field.list', + 'bulbs.cms.dynamicContent.form.field.integer', + 'bulbs.cms.dynamicContent.form.field.invalid', + 'bulbs.cms.dynamicContent.form.field.richtext', + 'bulbs.cms.dynamicContent.form.field.text', + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.dynamicContent.form.types', + 'bulbs.cms.site.config', + 'lodash', + 'uuid4' +]) + .directive('dynamicContentFormFieldObject', [ + '_', '$compile', 'CmsConfig', 'FIELD_TYPES_META', 'uuid4', + function (_, $compile, CmsConfig, FIELD_TYPES_META, uuid4) { + + return { + link: function (scope, element, attrs) { + var $form = element.find('ng-form'); + + scope.hideLabel = 'hideLabel' in attrs; + + scope.$watch('form.$valid', function (isValid) { + scope.onValidityChange({ + isValid: isValid, + internalForm: scope.form + }); + }); + + scope.$watch('schema', function () { + if (_.has(scope.schema, 'fields')) { + var fieldKeys = Object.keys(scope.schema.fields); + var includeOnlyProvided = _.isArray(scope.includeOnly); + var hideOnlyChildLabel = false; + + if (includeOnlyProvided) { + fieldKeys = _.intersection(fieldKeys, scope.includeOnly); + + if (scope.includeOnly.length === 1) { + hideOnlyChildLabel = true; + } + } + + fieldKeys.forEach(function (id) { + var fieldSchema = scope.schema.fields[id]; + var fieldMeta = FIELD_TYPES_META[fieldSchema.type]; + + if (_.isUndefined(fieldMeta)) { + if (_.has(fieldSchema, 'fields')) { + fieldMeta = FIELD_TYPES_META.object; + } else { + fieldMeta = FIELD_TYPES_META.invalid; + } + } + + var tagName = fieldMeta.tagName; + var html = angular.element('<' + tagName + '>'); + + html.attr('uuid', uuid4.generate()); + html.attr('name', id); + html.attr('schema', 'schema.fields.' + id); + html.attr('class', 'dynamic-content-form-field'); + + if (hideOnlyChildLabel) { + html.attr('hide-label', 'true'); + } + + // NOTE : Angular is not able to bind primitives properly when + // passed into isolate scopes. See + // https://github.com/angular/angular.js/issues/1924. The + // parent object is passed in in its entirety and the child + // directive is responsible for accessing the key it needs to + // be able to modify. + if (_.isString(scope.name)) { + // when we're at a nested object, scope.name will be defined + html.attr('ng-model', 'ngModel[name]'); + } else { + html.attr('ng-model', 'ngModel'); + } + + $form.append(html); + $compile(html)(scope); + }); + } + }, true); + }, + require: 'ngModel', + restrict: 'E', + scope: { + name: '@', + schema: '=', + ngModel: '=', + onValidityChange: '&', + includeOnly: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-object', + 'dynamic-content-form-field-object.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.richtext', [ + 'bulbs.cms.site.config', + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.dynamicContent.form.input.errors', + 'OnionEditor' +]) + .directive('dynamicContentFormFieldRichtext', [ + 'CmsConfig', + function (CmsConfig) { + return { + link: function (scope, element, attr, ctrls) { + scope.formField = ctrls[1][scope.name]; + }, + require: ['ngModel', '^^form'], + restrict: 'E', + scope: { + name: '@', + ngModel: '=', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-richtext', + 'dynamic-content-form-field-richtext.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.field.text', [ + 'bulbs.cms.site.config', + 'bulbs.cms.dynamicContent.form.input.label', + 'bulbs.cms.dynamicContent.form.input.errors', + 'jquery' +]) + .directive('dynamicContentFormFieldText', [ + 'CmsConfig', + function (CmsConfig) { + return { + link: function (scope, element, attr, ctrls) { + scope.formField = ctrls[1][scope.name]; + + if (scope.schema.max_length > 0) { + var input = element.find('input'); + input.css('width', scope.schema.max_length + 'em'); + + if (element.width() <= input.width()) { + input.css('width', ''); + } + } + }, + require: ['ngModel', '^^form'], + restrict: 'E', + scope: { + uuid: '@', + name: '@', + ngModel: '=', + schema: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + 'dynamic-content-form-field', + 'dynamic-content-form-field-text', + 'dynamic-content-form-field-text.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form.types', []) + .constant('FIELD_TYPES_META', { + array: { + tagName: 'dynamic-content-form-field-list' + }, + boolean: { + tagName: 'dynamic-content-form-field-boolean' + }, + color: { + tagName: 'dynamic-content-form-field-color' + }, + datetime: { + tagName: 'dynamic-content-form-field-date-time' + }, + image: { + tagName: 'dynamic-content-form-field-image' + }, + invalid: { + tagName: 'dynamic-content-form-field-invalid' + }, + object: { + tagName: 'dynamic-content-form-field-object' + }, + richtext: { + tagName: 'dynamic-content-form-field-richtext' + }, + string: { + tagName: 'dynamic-content-form-field-text' + }, + integer: { + tagName: 'dynamic-content-form-field-integer' + } + }); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent.form', [ + 'bulbs.cms.dynamicContent.api', + 'bulbs.cms.dynamicContent.form.field.object', + 'bulbs.cms.site.config', + 'lodash' +]) + .directive('dynamicContentForm', [ + '_', 'CmsConfig', + function (_, CmsConfig) { + + var DynamicContentFormError = BulbsCmsError.build(''); + var template = function (name) { + return CmsConfig.buildComponentPath( + 'dynamic-content', + 'dynamic-content-form', + name + ); + }; + + return { + controller: [ + '$scope', 'DynamicContentApi', + function ($scope, DynamicContentApi) { + if (!_.isString($scope.schemaSrc)) { + throw new DynamicContentFormError('must be provided a schema url!'); + } + + if (!_.isObject($scope.ngModel)) { + throw new DynamicContentFormError('must be provided an object for ng-model!'); + } + + $scope.template = template('dynamic-content-form-loading.html'); + $scope.schema = {}; + $scope.validityCallback = function (isValid) { + $scope.onValidityChange({ isValid: isValid }); + }; + + DynamicContentApi.retrieveSchema($scope.schemaSrc) + .then(function (response) { + $scope.template = template('dynamic-content-form-loaded.html'); + $scope.schema = response.data; + }) + .catch(function () { + $scope.template = template('dynamic-content-form-error.html'); + $scope.errorMessage = 'Unable to retrieve schema'; + }); + } + ], + require: 'ngModel', + restrict: 'E', + scope: { + schemaSrc: '@', + ngModel: '=', + onValidityChange: '&', + includeOnly: '=' + }, + template: '' + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.dynamicContent', [ + 'bulbs.cms.dynamicContent.form' +]); + +'use strict'; + +angular.module('bulbs.cms.editor.wrapper', [ + 'BettyCropper', + 'bulbs.cms.imageCropModal', + 'bulbs.cms.site.config', + 'jquery', + 'OnionEditor', +]) + .run([ + '$', + function ($) { + // NOTE : this is the old editor-cms-bridge.js + /* + Image + This bridges the embed module that the editor exposes & our custom image implementation. + */ + /* prevents backspace from accidentally triggering a back event */ + $(document).unbind('keydown').bind('keydown', function (event) { + var doPrevent = false; + if (event.keyCode === 8) { + var d = event.srcElement || event.target; + if (['TEXTAREA', 'INPUT'].indexOf(d.tagName.toUpperCase() !== -1)) { + doPrevent = d.readOnly || d.disabled; + } else if (d.isContentEditable) { + //we're in a content editable field + doPrevent = false; + } else { + doPrevent = true; + } + } + if (doPrevent) { + event.preventDefault(); + } + }); + } + ]) + .directive('onionEditor', [ + '$', 'BettyCropper', 'CmsConfig', 'OnionEditor', 'openImageCropModal', + 'Zencoder', + function ($, BettyCropper, CmsConfig, OnionEditor, openImageCropModal, + Zencoder) { + + var safeApply = function (scope, fn) { + if (scope.$$phase || scope.$root.$$phase) { + fn(); + } else { + scope.$apply(function () { + fn(); + }); + } + }; + + return { + controller: [ + '$scope', + function ($scope) { + $scope.editor = null; + + this.getEditor = function () { + return $scope.editor; + }; + } + ], + require: 'ngModel', + replace: true, + restrict: 'E', + templateUrl: CmsConfig.buildComponentPath( + 'editor', + 'editor-wrapper', + 'editor-wrapper.html' + ), + scope: { + ngModel: '=' + }, + link: function (scope, element, attrs, ngModel) { + + if (!ngModel) { + return; + } + + var formatting; + if (attrs.formatting) { + formatting = attrs.formatting.split(','); + } + + var options = {}; + var defaultValue = ''; + + if (attrs.role === 'multiline') { + defaultValue = '


'; + options = { + // global options + multiline: true, + formatting: formatting || ['link', 'bold', 'italic', 'blockquote', 'heading', 'list', 'strike', 'underline'], + placeholder: { + text: attrs.placeholder || '

Write here

', + container: $('.editorPlaceholder', element[0])[0], + }, + link: { + domain: attrs.linkDomain || false, + // Sean, you can figure out a nicer way to handle the search handler. + searchHandler: window[attrs.linkSearchHandler] || false + }, + statsContainer: '.wordcount', + inlineObjects: attrs.inlineObjects || CmsConfig.getInlineObjecsPath(), + image: { + insertDialog: BettyCropper.upload, + editDialog: openImageCropModal, + }, + video: { + insertDialog: Zencoder.onVideoFileUpload, + editDialog: Zencoder.openVideoThumbnailModal, + videoEmbedUrl: CmsConfig.buildVideoUrl() + } + }; + } else { + $('.document-tools, .embed-tools', element).hide(); + defaultValue = ''; + options = { + // global options + multiline: false, + placeholder: { + text: attrs.placeholder || 'Write here', + container: $('.editorPlaceholder', element[0])[0], + }, + formatting: formatting || [] + }; + } + + scope.editor = new OnionEditor($('.editor', element[0])[0], options); + + ngModel.$render = function () { + scope.editor.setContent(ngModel.$viewValue || defaultValue); + // register on change here, after the initial load so angular doesn't get mad... + setTimeout(function () { + scope.editor.setChangeHandler(read); + }); + }; + + // Redefine what empty looks like + ngModel.$isEmpty = function (value) { + return ! value || scope.editor.scribe.allowsBlockElements() && value === defaultValue; + }; + + // Write data to the model + function read() { + safeApply(scope, function () { + var html = scope.editor.getContent(); + if (html === defaultValue) { + html = ''; + } + ngModel.$setViewValue(html); + }); + } + + scope.$watch(ngModel, function () { + scope.editor.setContent(ngModel.$viewValue || defaultValue); + if (window.picturefill) { + window.picturefill(element[0]); + } + }); + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.editor', [ + 'bulbs.cms.editor.wrapper' +]); + +'use strict'; + +angular.module('EditorsPick', [ + 'bulbs.cms.site.config', + 'customSearch' +]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/sod/', { + controller: function ($scope, $window) { + + $window.document.title = CmsConfig.getCmsName() + ' | SoD'; + + $scope.$watch('queryData', function () { console.log(arguments); }); + + $scope.queryData = {}; + $scope.updateQueryData = function () { + $scope.queryData = { + groups: [{ + conditions: [{ + field: 'content-type', + type: 'all', + values: [{ + name: 'for display', + value: 'actually-use-this-value-123' + }] + }], + time: '1 day' + }], + included_ids: [1], + excluded_ids: [2], + pinned_ids: [3], + page: 1, + query: 'query balh blah blahb' + }; + }; + + $scope.updateConditionData = function () { + $scope.queryData.groups[0].conditions = [{ + field: 'content-type', + type: 'all', + values: [{ + name: 'ANOTHER THIGN', + value: 'actually-use-this-value-123' + }] + }]; + }; + + }, + templateUrl: CmsConfig.buildComponentPath('editors-pick/editors-pick.html'), + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('evergreenField.directive', [ + 'lodash', + 'saveButton.directive' +]) + .directive('evergreenField', [ + 'CmsConfig', + function (CmsConfig) { + return { + restrict: 'E', + scope: { + article: '=' + }, + templateUrl: CmsConfig.buildComponentPath('evergreen-field/evergreen-field.html') + }; + } + ]); + +'use strict'; + +angular.module('evergreenField', [ + 'evergreenField.directive' +]); + +'use strict'; + +angular.module('filterListWidget.directive', [ + 'bulbs.cms.site.config' +]) + .directive('filterListWidget', function (_, $http, $location, $timeout, $, CmsConfig) { + return { + restrict: 'E', + scope: { + filters: '=' + }, + templateUrl: CmsConfig.buildComponentPath('filter-list-widget/filter-list-widget.html'), + link: function (scope, element, attrs) { + var $element = $(element); + var $input = $element.find('input'); + + scope.autocompleteArray = []; + + var filterInputCounter = 0, filterInputTimeout; + + $input.on('input', function (e) { + var search = $input.val(); + scope.searchTerm = search; + + $timeout.cancel(filterInputTimeout); + filterInputTimeout = $timeout(function () { getAutocompletes(search); }, 200); + + if (filterInputCounter > 2) { + getAutocompletes(search); + } + }); + function getAutocompletes(search) { + $timeout.cancel(filterInputTimeout); + filterInputCounter = 0; + if (search.length < 1) { + scope.autocompleteArray = []; + scope.$apply(); + return; + } + + $http({ + url: '/cms/api/v1/things/?type=tag&type=feature_type&type=author', + method: 'GET', + params: {'q': search} + }).success(function (data) { + scope.autocompleteArray = data; + }); + } + + $input.on('keyup', function (e) { + if (e.keyCode === 38) { arrowSelect('up'); }//up + if (e.keyCode === 40) { arrowSelect('down'); } //down + if (e.keyCode === 13) { //enter + if ($element.find('.selected').length > 0) { + // To trigger the click we need to first break out of the + // current $apply() cycle. Hence the $timeout() + $timeout(function () { + angular.element('.selected > a').triggerHandler('click'); + }, 0); + } else { + scope.addFilter('search', $input.val()); + } + } + }); + + scope.search = function () { + scope.addFilter('search', scope.filterInputValue); + }; + + scope.clearSearch = function () { + scope.filterInputValue = ''; + }; + + scope.clearFilters = function () { + scope.filters = {}; + scope.filterInputValue = ''; + return applyFilterChange({}); + }; + + $element.on('mouseover', '.entry', function () { + scope.selectEntry(this); + }); + + function arrowSelect(direction) { + var $entries = $element.find('.entry'); + var $selected = $element.find('.entry.selected'); + var $toSelect; + if ($selected.length > 0) { + if (direction === 'up') { $toSelect = $selected.first().prev(); } + if (direction === 'down') { $toSelect = $selected.first().next(); } + } else { + if (direction === 'up') { $toSelect = $entries.last(); } + if (direction === 'down') { $toSelect = $entries.first(); } + } + scope.selectEntry($toSelect); + } + scope.selectEntry = function (entry) { + $element.find('.selected').removeClass('selected'); + $(entry).addClass('selected'); + }; + + $input.on('blur', function () { + $element.find('.dropdown-menu').fadeOut(200); + }); + $input.on('focus', function () { + $element.find('.dropdown-menu').fadeIn(200); + }); + + scope.addFilter = function (type, newFilterValue) { + var filterObject = $location.search(); + if (type === 'search') { + filterObject.search = newFilterValue; + } else { + if (!filterObject[type]) { + filterObject[type] = []; + } + if (typeof(filterObject[type]) === 'string') { + filterObject[type] = [filterObject[type]]; + } + if (!_.contains(filterObject[type], newFilterValue)) { + // this value is not already in + filterObject[type].push(newFilterValue); + } + } + return applyFilterChange(filterObject); + }; + + scope.deleteFilter = function (key) { + var filterObject = $location.search(); + var toDelete = scope.filters[key]; + if (typeof(filterObject[toDelete.type]) === 'string') { + filterObject[toDelete.type] = [filterObject[toDelete.type]]; + } + var toSplice; + for (var i in filterObject[toDelete.type]) { + if (filterObject[toDelete.type][i] === toDelete.query) { + toSplice = i; + break; + } + } + filterObject[toDelete.type].splice(i, 1); + filterObject.search = $input.val(); + delete scope.filters[key]; + return applyFilterChange(filterObject); + }; + + function applyFilterChange(filterObject) { + filterObject.page = 1; + $location.search(filterObject); + scope.autocompleteArray = []; + $input.trigger('blur'); + } + + function getFilterObjects() { + var search = $location.search(); + scope.filters = {}; + if (typeof(search) === 'undefined') { console.log('undefined'); return; } + //TODO: this sucks + var filterParamsToTypes = {'authors': 'author', 'tags': 'tag', 'feature_types': 'feature_type'}; + for (var filterParam in filterParamsToTypes) { + var filterType = filterParamsToTypes[filterParam]; + if (typeof(search[filterParam]) === 'string') { search[filterParam] = [search[filterParam]]; } + for (var i in search[filterParam]) { + var value = search[filterParam][i]; + scope.filters[filterType + value] = {'query': value, 'type': filterParam}; + getQueryToLabelMappings(filterType, value); + } + } + if (search.search) { + scope.filterInputValue = search.search; + } + } + + scope.$on('$routeUpdate', function () { + getFilterObjects(); + }); + + getFilterObjects(); + + function getQueryToLabelMappings(type, query) { + //this is pretty stupid + //TODO: Maybe do this with some localStorage caching? + //TODO: Maybe just dont do this at all? I dont know if thats possible + // because there is no guarantee of any state (like if a user comes + // directly to a filtered search page via URL) + scope.queryToLabelMappings = scope.queryToLabelMappings || {}; + + if (query in scope.queryToLabelMappings) { return; } + + $http({ + url: '/cms/api/v1/things/?type=' + type, + method: 'GET', + params: {'q': query} + }).success(function (data) { + for (var i in data) { + scope.queryToLabelMappings[data[i].value] = data[i].name; + } + }); + + } + + } + + }; + }); + +'use strict'; + +angular.module('filterListWidget', [ + 'filterListWidget.directive' +]); +'use strict'; + +angular.module('filterWidget.directive', [ + 'contentServices.listService' +]) + .directive('filterWidget', function (_, $http, $location, $timeout, $, + ContentListService, CmsConfig) { + return { + restrict: 'E', + scope: {}, + templateUrl: CmsConfig.buildComponentPath('filter-widget/filter-widget.html'), + link: function (scope, element, attrs) { + var $element = $(element); + var $input = $element.find('input'); + + scope.autocompleteArray = []; + + var filterInputCounter = 0, filterInputTimeout; + + $input.on('input', function (e) { + var search = $input.val(); + scope.searchTerm = search; + + $timeout.cancel(filterInputTimeout); + filterInputTimeout = $timeout(function () { getAutocompletes(search); }, 200); + + if (filterInputCounter > 2) { + getAutocompletes(search); + } + }); + function getAutocompletes(search) { + $timeout.cancel(filterInputTimeout); + filterInputCounter = 0; + if (search.length < 1) { + scope.autocompleteArray = []; + scope.$apply(); + return; + } + + $http({ + url: '/cms/api/v1/things/?type=tag&type=feature_type&type=author', + method: 'GET', + params: {'q': search} + }).success(function (data) { + scope.autocompleteArray = data; + }); + } + + $input.on('keyup', function (e) { + if (e.keyCode === 38) { arrowSelect('up'); }//up + if (e.keyCode === 40) { arrowSelect('down'); } //down + if (e.keyCode === 13) { //enter + if ($element.find('.selected').length > 0) { + // To trigger the click we need to first break out of the + // current $apply() cycle. Hence the $timeout() + $timeout(function () { + angular.element('.selected > a').triggerHandler('click'); + }, 0); + } else { + scope.addFilter('search', $input.val()); + } + } + }); + + scope.search = function () { + scope.addFilter('search', scope.filterInputValue); + }; + + scope.clearSearch = function () { + scope.filterInputValue = ''; + }; + + scope.clearFilters = function () { + scope.filterObjects = {}; + scope.filterInputValue = ''; + return applyFilterChange({}); + }; + + $element.on('mouseover', '.entry', function () { + scope.selectEntry(this); + }); + + function arrowSelect(direction) { + var $entries = $element.find('.entry'); + var $selected = $element.find('.entry.selected'); + var $toSelect; + if ($selected.length > 0) { + if (direction === 'up') { $toSelect = $selected.first().prev(); } + if (direction === 'down') { $toSelect = $selected.first().next(); } + } else { + if (direction === 'up') { $toSelect = $entries.last(); } + if (direction === 'down') { $toSelect = $entries.first(); } + } + scope.selectEntry($toSelect); + } + scope.selectEntry = function (entry) { + $element.find('.selected').removeClass('selected'); + $(entry).addClass('selected'); + }; + + $input.on('blur', function () { + $element.find('.dropdown-menu').fadeOut(200); + }); + $input.on('focus', function () { + $element.find('.dropdown-menu').fadeIn(200); + }); + + scope.addFilter = function (type, newFilterValue) { + var filterObject = $location.search(); + if (type === 'search') { + filterObject.search = newFilterValue; + } else { + if (!filterObject[type]) { + filterObject[type] = []; + } + if (typeof(filterObject[type]) === 'string') { + filterObject[type] = [filterObject[type]]; + } + if (!_.contains(filterObject[type], newFilterValue)) { + // this value is not already in + filterObject[type].push(newFilterValue); + } + } + return applyFilterChange(filterObject); + }; + + scope.deleteFilter = function (key) { + var filterObject = $location.search(); + var toDelete = scope.filterObjects[key]; + if (typeof(filterObject[toDelete.type]) === 'string') { + filterObject[toDelete.type] = [filterObject[toDelete.type]]; + } + var toSplice; + for (var i in filterObject[toDelete.type]) { + if (filterObject[toDelete.type][i] === toDelete.query) { + toSplice = i; + break; + } + } + filterObject[toDelete.type].splice(i, 1); + filterObject.search = $input.val(); + delete scope.filterObjects[key]; + return applyFilterChange(filterObject); + }; + + function applyFilterChange(filterObject) { + filterObject.page = 1; + $location.search(filterObject); + scope.autocompleteArray = []; + $input.trigger('blur'); + + return ContentListService.$updateContent(filterObject); + } + + function getFilterObjects() { + var search = $location.search(); + scope.filterObjects = {}; + if (typeof(search) === 'undefined') { console.log('undefined'); return; } + //TODO: this sucks + var filterParamsToTypes = {'authors': 'author', 'tags': 'tag', 'feature_types': 'feature_type'}; + for (var filterParam in filterParamsToTypes) { + var filterType = filterParamsToTypes[filterParam]; + if (typeof(search[filterParam]) === 'string') { search[filterParam] = [search[filterParam]]; } + for (var i in search[filterParam]) { + var value = search[filterParam][i]; + scope.filterObjects[filterType + value] = {'query': value, 'type': filterParam}; + getQueryToLabelMappings(filterType, value); + } + } + if (search.search) { + scope.filterInputValue = search.search; + } + } + + scope.$on('$routeUpdate', function () { + getFilterObjects(); + }); + + getFilterObjects(); + + function getQueryToLabelMappings(type, query) { + //this is pretty stupid + //TODO: Maybe do this with some localStorage caching? + //TODO: Maybe just dont do this at all? I dont know if thats possible + // because there is no guarantee of any state (like if a user comes + // directly to a filtered search page via URL) + scope.queryToLabelMappings = scope.queryToLabelMappings || {}; + + if (query in scope.queryToLabelMappings) { return; } + + $http({ + url: '/cms/api/v1/things/?type=' + type, + method: 'GET', + params: {'q': query} + }).success(function (data) { + for (var i in data) { + scope.queryToLabelMappings[data[i].value] = data[i].name; + } + }); + + } + + } + + }; + }); + +'use strict'; + +angular.module('filterWidget', [ + 'filterWidget.directive' +]); + +'use strict'; + +angular.module('genericAjaxButton.controller', []) + .controller('GenericAjaxButtonController', function ($scope) { + $scope.STATES = { + DONE: 'done', + PROGRESS: 'in-progress', + ERROR: 'error' + }; + $scope.doClick = function () { + $scope.state = $scope.STATES.PROGRESS; + $scope.clickFunction() + .then(function () { + $scope.state = $scope.STATES.DONE; + }) + .catch(function () { + $scope.state = $scope.STATES.ERROR; + }); + }; + }); + +'use strict'; + +/** + * Highly customizable four state ajax button. Useful for buttons which require + * different displays for disabled/action/progress/complete states. + */ +angular.module('genericAjaxButton.directive', [ + 'bulbs.cms.site.config', + 'genericAjaxButton.controller' +]) + .directive('genericAjaxButton', function (CmsConfig) { + return { + controller: 'GenericAjaxButtonController', + restrict: 'E', + scope: { + disableWhen: '&', + clickFunction: '=', + cssBtnClassComplete: '@', + cssBtnClassError: '@', + cssBtnClassProgress: '@', + cssIconComplete: '@', + textError: '@', + textProgress: '@', + textComplete: '@' + }, + templateUrl: CmsConfig.buildComponentPath('generic-ajax-button/generic-ajax-button.html') + }; + }); + +'use strict'; + +angular.module('genericAjaxButton', [ + 'genericAjaxButton.directive' +]); + +'use strict'; + +angular.module('saveButton.directive', [ + 'bulbs.cms.site.config', + 'genericAjaxButton' +]) + .directive('saveButton', function (CmsConfig) { + return { + controller: 'GenericAjaxButtonController', + link: { + pre: function (scope) { + scope.cssIconComplete = 'glyphicon-floppy-disk'; + scope.textProgress = 'Saving...'; + scope.textComplete = 'Save'; + } + }, + restrict: 'E', + scope: { + disableWhen: '&', + clickFunction: '=', + }, + templateUrl: CmsConfig.buildComponentPath('generic-ajax-button/generic-ajax-button.html') + }; + }); + +'use strict'; + +angular.module('hideFromRssField.directive', [ + 'bulbs.cms.site.config' +]) + .directive('hideFromRssField', [ + 'CmsConfig', + function (CmsConfig) { + return { + restrict: 'E', + scope: { + article: '=' + }, + templateUrl: CmsConfig.buildComponentPath('hide-from-rss-field', 'hide-from-rss-field.html') + }; + } + ]); + +'use strict'; + +angular.module('hideFromRssField', [ + 'hideFromRssField.directive' +]); + +'use strict'; + +angular.module('bulbs.cms.imageCropModal.controller', [ + 'BettyCropper', + 'jquery', + 'ui.bootstrap.modal' +]) + .controller('ImageCropModalCtrl', [ + '$', '$modalInstance', '$scope', '$timeout', 'BettyCropper', 'Selection', + 'imageData', 'ratios', + function ($, $modalInstance, $scope, $timeout, BettyCropper, Selection, + imageData, ratios) { + + $scope.selectedCrop = null; + $scope.cropMode = false; + $scope.ratios = ratios; + $scope.finished = false; + $scope.thumb_container_styles = {}; + $scope.imageData = imageData; + + if (!$scope.image) { + $scope.image = null; + BettyCropper.get(imageData.id) + .then(function (success) { + $scope.image = success.data; + }); + } + + $scope.$watch('image', function (image) { + if (!image) { + return; + } + + var finished = true; + for (var ratio in image.selections) { + if (image.selections[ratio].source === 'auto') { + finished = false; + break; + } + } + $scope.finished = finished; + + $scope.scaleData = image.scaleToFit(550, 400); + + angular.element('.crop-image-container img').one('load', function () { + $(this).Jcrop({ + allowSelect: false, + allowMove: true, + allowResize: true, + keySupport: false + }, function () { + $scope.jcrop_api = this; + }); + }); + + $scope.image_url = image.url('original', 1200, 'jpg'); + if (!$scope.ratios) { + $scope.ratios = Object.keys(image.selections); + } + + $scope.setThumbStyles(); + }); + + $scope.$watch('selectedCrop', function (crop) { + if (!$scope.image) { + return; + } + var finished = true; + for (var ratio in $scope.image.selections) { + if ($scope.image.selections[ratio].source === 'auto' && ratio !== crop) { + finished = false; + break; + } + } + $scope.finished = finished; + }); + + $scope.selectCrop = function (ratio) { + if (!ratio) { + ratio = Object.keys($scope.image.selections)[0]; + for (var key in $scope.image.selections) { + if ($scope.image.selections[key].source === 'auto') { + ratio = key; + break; + } + } + } + var selection = $scope.image.selections[ratio].scaleBy($scope.scaleData.scale); + + if ($scope.jcrop_api) { + $scope.jcrop_api.setOptions({ + aspectRatio: selection.width() / selection.height() + }); + + $scope.jcrop_api.setSelect([ + selection.x0, + selection.y0, + selection.x1, + selection.y1 + ]); + } + + $scope.cropMode = true; + $scope.selectedCrop = ratio; + }; + + $scope.setThumbStyles = function () { + $scope.thumb_styles = $scope.thumb_styles || {}; + + for (var ratio in $scope.image.selections) { + var scaledSelection = $scope.image.selections[ratio].scaleToFit(170, 170); + $scope.thumb_container_styles[ratio] = { + 'padding-top': Math.round((180 - scaledSelection.height()) / 2) + 'px', + 'padding-bottom': '5px', + 'padding-left': Math.round((180 - scaledSelection.width()) / 2) + 'px', + 'padding-right': '5px' + }; + + $scope.thumb_styles[ratio] = $scope.image.getStyles(170, 170, ratio); + } + }; + + $scope.save = function (ratio) { + + var jcrop_selection = $scope.jcrop_api.tellSelect(); + + var newSelection = new Selection({ + x0: jcrop_selection.x, + x1: jcrop_selection.x2, + y0: jcrop_selection.y, + y1: jcrop_selection.y2, + source: 'user' + }); + newSelection = newSelection.scaleBy(1 / $scope.scaleData.scale); + if (newSelection.x1 > $scope.image.width) { + newSelection.x1 = $scope.image.width; + } + if (newSelection.y1 > $scope.image.height) { + newSelection.y1 = $scope.image.height; + } + + return this.image.updateSelection(ratio, newSelection); + }; + + $scope.saveAndQuit = function () { + var ratio = $scope.selectedCrop; + this.save(ratio).then(function (success) { + var ratio = success.data[0]; + var selection = success.data[1]; + $scope.image.selections[ratio] = selection; + }); + $scope.cropMode = false; + $modalInstance.close(imageData); + }; + + $scope.saveAndNext = function () { + var ratio = $scope.selectedCrop; + this.save(ratio).then(function (success) { + var ratio = success.data[0]; + var selection = success.data[1]; + $scope.image.selections[ratio] = selection; + + var nextRatioIndex = ($scope.ratios.indexOf(ratio) + 1) % $scope.ratios.length; + + $scope.selectCrop($scope.ratios[nextRatioIndex]); + }); + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.imageCropModal', [ + 'bulbs.cms.imageCropModal.controller', + 'bulbs.cms.site.config', + 'ui.bootstrap.modal' +]) + .factory('openImageCropModal', [ + '$modal', 'CmsConfig', + function ($modal, CmsConfig) { + + return function (imageData, ratios) { + return $modal.open({ + templateUrl: CmsConfig.buildComponentPath( + 'image-crop-modal', + 'image-crop-modal.html' + ), + controller: 'ImageCropModalCtrl', + resolve: { + imageData: function () { return imageData; }, + ratios: function () { return ratios || false; } + }, + backdrop: 'static' + }).result; + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.liveBlog.api', [ + 'bulbs.cms.site.config', + 'bulbs.cms.utils', + 'lodash', + 'moment' +]) + .service('LiveBlogApi', [ + '_', '$http', 'CmsConfig', 'moment', 'Utils', + function (_, $http, CmsConfig, moment, Utils) { + + var liveBlogEndpoint = CmsConfig.buildApiUrlRoot.bind(null, 'liveblog'); + var liveBlogEntryEndpoint = liveBlogEndpoint.bind(null, 'entry'); + + var parsePayload = function (payload) { + var data = _.cloneDeep(payload); + + if (payload.published) { + data.published = moment.tz(payload.published, CmsConfig.getTimezoneName()); + } + + if (payload.created) { + data.created = moment.tz(payload.created, CmsConfig.getTimezoneName()); + } + + if (payload.updated) { + data.updated = moment.tz(payload.updated, CmsConfig.getTimezoneName()); + } + + return data; + }; + + var cleanData = function (data) { + var payload = _.chain(data) + .omit('published') + .cloneDeep() + .value(); + + if (data.published) { + payload.published = data.published.format(); + } + + if (data.created) { + payload.created = data.created.format(); + } + + if (data.updated) { + payload.updated = data.updated.format(); + } + + return payload; + }; + + return { + createEntry: function (data) { + var payload = cleanData(data); + return $http.post(liveBlogEntryEndpoint('/'), payload) + .then(function (response) { + return parsePayload(response.data); + }); + }, + updateEntry: function (entry) { + var payload = cleanData(entry); + return $http.put(liveBlogEntryEndpoint(payload.id, '/'), payload) + .then(function (response) { + return parsePayload(response.data); + }); + }, + deleteEntry: function (entry) { + return $http.delete(liveBlogEntryEndpoint(entry.id, '/')); + }, + getEntries: function (id) { + var params; + if (id) { + params = Utils.param({ liveblog: id }); + } + + return $http.get(liveBlogEntryEndpoint('/', params)) + .then(function (response) { + return { + results: response.data.results.map(function (result) { + return parsePayload(result); + }) + }; + }); + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.liveBlog.entries.authorBridge', [ + 'bulbs.cms.site.config' +]) + .directive('liveBlogEntriesAuthorBridge', [ + '$compile', 'CmsConfig', + function ($compile, CmsConfig) { + + return { + link: function (scope, element) { + var name = CmsConfig.getLiveBlogAuthorSelectorDirectiveName(); + + scope.authors = scope.ngModel; + + if (name) { + var html = angular.element('<' + name + ' ng-model="authors">'); + + element.find('> div').html($compile(html)(scope)); + } + + scope.$watch('authors', function (newValue, oldValue) { + if (!angular.equals(newValue, oldValue)) { + scope.onUpdate({ newValue: newValue }); + scope.ngModel = newValue; + } + }, true); + }, + restrict: 'E', + scope: { + ngModel: '=', + onUpdate: '&' + }, + template: + '
' + + ' ' + + 'No live blog author selector has been configured!' + + '
' + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.liveBlog.entries', [ + 'bulbs.cms.currentUser', + 'bulbs.cms.dateTimeFilter', + 'bulbs.cms.dateTimeModal', + 'bulbs.cms.dateTimeModal', + 'bulbs.cms.liveBlog.api', + 'bulbs.cms.liveBlog.entries.authorBridge', + 'bulbs.cms.site.config', + 'bulbs.cms.utils', + 'confirmationModal', + 'OnionEditor', + 'Raven' +]) + .directive('liveBlogEntries', [ + '$q', 'CmsConfig', 'CurrentUserApi', 'LiveBlogApi', 'Raven', 'Utils', + function ($q, CmsConfig, CurrentUserApi, LiveBlogApi, Raven, Utils) { + return { + link: function (scope) { + var reportError = function (message, data) { + Raven.captureMessage(message, data); + scope.errorMessage = message; + }; + + var titleDisplay = function (entry) { + return entry.headline ? '"' + entry.headline + '"' : 'an entry'; + }; + + scope.clearError = function () { + scope.errorMessage = ''; + }; + + LiveBlogApi.getEntries(scope.article.id) + .then(function (response) { + scope.entries = response.results; + }) + .catch(function (response) { + var message = 'An error occurred retrieving entries!'; + reportError(message, { response: response }); + }); + + var panelOpen = {}; + scope.isPanelOpen = function (entry) { + if (angular.isUndefined(panelOpen[entry.id])) { + panelOpen[entry.id] = true; + } + return panelOpen[entry.id]; + }; + scope.togglePanel = function (entry) { + panelOpen[entry.id] = !panelOpen[entry.id]; + }; + scope.collapseAll = function () { + scope.entries.forEach(function (entry) { + panelOpen[entry.id] = false; + }); + }; + scope.expandAll = function () { + scope.entries.forEach(function (entry) { + panelOpen[entry.id] = true; + }); + }; + + var entryForm = 'entryForm_'; + + scope.wrapperForm = {}; + scope.makeEntryFormName = function (entry) { + return entryForm + entry.id; + }; + scope.getEntryForm = function (entry) { + var name = scope.makeEntryFormName(entry); + + if (scope.wrapperForm[name]) { + return scope.wrapperForm[name]; + } + scope.wrapperForm[name] = {}; + return scope.wrapperForm[name]; + }; + scope.isEntryFormSaveDisabled = function (entry) { + return scope.transactionsLocked() || scope.getEntryForm(entry).$pristine; + }; + + var lock = Utils.buildLock(); + scope.transactionsLocked = lock.isLocked; + + scope.addEntry = lock(function () { + + return CurrentUserApi.getCurrentUserWithCache() + .then(function (user) { + var now = moment(); + + return LiveBlogApi.createEntry({ + liveblog: scope.article.id, + created_by: user, + created: now, + updated_by: user, + updated: now + }) + .then(function (entry) { + scope.entries.unshift(entry); + }) + .catch(function (response) { + var message = 'An error occurred attempting to add an entry!'; + reportError(message, { response: response }); + }); + }); + }); + + scope.saveEntry = lock(function (entry) { + + return CurrentUserApi.getCurrentUserWithCache() + .then(function (user) { + var oldUpdateBy = entry.updated_by; + var oldUpdated = entry.updated; + + entry.updated_by = user; + entry.updated = moment(); + + return LiveBlogApi.updateEntry(entry) + .then(function () { + scope.getEntryForm(entry).$setPristine(); + }) + .catch(function (response) { + entry.updated_by = oldUpdateBy; + entry.updated = oldUpdated; + + var message = 'An error occurred attempting to save ' + titleDisplay(entry) + '!'; + reportError(message, { response: response }); + + return $q.reject(); + }); + }); + }); + + scope.publishAndSave = function (entry, newDate) { + var oldDate = entry.published; + + entry.published = newDate; + + return scope.saveEntry(entry) + .then(function () { + // sort entries from falsy to newest publish date to oldest publish date + scope.entries.sort(function (entry1, entry2) { + var entry1IsMoment = moment.isMoment(entry1.published); + var entry2IsMoment = moment.isMoment(entry2.published); + + if (entry1IsMoment && + (!entry2IsMoment || entry1.published.isBefore(entry2.published))) { + return 1; + } else if (entry2IsMoment && + (!entry1IsMoment || entry2.published.isBefore(entry1.published))) { + return -1; + } + + return 0; + }); + }) + .catch(function () { + entry.published = oldDate; + return false; + }); + }; + + scope.deleteEntry = lock(function (entry) { + + return LiveBlogApi.deleteEntry(entry) + .then(function () { + var index = scope.entries.indexOf(entry); + Utils.removeFrom(scope.entries, index); + }) + .catch(function (response) { + var message = 'An error occurred attempting to delete ' + titleDisplay(entry) + '!'; + reportError(message, { response: response }); + }); + }); + }, + restrict: 'E', + scope: { + article: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'live-blog', + 'live-blog-entries', + 'live-blog-entries.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.liveBlog', [ + 'bulbs.cms.liveBlog.entries' +]); + +'use strict'; + +angular.module('notifications.edit.directive', [ + 'apiServices.notification.factory', + 'BettyCropper', + 'bulbs.cms.site.config', + 'copyButton', + 'customSearch', + 'lodash', + 'saveButton.directive', + 'notifications.settings', + 'topBar' +]) + .directive('notificationsEdit', function (CmsConfig) { + return { + controller: function (_, $location, $q, $scope, NOTIFICATIONS_LIST_REL_PATH, Notification) { + + $scope.LIST_URL = CmsConfig.buildExternalUrl(NOTIFICATIONS_LIST_REL_PATH); + + $scope.needsSave = false; + + var modelId = $scope.getModelId(); + if (modelId === 'new') { + // this is a new notification, build it + $scope.model = Notification.$build(); + $scope.isNew = true; + } else { + // this is an existing notification, find it + $scope.model = Notification.$find($scope.getModelId()); + } + + window.onbeforeunload = function (e) { + if (!_.isEmpty($scope.model.$dirty()) || $scope.isNew || $scope.needsSave) { + // unsaved changes, show confirmation alert + return 'You have unsaved changes.'; + } + }; + + $scope.$on('$destroy', function() { + // ensure even is cleaned up when we leave + delete window.onbeforeunload; + }); + + $scope.saveModel = function () { + var promise; + + if ($scope.model) { + // have model, use save promise as deferred + promise = $scope.model.$save().$asPromise().then(function (data) { + if (modelId === 'new') { + $location.path('/cms/app/notification/edit/' + data.id + '/'); + } + $scope.isNew = false; + $scope.needsSave = false; + }); + } else { + // no model, this is an error, defer and reject + var deferred = $q.defer(); + deferred.reject(); + promise = deferred.promise; + } + + return promise; + }; + }, + restrict: 'E', + scope: { + getModelId: '&modelId' + }, + templateUrl: CmsConfig.buildComponentPath('notifications/notifications-edit/notifications-edit.html') + }; + }); + +'use strict'; + +angular.module('notifications.edit', [ + 'bulbs.cms.site.config', + 'notifications.edit.directive' +]) + .config(function ($routeProvider) { + $routeProvider + .when('/cms/app/notifications/edit/:id/', { + controller: function ($routeParams, $scope, $window, CmsConfig) { + $window.document.title = CmsConfig.getCmsName() + ' | Edit Notification'; + $scope.routeId = $routeParams.id; + }, + template: '', + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('notifications.list', [ + 'apiServices.notification.factory', + 'bulbs.cms.site.config', + 'listPage', + 'moment', + 'notifications.settings' +]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/notifications/', { + controller: function ($scope, $window, NOTIFICATIONS_LIST_REL_PATH, Notification) { + $window.document.title = CmsConfig.getCmsName() + ' | Notification'; + $scope.modelFactory = Notification; + $scope.LIST_URL = CmsConfig.buildExternalUrl(NOTIFICATIONS_LIST_REL_PATH); + }, + templateUrl: CmsConfig.buildComponentPath('notifications/notifications-list/notifications-list-page.html') + }); + }); + +'use strict'; + +angular.module('notifications.settings', []) + .value('NOTIFICATIONS_LIST_REL_PATH', '/notification/'); + +'use strict'; + +angular.module('notifications', [ + 'notifications.list', + 'notifications.edit' +]); + +'use strict'; + +angular.module('polls.edit.directive', [ + 'apiServices.answer.factory', + 'apiServices.poll.factory', + 'BettyCropper', + 'bulbs.cms.site.config', + 'lodash', + 'saveButton.directive', + 'topBar' +]).constant('RESPONSE_TYPES', [ + { + name: 'Text Only', + value: 'Text' + }, + { + name: 'Image + Text', + value: 'Image' + } +]) +.directive('pollsEdit', function (CmsConfig) { + return { + templateUrl: CmsConfig.buildComponentPath('polls/polls-edit/polls-edit.html'), + controller: function (_, $http, $location, $q, $routeParams, $scope, $timeout, Answer, Poll) { + // populate model for use + if ($routeParams.id === 'new') { + $scope.model = {}; + $scope.isNew = true; + } else { + Poll.getPoll($routeParams.id) + .then(function successCallback(response) { + $scope.model = response; + $scope.answers = _.cloneDeep(response.answers); + }); + } + + window.onbeforeunload = function (e) { + if(!_.isEmpty($scope.model.$dirty()) || $scope.isNew || $scope.needsSave) { + // show confirmation alert + return 'You have unsaved changes.'; + } + }; + + $scope.$on('$destroy', function () { + // remove alert when we go + delete window.onbeforeunload; + }); + + $scope.embedCode = function () { + return ''; + }; + + $scope.validatePublication = function () { + // The datetime-selection-modal-opener interacts with scope + // in such a way that modal-on-close="validatePublication()" + // fires before the scope model data has changed. + $timeout(function () { + var published = $scope.model.published; + var endDate = $scope.model.end_date; + var publishedField = $scope.pollForm.published; + var endDateField = $scope.pollForm.endDate; + + publishedField.$setValidity( + 'requiredWithEndDate', + !(endDate && !published) + ); + + var comesAfterPublishedValid = true; + if (endDate && published) { + comesAfterPublishedValid = published.isBefore(endDate); + } + endDateField.$setValidity( + 'comesAfterPublished', + comesAfterPublishedValid + ); + }); + }; + + $scope.saveModel = function () { + if ($scope.model) { + + if(!$scope.isNew) { + Answer.updatePollAnswers($scope); + // reset deleted answers + $scope.deletedAnswers = []; + return Poll.updatePoll($scope.model); + + } else { + return Poll.postPoll($scope.model).then(function (data) { + var answerPromise = _.map($scope.answers, function (answer) { + return Answer.postAnswer(answer, data.id); + }); + + return $q.all(answerPromise).then(function () { + $location.path('/cms/app/polls/edit/' + data.id + '/'); + }); + }); + } + } else { + return $q.reject('Save failed'); + } + }; + + $scope.deletedAnswers = []; + var newId = ($scope.answers) ? $scope.answers.length : 0; + + $scope.addAnswer = function () { + $scope.answers.push({id: newId++, notOnSodahead: true}); + }; + + // create 3 blank answer objects if this is a new poll + if($scope.isNew) { + $scope.answers = []; + _.times(3, $scope.addAnswer); + } + + + $scope.removeAnswer = function (answerId) { + var deletedAnswer = _.remove($scope.answers, function (a) { + return a.id === answerId; + }); + if(deletedAnswer[0].notOnSodahead) { return; } + $scope.deletedAnswers.push(deletedAnswer[0]); + }; + }, + restrict: 'E', + scope: { getModelId: '&modelId' }, + }; +}); + +'use strict'; + +angular.module('polls.edit', [ + 'bulbs.cms.site.config', + 'polls.edit.directive' +]) + .config(function ($routeProvider) { + $routeProvider + .when('/cms/app/polls/edit/:id/', { + controller: function ($routeParams, $scope, $window, CmsConfig) { + $window.document.title = CmsConfig.getCmsName() + ' | Edit Poll'; + $scope.routeId = $routeParams.id; + }, + template: '', + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('polls.list', [ + 'apiServices.poll.factory', + 'bulbs.cms.site.config', + 'bulbsCmsApp.nonRestmodListPage', + 'moment' +]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/polls/', { + controller: function ($scope, $window, Poll) { + $window.document.title = CmsConfig.getCmsName() + ' | Poll'; + $scope.modelFactory = Poll; + }, + templateUrl: CmsConfig.buildComponentPath('polls/polls-list/polls-list-page.html') + }); + }); + +'use strict'; + +angular.module('polls', [ + 'polls.list', + 'polls.edit' +]); + +'use strict'; + +angular.module('promotedContentArticle.directive', [ + 'bulbs.cms.site.config' +]) + .directive('promotedContentArticle', function (CmsConfig) { + return { + restrict: 'E', + scope: { + article: '=' + }, + templateUrl: CmsConfig.buildComponentPath('promoted-content/promoted-content-article/promoted-content-article.html') + }; + }); + +'use strict'; + +angular.module('promotedContentArticle', [ + 'promotedContentArticle.directive' +]); + +'use strict'; + +angular.module('promotedContentList.directive', [ + 'bulbs.cms.site.config', + 'promotedContentArticle', + 'promotedContentSave', + 'ui.sortable' +]) + .directive('promotedContentList', function ($, CmsConfig) { + return { + controller: function ($scope, PromotedContentService) { + + $scope.pzoneData = PromotedContentService.getData(); + + $scope.moveUp = function (index) { + PromotedContentService.moveContentUp(index); + }; + + $scope.moveDown = function (index) { + PromotedContentService.moveContentDn(index); + }; + + $scope.remove = function (article) { + PromotedContentService.$removeContentFromPZone(article.id); + }; + + $scope.completeAction = function (index) { + PromotedContentService.$completeContentAction(index); + }; + + $scope.stopAction = function () { + PromotedContentService.stopContentAction(); + }; + + $scope.markDirty = function () { + PromotedContentService.markDirtySelectedPZone(); + }; + + $scope.disableControls = function () { + return PromotedContentService.isPZoneRefreshPending(); + }; + }, + link: function (scope, element, attr) { + + scope.sortableOptions = { + beforeStop: function (e, ui) { + ui.helper.css('margin-top', 0); + ui.item.parent().removeClass('ui-sortable-dragging'); + }, + cancel: '.ui-sortable-unsortable', + change: function (e, ui) { + ui.helper.css('margin-top', $(window).scrollTop()); + }, + containment: 'promoted-content-list', + distance: 3, + opacity: 0.75, + placeholder: 'dropzone', + start: function (e, ui) { + ui.item.parent().addClass('ui-sortable-dragging'); + ui.helper.css('margin-top', $(window).scrollTop()); + }, + stop: function () { + scope.markDirty(); + } + }; + }, + restrict: 'E', + scope: {}, + templateUrl: CmsConfig.buildComponentPath('promoted-content/promoted-content-list/promoted-content-list.html') + }; + }); + +'use strict'; + +angular.module('promotedContentList', [ + 'promotedContentList.directive' +]); + +'use strict'; + +angular.module('promotedContentOperationsList.directive', [ + 'bulbs.cms.site.config', + 'promotedContent.service' +]) + .directive('promotedContentOperationsList', function (_, CmsConfig, moment) { + return { + controller: function (moment, $scope, PromotedContentService) { + + $scope.pzoneData = PromotedContentService.getData(); + $scope.scheduleDateFrom = moment(); + $scope.scheduleDateTo = moment().add(3, 'days'); + $scope.deleteStatus = { + message: '', + isError: false + }; + + PromotedContentService.$ready() + .then(function () { + $scope.aggregatedOperations = $scope.pzoneData.operations.concat($scope.pzoneData.unsavedOperations); + }); + + $scope.removeOperation = function (operation) { + PromotedContentService.$removeOperation(operation.id) + .then(function () { + $scope.deleteStatus = { + message: 'Operation successfully removed!', + isError: false + }; + }) + .catch(function (err) { + $scope.deleteStatus = { + message: err, + isError: true + }; + }); + }; + + $scope.clearDeleteStatus = function () { + $scope.deleteStatus.message = ''; + }; + + $scope.setPreviewTime = function (time) { + // set preview time to time plus a minute so that all operations occuring in that + // minute can be previewed + PromotedContentService.setPreviewTime(time.add(1, 'minute')); + }; + + $scope.disableControls = function () { + return PromotedContentService.isPZoneRefreshPending(); + }; + + $scope.operationsStale = function () { + return PromotedContentService.isPZoneOperationsStale(); + }; + + $scope.refreshingOperations = false; + $scope.refreshOperations = function () { + + if (!$scope.refreshingOperations) { + $scope.refreshingOperations = true; + PromotedContentService.$refreshOperations({ + from: $scope.scheduleDateFrom.toISOString(), + to: $scope.scheduleDateTo.toISOString() + }) + .finally(function () { + $scope.refreshingOperations = false; + }); + } + }; + }, + link: function (scope, element, attr) { + + var operationTime = function (operation) { + var compTime; + if (operation.whenAsMoment) { + // has a time, use that + compTime = operation.whenAsMoment; + } else if (scope.pzoneData.previewTime){ + // has no time, but preview time is set, use that + compTime = scope.pzoneData.previewTime; + } else { + // this is an immediate operation + compTime = moment(); + } + return compTime; + }; + + scope.aggregatedOperations = {}; + scope.groupDateFormat = 'M/D/YY @ h:mma'; + var aggregator = function () { + var tempAggregate = scope.pzoneData.operations.concat(scope.pzoneData.unsavedOperations); + + scope.aggregatedOperations = _.chain(tempAggregate) + .sortBy(operationTime) + .groupBy(function (operation) { + return operationTime(operation).format(scope.groupDateFormat); + }) + .pairs() + .map(function (pair) { + return [moment(pair[0], scope.groupDateFormat), pair[1]]; + }) + .sortBy(function (pair) { + return pair[0]; + }) + .value(); + }; + + scope.$watchCollection('pzoneData.operations', aggregator); + scope.$watchCollection('pzoneData.unsavedOperations', aggregator); + + }, + restrict: 'E', + scope: {}, + templateUrl: CmsConfig.buildComponentPath('promoted-content/promoted-content-operations-list/promoted-content-operations-list.html') + }; + }); + +'use strict'; + +angular.module('promotedContentOperationsList', [ + 'promotedContentOperationsList.directive' +]); + +'use strict'; + +angular.module('promotedContentPzoneSelect.directive', [ + 'bulbs.cms.site.config', + 'promotedContent.service' +]) + .directive('promotedContentPzoneSelect', function (CmsConfig) { + return { + controller: function ($scope, PromotedContentService) { + + $scope.pzoneData = PromotedContentService.getData(); + $scope.selectedPZoneName = ''; + + PromotedContentService.$ready() + .then(function () { + $scope.selectedPZoneName = $scope.pzoneData.selectedPZone.name; + }); + + $scope.changePZone = function (name) { + (function (name) { + PromotedContentService.$refreshPZones() + .then(function () { + PromotedContentService.$selectPZone(name); + }); + })(name); + }; + + $scope.disableControls = function () { + return PromotedContentService.isPZoneRefreshPending(); + }; + }, + restrict: 'E', + scope: {}, + templateUrl: CmsConfig.buildComponentPath('promoted-content/promoted-content-pzone-select/promoted-content-pzone-select.html') + }; + }); + +'use strict'; + +angular.module('promotedContentPzoneSelect', [ + 'promotedContentPzoneSelect.directive' +]); + +'use strict'; + +angular.module('promotedContentSave.directive', [ + 'bulbs.cms.site.config', + 'promotedContent.service' +]) + .directive('promotedContentSave', function (CmsConfig) { + return { + controller: function ($scope, PromotedContentService) { + + $scope.pzoneData = PromotedContentService.getData(); + + $scope.savePZone = function () { + PromotedContentService.$saveSelectedPZone(); + }; + + $scope.clearOperations = function () { + // clear any unsaved operations + PromotedContentService.clearUnsavedOperations(); + + // refresh selected pzone + PromotedContentService.$refreshSelectedPZone($scope.pzoneData.previewTime); + }; + + $scope.disableControls = function () { + return PromotedContentService.isPZoneRefreshPending(); + }; + }, + restrict: 'E', + scope: {}, + templateUrl: CmsConfig.buildComponentPath('promoted-content/promoted-content-save/promoted-content-save.html') + }; + }); + +'use strict'; + +angular.module('promotedContentSave', [ + 'promotedContentSave.directive' +]); + +'use strict'; + +angular.module('promotedContentSearch.directive', [ + 'bulbs.cms.site.config', + 'statusFilter', + 'filterWidget', + 'promotedContent.service', + 'promotedContentArticle' +]) + .directive('promotedContentSearch', function (CmsConfig) { + return { + controller: function (_, moment, $scope, $location, PromotedContentService) { + + $scope.pzoneData = PromotedContentService.getData(); + $scope.pageNumber = $location.search().page || '1'; + + $scope.goToPage = function () { + PromotedContentService.$refreshAllContent({page: $scope.pageNumber}, true); + }; + + /** + * Check if an content is enabled. Actions are allowed if preview time is + * set to immediate and the content is already published, or if a preview + * time is set into the future and the content will be published before that. + * In either case, content is only draggable if it is not already listed. + * + * @param {object} content - content to check if enabled. + * @returns {Boolean} true if content is enabled, false otherwise. + */ + $scope.contentIsEnabled = function (content) { + var notAlreadyInList = + ($scope.pzoneData.selectedPZone && + _.isUndefined(_.find($scope.pzoneData.selectedPZone.content, {id: content.id}))); + var immediateDraggable = + ($scope.pzoneData.previewTime === null && + moment().isAfter(content.published)); + var futureDraggable = + ($scope.pzoneData.previewTime !== null && + moment().isBefore($scope.pzoneData.previewTime) && + $scope.pzoneData.previewTime.isAfter(content.published)); + + return notAlreadyInList && (immediateDraggable || futureDraggable); + }; + + $scope.beginInsert = function (article) { + PromotedContentService.beginContentInsert(article); + }; + + $scope.beginReplace = function (article) { + PromotedContentService.beginContentReplace(article); + }; + + $scope.stopAction = function () { + PromotedContentService.stopContentAction(); + }; + + $scope.disableControls = function () { + return PromotedContentService.isPZoneRefreshPending(); + }; + }, + link: function (scope, element, attr) { + + scope.tools = null; + scope.openToolsFor = function (article) { + var doOpen = false; + if (!scope.disableControls()) { + scope.tools = article.id; + doOpen = true; + } + return doOpen; + }; + + scope.closeTools = function () { + scope.tools = null; + return true; + }; + + scope.toolsOpenFor = function (article) { + return scope.tools === article.id; + }; + }, + restrict: 'E', + scope: {}, + templateUrl: CmsConfig.buildComponentPath('promoted-content/promoted-content-search/promoted-content-search.html') + }; + }); + +'use strict'; + +angular.module('promotedContentSearch', [ + 'promotedContentSearch.directive' +]); + +'use strict'; + +/** + * Main service for promoted content page. Handles all data, all data modifications + * for this page should be done through this service. + */ +angular.module('promotedContent.service', [ + 'contentServices', + 'moment', + 'restangular' +]) + .service('PromotedContentService', function ($, _, moment, $q, Restangular, + ContentFactory, ContentListService) { + + var PromotedContentService = this; + PromotedContentService._serviceData = { + allContent: ContentListService.getData(), + actionContent: null, + action: null, + pzones: [], + unsavedOperations: [], + operations: [], + selectedPZone: null, + previewTime: null + }; + var _data = PromotedContentService._serviceData; + + // promise that resolves once this service is done setting up + var setupDefer = $q.defer(); + + var readableOperationTypes = { + INSERT: 'INSERT', + DELETE: 'DELETE', + REPLACE: 'REPLACE' + }; + PromotedContentService.readableOperationTypes = readableOperationTypes; + + var operationTypeToReadable = { + 'promotion_insertoperation': readableOperationTypes.INSERT, + 'promotion_deleteoperation': readableOperationTypes.DELETE, + 'promotion_replaceoperation': readableOperationTypes.REPLACE + }; + var readableToOperationType = + _.reduce(operationTypeToReadable, function (result, val, key) { + result[val] = key; + return result; + }, {}); + + // flag to check if there's a pzone data refresh pending + var pzoneRefreshPending = false; + // flag to check if operations data is stale + var pzoneOperationsStale = true; + + /** + * Getter for refresh pending flag. Use this to check if a pzone is in the + * process of loading its data, to prevent editing. + * + * @returns {Boolean} true if pzone data is refreshing, false otherwise. + */ + PromotedContentService.isPZoneRefreshPending = function () { + return pzoneRefreshPending; + }; + + /** + * Getter for pzone operations stale flag. Use this to check if operations list + * is out of sync and needs to be refreshed before data is accurate. + * + * @returns {Boolean} ture if pzone operations are stale, false otherwise. + */ + PromotedContentService.isPZoneOperationsStale = function () { + return pzoneOperationsStale; + }; + + /** + * Refresh pzone data, given the following parameters: + * + * @param {Object} filters - filter zones with these parameters. + * @returns {Promise} resolves with pzone data, or rejects with an error message. + */ + PromotedContentService.$refreshPZones = function (filters) { + var deferred = $q.defer(); + + // start a new request if one isn't already pending + if (!pzoneRefreshPending) { + pzoneRefreshPending = true; + + return ContentFactory.all('pzone').getList(filters) + .then(function (data) { + _data.pzones = data; + // mark everything as saved + _.each(_data.pzones, function (pzone) { + pzone.saved = true; + }); + + deferred.resolve(); + + // resolve with pzones + return _data.pzones; + }) + .catch(function (err) { + deferred.reject(); + return err; + }) + .finally(function () { + pzoneRefreshPending = false; + }); + } else { + deferred.reject(); + } + + return deferred.promise; + }; + + /** + * Mark selected pzone as dirty. + */ + PromotedContentService.markDirtySelectedPZone = function () { + delete _data.selectedPZone.saved; + }; + + /** + * Mark selected pzone as saved (not dirty). + */ + PromotedContentService.markSavedSelectedPZone = function () { + _data.selectedPZone.saved = true; + }; + + /** + * Make the list of operations stale, meaning the user will have to manually + * refresh it. + */ + PromotedContentService.makeOperationsStale = function () { + _data.operations = []; + pzoneOperationsStale = true; + }; + + /** + * Save the currently selected pzone by posting all operations at currently + * selected time. If no time is selected, pzone will be immediately updated. + * + * @returns {Promise} resolves with selected pzone once saving is done. + */ + PromotedContentService.$saveSelectedPZone = function () { + var defer = $q.defer(); + + if (_data.previewTime && _data.previewTime.isAfter(moment())) { + PromotedContentService.makeOperationsStale(); + + // grab operations out of unsaved operations and post them into operations list + _.each(_data.unsavedOperations, function (operation) { + // use preview time, or send null if immediate + operation.when = _data.previewTime ? _data.previewTime.toISOString() : null; + // remove client side client_id + delete operation.client_id; + }); + + // post all operations as an array + _data.selectedPZone.all('operations').post(_data.unsavedOperations) + .then(function () { + defer.resolve(_data.selectedPZone); + }); + + // clear whatever unsaved operations we have, shouldn't be any in this case + PromotedContentService.clearUnsavedOperations(); + + } else if (!_data.previewTime){ + // clear whatever unsaved operations we have, shouldn't be an in this case + PromotedContentService.clearUnsavedOperations(); + + PromotedContentService.makeOperationsStale(); + + // no preview time is set, post pzone immediately + _data.selectedPZone.put() + .then(function () { + defer.resolve(_data.selectedPZone); + }) + .catch(function (err) { + defer.reject(err); + }); + } else { + // preview time is in the past, error out + defer.reject('Cannot save operations in the past.'); + } + + return defer.promise; + }; + + /** + * Refresh content data using ContentListService. + * + * @param {...object} var_args - arguments taken by [ContentListService.$updateContent]{@link ContentListService#$updateContent}. + * @returns {Promise} resolves based on [ContentListService.$updateContent]{@link ContentListService#$updateContent}. + */ + PromotedContentService.$refreshAllContent = function () { + return ContentListService.$updateContent.apply(ContentListService, arguments); + }; + + /** + * Create a new operation. Note, this will not be saved until user clicks + * save, at which point the new item should be posted to the operations list. + * If preview time is set to immediate, no operation will be created, and this + * function will resolve with nothing. + * + * @param {Object} props - properties of new operation. + * @returns {Promise} resolves with new operation or nothing, or rejects with an error message. + */ + PromotedContentService.$addOperation = function (props) { + var defer = $q.defer(); + + if (!PromotedContentService.isPreviewTimeImmediate()) { + if (!PromotedContentService.isPreviewTimePast()) { + var lastId = _.max(_data.unsavedOperations, 'client_id').client_id; + var nextId = lastId ? lastId + 1 : 0; + var allProps = _.assign({ + client_id: nextId, + type_name: readableToOperationType[props.cleanType] || '', + pzone: _data.selectedPZone.id, + applied: false, + content: null, + content_title: '', + index: null + }, props); + + var operation = Restangular.restangularizeElement( + _data.selectedPZone.all('operations'), allProps + ); + _data.unsavedOperations.push(operation); + + defer.resolve(operation); + } else { + // we are looking at the past, we cannot add new operations + defer.reject('Cannot add operations in the past.'); + } + } else { + // preview time is immediate, don't add an operation + defer.resolve(); + } + + return defer.promise; + }; + + /** + * Remove an operation from operation list. Only saved, future operations are removable. + * + * @param {Number} id - id of operation to remove. + * @returns {Promise} promise that resolves with nothing, or rejects with an + * error message. + */ + PromotedContentService.$removeOperation = function (id) { + var defer = $q.defer(); + + // delete this from the saved operations list + var index = _.findIndex(_data.operations, {id: id}); + var operation = _data.operations[index]; + if (operation) { + if (operation.whenAsMoment.isAfter(moment())) { + operation.remove() + .then(function () { + // remove operation and resolve + _data.operations.splice(index, 1); + defer.resolve(); + }) + .catch(function (err) { + if (err.status === 404) { + defer.reject('Cannot find operation to delete.'); + } else { + defer.reject(err); + } + }); + } else { + defer.reject('Cannot delete an operation in the past.'); + } + } else { + defer.reject('Could not find saved operation with id ' + id + ' to delete.'); + } + + return defer.promise; + }; + + /** + * Clear unsaved operations list. + */ + PromotedContentService.clearUnsavedOperations = function () { + _data.unsavedOperations = []; + PromotedContentService.markSavedSelectedPZone(); + }; + + /** + * Refresh operations data for selected pzone. Each operation returned will + * contain an additional property called cleanType that is the clean, + * displayable representation of the operation type. + * + * GOD DAMN IT RACE CONDITIONS NOTE: To avoid race conditions, only call + * this function as a result of user interaction. + * + * @param {object} params - query parameters to append to request. + * @returns {Promise} resolves with operation data, or rejects with an error message. + */ + PromotedContentService.$refreshOperations = function (params) { + return _data.selectedPZone.getList('operations', params) + .then(function (data) { + + _data.operations = data; + + _.each(_data.operations, function (operation) { + operation.cleanType = operationTypeToReadable[operation.type_name]; + operation.whenAsMoment = moment(operation.when); + }); + + pzoneOperationsStale = false; + + return _data.operations; + }) + .catch(function (err) { + return err; + }); + }; + + /** + * Select a pzone with the given name. Will refresh operations list. + * + * @param {string} [name] - name of pzone to select, selects first pzone if + * name not provided. + * @returns {Promise} resolves based on $refreshSelectedPZone Promise. + */ + PromotedContentService.$selectPZone = function (name) { + // select pzone to edit + _data.selectedPZone = _.find(_data.pzones, {name: name}) || _data.pzones[0]; + + // immediately clear any unsaved operations + PromotedContentService.clearUnsavedOperations(); + + // begin refreshing interface with new pzone data + return PromotedContentService.$refreshSelectedPZone(_data.previewTime); + }; + + /** + * Remove content from currently selected pzone. + * + * @param {Number} contentId - id of content to delete. + * @returns {Promise} resolves if content removed, or rejects with an error message. + */ + PromotedContentService.$removeContentFromPZone = function (contentId) { + var defer = $q.defer(); + var i = _.findIndex(_data.selectedPZone.content, {id: contentId}); + if (i >= 0) { + // found it, splice away + PromotedContentService.$addOperation({ + cleanType: readableOperationTypes.DELETE, + content: contentId, + content_title: _data.selectedPZone.content[i].title + }).then(function () { + PromotedContentService.markDirtySelectedPZone(); + _data.selectedPZone.content.splice(i, 1); + defer.resolve(); + }).catch(function (err) { + defer.reject(err); + }); + } else { + defer.reject('Could not find content with given id to delete.'); + } + return defer.promise; + }; + + /** + * Content moving function. + * + * @param {Number} indexFrom - Index to move content from. + * @param {Number} indexTo - Index to move content to. + * @returns {Boolean} true if content moved, false otherwise. + */ + var moveTo = function (indexFrom, indexTo) { + var ret = false; + var content = _data.selectedPZone.content; + if (indexFrom >= 0 && indexFrom < content.length && + indexTo >= 0 && indexTo < content.length) { + var splicer = content.splice(indexFrom, 1, content[indexTo]); + if (splicer.length > 0) { + content[indexTo] = splicer[0]; + ret = true; + PromotedContentService.markDirtySelectedPZone(); + } + } + return ret; + }; + + /** + * Move content up in the currently selected pzone. + * + * @param {Number} index - index of content to move up. + * @returns {Boolean} true if moved up, false otherwise. + */ + PromotedContentService.moveContentUp = function (index) { + return moveTo(index, index - 1); + }; + + /** + * Move content down in the currently selected pzone. + * + * @param {Number} index - index of content to move down. + * @return {Boolean} true if moved down, false otherwise. + */ + PromotedContentService.moveContentDn = function (index) { + return moveTo(index, index + 1); + }; + + /** + * Begin content insert action. + * + * @param {Object} article - article to be inserted. + */ + PromotedContentService.beginContentInsert = function (article) { + _data.actionContent = article; + _data.action = readableOperationTypes.INSERT; + }; + + /** + * Begin content replace operation. + * + * @param {Object} article - article to be replaced. + */ + PromotedContentService.beginContentReplace = function (article) { + _data.actionContent = article; + _data.action = readableOperationTypes.REPLACE; + }; + + /** + * Stop doing current action. + */ + PromotedContentService.stopContentAction = function () { + _data.actionContent = null; + _data.action = null; + }; + + /** + * Complete insert or replace operation. + * + * @param {Number} index - index where operation will occur. + * @returns {Promise} resolves with nothing or rejects with an error message. + */ + PromotedContentService.$completeContentAction = function (index) { + var deferred = $q.defer(); + + if (_data.action) { + PromotedContentService.$addOperation({ + cleanType: _data.action, + content: _data.actionContent.id, + content_title: _data.actionContent.title, + index: index + }) + .then(function () { + // find index of duplicate if there is one + var duplicateIndex = _.findIndex(_data.selectedPZone.content, {id: _data.actionContent.id}); + + // ensure that duplicate is deleted + if (index !== duplicateIndex && duplicateIndex >= 0) { + _data.selectedPZone.content.splice(duplicateIndex, 1); + } + + // add item to pzone + var replace = _data.action === readableOperationTypes.REPLACE; + _data.selectedPZone.content.splice(index, (replace ? 1 : 0), _data.actionContent); + + // stop action + PromotedContentService.stopContentAction(); + + // ensure pzone is marked dirty + PromotedContentService.markDirtySelectedPZone(); + + deferred.resolve(); + }) + .catch(deferred.reject); + } else { + deferred.reject('No action to complete in progress.'); + } + + return deferred.promise; + }; + + /** + * Set preview time to some moment. Applying this operation will cause the + * unsaved operations list to clear out. + * + * @param {moment} time - moment to set _data.preview time as. + */ + PromotedContentService.setPreviewTime = function (time) { + // set time to edit + _data.previewTime = time; + + // clear all unsaved operations + PromotedContentService.clearUnsavedOperations(); + + // begin requesting pzone to edit + return PromotedContentService.$refreshSelectedPZone(_data.previewTime); + }; + + /** + * Set preview time to now, effectively causing all operations to be + * immediately applied when saved. + */ + PromotedContentService.setPreviewTimeToImmediate = function () { + PromotedContentService.setPreviewTime(null); + }; + + /** + * Check if preview time is set to immediate. + * + * @returns true if preview time is immediate, false otherwise. + */ + PromotedContentService.isPreviewTimeImmediate = function () { + return _data.previewTime === null; + }; + + /** + * Check if set preview time is in the past. + * + * @returns true if preview time is in the past, false otherwise. + */ + PromotedContentService.isPreviewTimePast = function () { + return !PromotedContentService.isPreviewTimeImmediate() && + _data.previewTime.isBefore(moment()); + }; + + /** + * Refresh the currently selected pzone. Prevents new requests until the + * current one has resolved. + * + * @param {moment} [time] - optional time parameter to pass to get a preview. + * @returns {Promise} resolves with selected pzone data or reject with an error. + */ + PromotedContentService.$refreshSelectedPZone = function (time) { + var params = {}; + if (time) { + params.preview = time.toISOString(); + } + + PromotedContentService.makeOperationsStale(); + + var deferred = $q.defer(); + + // start a new request if one isn't already pending + if (!pzoneRefreshPending) { + pzoneRefreshPending = true; + _data.selectedPZone.get(params) + .then(function (data) { + deferred.resolve(); + _data.selectedPZone = data; + return _data.selectedPZone; + }) + .catch(function (err) { + deferred.reject(); + return err; + }) + .finally(function () { + PromotedContentService.markSavedSelectedPZone(); + pzoneRefreshPending = false; + }); + } else { + deferred.reject(); + } + + return deferred.promise; + }; + + /** + * Get the service's data. This function MUST be used to retrieve service + * data, accessing service data via the _serviceData variable could + * potentially destroy some two-way databinding magic. + * + * @returns {Object} service data. + */ + PromotedContentService.getData = function () { + return _data; + }; + + /** + * @returns {Promise} resolves when service is ready. + */ + PromotedContentService.$ready = function () { + return setupDefer.promise; + }; + + // setup initial datas + PromotedContentService.$refreshPZones() + .then(function () { + return PromotedContentService.$refreshAllContent(); + }) + .then(function () { + return PromotedContentService.$selectPZone(); + }) + .then(function () { + // service is ready to go + setupDefer.resolve(); + }); + + }); + +'use strict'; + +angular.module('promotedContentTimePicker.directive', [ + 'bulbs.cms.site.config', + 'promotedContent.service' +]) + .directive('promotedContentTimePicker', function (CmsConfig) { + return { + controller: function (moment, $scope, PromotedContentService) { + + $scope.contentData = PromotedContentService.getData(); + + $scope.setPreviewTime = function (previewTime) { + PromotedContentService.setPreviewTime(previewTime); + }; + + $scope.setPreviewTimeToImmediate = function () { + $scope.previewTime = null; + PromotedContentService.setPreviewTimeToImmediate(); + }; + + $scope.disableControls = function () { + return PromotedContentService.isPZoneRefreshPending(); + }; + }, + restrict: 'E', + scope: {}, + templateUrl: CmsConfig.buildComponentPath('promoted-content/promoted-content-time-picker/promoted-content-time-picker.html') + }; + }); + +'use strict'; + +angular.module('promotedContentTimePicker', [ + 'promotedContentTimePicker.directive' +]); + +'use strict'; + +angular.module('promotedContent', [ + 'bulbs.cms.site.config', + 'promotedContentPzoneSelect', + 'promotedContentList', + 'promotedContentSearch', + 'promotedContentTimePicker', + 'promotedContentOperationsList', +]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/promotion/', { + controller: [ + '$window', 'CmsConfig', + function ($window) { + $window.document.title = CmsConfig.getCmsName() + ' | Promotion Tool'; + } + ], + templateUrl: CmsConfig.buildComponentPath('promoted-content/promoted-content.html'), + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('lineItems.edit.directive', [ + 'apiServices.lineItem.factory', + 'bulbs.cms.site.config', + 'lodash', + 'saveButton.directive', + 'topBar' +]) + .directive('lineItemsEdit', function (CmsConfig) { + return { + controller: function (_, $location, $q, $routeParams, $scope, LineItem) { + if ($routeParams.id === 'new') { + $scope.model = LineItem.$build(); + $scope.isNew = true; + } else { + $scope.model = LineItem.$find($routeParams.id); + } + + window.onbeforeunload = function (e) { + if (!_.isEmpty($scope.model.$dirty()) || $scope.isNew || $scope.needsSave) { + return 'You have unsaved changes.'; + } + }; + + $scope.$on('$destroy', function() { + delete window.onbeforeunload; + }); + + $scope.saveModel = function () { + var promise; + + if ($scope.model) { + promise = $scope.model.$save().$asPromise().then(function (data) { + $location.path('/cms/app/line-items/edit/' + data.id + '/'); + }); + } else { + var deferred = $q.defer(); + deferred.reject(); + promise = deferred.promise; + } + + return promise; + }; + }, + restrict: 'E', + scope: { + getModelId: '&modelId' + }, + templateUrl: CmsConfig.buildComponentPath('reporting/reporting-line-items-edit/reporting-line-items-edit.html') + }; + }); + +'use strict'; + +angular.module('lineItems.edit', [ + 'bulbs.cms.site.config', + 'lineItems.edit.directive' +]) + .config(function ($routeProvider) { + $routeProvider + .when('/cms/app/line-items/edit/:id/', { + controller: function ($routeParams, $scope, $window, CmsConfig) { + $window.document.title = CmsConfig.getCmsName() + ' | Edit Line Item'; + $scope.routeId = $routeParams.id; + }, + template: '', + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('lineItems.list', [ + 'apiServices.lineItem.factory', + 'bulbs.cms.site.config', + 'listPage' + ]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/line-items/', { + controller: function($modal, $scope, $window, LineItem) { + $window.document.title = CmsConfig.getCmsName() + ' | Line Items'; + + $scope.modelFactory = LineItem; + + $scope.LineItemExportModal = function () { + return $modal.open({ + templateUrl: '/views/modals/line-item-export-modal.html', + controller: 'LineitemexportmodalCtrl', + }); + }; + + $scope.utilityButtons = [{ + title: 'Export CSV', + click: function () { + $scope.LineItemExportModal(); + }, + buttonClasses: 'add-item btn btn-primary', + iconClasses: 'font-awesome' + }]; + }, + + templateUrl: CmsConfig.buildComponentPath('reporting/reporting-line-items-list/reporting-line-items-list.html') + }); + }); + +'use strict'; + +angular.module('rateOverrides.edit.directive', [ + 'apiServices.rateOverride.factory', + 'apiServices.featureType.factory', + 'bulbs.cms.site.config', + 'lodash', + 'saveButton.directive', + 'topBar' +]) + .directive('rateOverridesEdit', function (CmsConfig) { + return { + controller: function (_, $location, $http, $q, $routeParams, $scope, ContentFactory, FeatureType, RateOverride, Raven) { + var resourceUrl = '/cms/api/v1/contributions/role/'; + + if ($routeParams.id === 'new') { + $scope.model = RateOverride.$build(); + $scope.isNew = true; + } else { + $scope.model = RateOverride.$find($routeParams.id); + $scope.model.$promise.then(function () { + if (($scope.model.hasOwnProperty('role')) && ($scope.model.role !== null)){ + $scope.model.role = $scope.model.role.id; + } + }); + } + + window.onbeforeunload = function (e) { + if (!_.isEmpty($scope.model.$dirty()) || $scope.isNew || $scope.needsSave) { + return 'You have unsaved changes.'; + } + }; + + $scope.$on('$destroy', function() { + delete window.onbeforeunload; + }); + + $scope.getPaymentType = function (roleId) { + if ($scope.hasOwnProperty('roles')) { + for (var i = 0; i < $scope.roles.length; i++) { + if ($scope.roles[i].id === roleId) { + return $scope.roles[i].payment_type; + } + } + return null; + } + }; + + $scope.isFeatureRated = function () { + if ($scope.getPaymentType($scope.model.role) === 'FeatureType'){ + return true; + } + return false; + }; + + $scope.isHourlyRated = function () { + if ($scope.getPaymentType($scope.model.role) === 'Hourly'){ + return true; + } + return false; + }; + + $scope.isFlatRated = function () { + if ($scope.getPaymentType($scope.model.role) === 'Flat Rate'){ + return true; + } + return false; + }; + + $scope.addFeatureType = function () { + if (!$scope.model.hasOwnProperty('featureTypes')) { + $scope.model.featureTypes = []; + } + + $scope.model.featureTypes.push({ + featureType: null, + rate: 0, + }); + }; + + $scope.getFeatureTypes = function () { + $http({ + method: 'GET', + url: resourceUrl + }).success(function (data) { + $scope.featureTypes = data.results || data; + }).error(function (data, status, headers, config) { + Raven.captureMessage('Error fetching FeatureTypes', {extra: data}); + }); + }; + + $scope.getRoles = function () { + $http({ + method: 'GET', + url: resourceUrl + }).success(function (data) { + $scope.roles = data.results || data; + }).error(function (data, status, headers, config) { + Raven.captureMessage('Error fetching Roles', {extra: data}); + }); + }; + + $scope.searchFeatureTypes = function (searchTerm) { + return FeatureType.simpleSearch(searchTerm); + }; + + $scope.saveModel = function () { + var promise; + + if ($scope.model) { + promise = $scope.model.$save().$asPromise().then(function (data) { + $location.path('/cms/app/rate-overrides/edit/' + data.id + '/'); + if (($scope.model.hasOwnProperty('role')) && ($scope.model.role !== null)){ + $scope.model.role = $scope.model.role.id; + } + }); + } else { + var deferred = $q.defer(); + deferred.reject(); + promise = deferred.promise; + } + + return promise; + }; + + $scope.getRoles(); + $scope.getFeatureTypes(); + }, + restrict: 'E', + scope: { + getModelId: '&modelId' + }, + templateUrl: CmsConfig.buildComponentPath('reporting/reporting-rate-overrides-edit/reporting-rate-overrides-edit.html') + }; + }); + +'use strict'; + +angular.module('rateOverrides.edit', [ + 'bulbs.cms.site.config', + 'rateOverrides.edit.directive' +]) + .config(function ($routeProvider) { + $routeProvider + .when('/cms/app/rate-overrides/edit/:id/', { + controller: function ($routeParams, $scope, $window, CmsConfig) { + $window.document.title = CmsConfig.getCmsName() + ' | Edit Rate Override'; + $scope.routeId = $routeParams.id; + }, + template: '', + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('rateOverrides.list', [ + 'apiServices.rateOverride.factory', + 'bulbs.cms.site.config', + 'listPage' + ]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/rate-overrides/', { + controller: function($scope, $window, RateOverride) { + $window.document.title = CmsConfig.getCmsName() + ' | Rate Overrides'; + $scope.modelFactory = RateOverride; + }, + + templateUrl: CmsConfig.buildComponentPath('reporting/reporting-rate-overrides-list/reporting-rate-overrides-list.html') + }); + }); + +'use strict'; + +angular.module('roles.edit.directive', [ + 'apiServices.reporting.factory', + 'bulbs.cms.site.config', + 'lodash', + 'saveButton.directive', + 'topBar' +]) + .constant('PAYMENT_TYPES', [ + { + name: 'Flat Rate', + value: 'Flat Rate' + }, + { + name: 'FeatureType', + value: 'FeatureType' + }, + { + name: 'Hourly', + value: 'Hourly' + }, + { + name: 'Manual', + value: 'Manual' + } + ]) + .directive('rolesEdit', function (CmsConfig) { + return { + controller: function (_, $location, $q, $routeParams, $scope, Role, PAYMENT_TYPES) { + + $scope.page = 'contributions'; + $scope.PAYMENT_TYPES = PAYMENT_TYPES; + + if ($routeParams.id === 'new') { + $scope.model = Role.$build(); + $scope.isNew = true; + } else { + $scope.model = Role.$find($routeParams.id).$then(function () { + $scope.model.feature_type_rates.$fetch(); + $scope.model.flat_rates.$fetch(); + $scope.model.hourly_rates.$fetch(); + }); + } + + window.onbeforeunload = function (e) { + if (!_.isEmpty($scope.model.$dirty()) || $scope.isNew || $scope.needsSave) { + return 'You have unsaved changes.'; + } + }; + + $scope.$on('$destroy', function() { + delete window.onbeforeunload; + }); + + $scope.rateEditable = function () { + var paymentTypes = PAYMENT_TYPES.slice(0, 3); + if (paymentTypes.indexOf($scope.model.paymentType >= 0)) { + return true; + } + + return false; + }; + + $scope.getDirtyRates = function () { + var dirty = []; + // // Validate if flat_rate is dirty + if ($scope.model.hasOwnProperty('flat_rate') && !_.isEmpty($scope.model.flat_rate.$dirty())) { + dirty.push($scope.model.flat_rate); + } + + // Validate if hourly_rate is dirty + if ($scope.model.hasOwnProperty('hourly_rate') && !_.isEmpty($scope.model.hourly_rate.$dirty())) { + dirty.push($scope.model.hourly_rate); + } + + // Validate if feature_type_rates are dirty + $scope.model.feature_type_rates.forEach(function (rate) { + if (!_.isEmpty(rate.$dirty())) { + dirty.push(rate); + } + }); + + return dirty; + }; + + $scope.saveDirtyRates = function () { + var dirtyRates = $scope.getDirtyRates(); + dirtyRates.forEach(function (rate) { + rate.$save(); + }); + }; + + $scope.saveModel = function () { + var promise; + $scope.saveDirtyRates(); + if ($scope.model) { + promise = $scope.model.$save().$asPromise().then(function (data) { + $location.path('/cms/app/roles/edit/' + data.id + '/'); + }); + } else { + var deferred = $q.defer(); + deferred.reject(); + promise = deferred.promise; + } + return promise; + }; + }, + restrict: 'E', + scope: { + getModelId: '&modelId' + }, + templateUrl: CmsConfig.buildComponentPath('reporting/reporting-roles-edit/reporting-roles-edit.html') + }; + }); + +'use strict'; + +angular.module('roles.edit', [ + 'bulbs.cms.site.config', + 'roles.edit.directive' +]) + .config(function ($routeProvider) { + $routeProvider + .when('/cms/app/roles/edit/:id/', { + controller: function ($routeParams, $scope, $window, CmsConfig) { + $window.document.title = CmsConfig.getCmsName() + ' | Edit Role'; + $scope.routeId = $routeParams.id; + }, + template: '', + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('roles.list', [ + 'apiServices.reporting.factory', + 'bulbs.cms.site.config', + 'listPage' + ]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/roles/', { + controller: function($scope, $window, Role) { + $window.document.title = CmsConfig.getCmsName() + ' | Roles'; + $scope.modelFactory = Role; + }, + templateUrl: CmsConfig.buildComponentPath('reporting/reporting-roles-list/reporting-roles-list.html') + }); + }); + +'use strict'; + +angular.module('reports', [ + 'lineItems.edit', + 'lineItems.list', + 'rateOverrides.edit', + 'rateOverrides.list', + 'roles.edit', + 'roles.list' +]); + +'use strict'; + +angular.module('sections.edit.directive', [ + 'apiServices.section.factory', + 'BettyCropper', + 'bulbs.cms.site.config', + 'copyButton', + 'customSearch', + 'lodash', + 'saveButton.directive', + 'sections.settings', + 'topBar' +]) + .directive('sectionsEdit', function (CmsConfig) { + return { + controller: function (_, $location, $q, $scope, SECTIONS_LIST_REL_PATH, Section) { + + $scope.LIST_URL = CmsConfig.buildExternalUrl(SECTIONS_LIST_REL_PATH); + + $scope.needsSave = false; + + var modelId = $scope.getModelId(); + if (modelId === 'new') { + // this is a new section, build it + $scope.model = Section.$build(); + $scope.isNew = true; + } else { + // this is an existing special coverage, find it + $scope.model = Section.$find($scope.getModelId()); + } + + window.onbeforeunload = function (e) { + if (!_.isEmpty($scope.model.$dirty()) || $scope.isNew || $scope.needsSave) { + // unsaved changes, show confirmation alert + return 'You have unsaved changes.'; + } + }; + + $scope.$on('$destroy', function() { + // ensure even is cleaned up when we leave + delete window.onbeforeunload; + }); + + $scope.saveModel = function () { + var promise; + + if ($scope.model) { + // have model, use save promise as deferred + promise = $scope.model.$save().$asPromise().then(function (data) { + if (modelId === 'new') { + $location.path('/cms/app/section/edit/' + data.id + '/'); + } + $scope.isNew = false; + $scope.needsSave = false; + }); + } else { + // no model, this is an error, defer and reject + var deferred = $q.defer(); + deferred.reject(); + promise = deferred.promise; + } + + return promise; + }; + }, + restrict: 'E', + scope: { + getModelId: '&modelId' + }, + templateUrl: CmsConfig.buildComponentPath('sections/sections-edit/sections-edit.html') + }; + }); + +'use strict'; + +angular.module('sections.edit', [ + 'bulbs.cms.site.config', + 'sections.edit.directive' +]) + .config(function ($routeProvider) { + $routeProvider + .when('/cms/app/section/edit/:id/', { + controller: function ($routeParams, $scope, $window, CmsConfig) { + $window.document.title = CmsConfig.getCmsName() + ' | Edit Section'; + $scope.routeId = $routeParams.id; + }, + template: '', + reloadOnSearch: false + }); + }); + +'use strict'; + +angular.module('sections.list', [ + 'apiServices.section.factory', + 'bulbs.cms.site.config', + 'listPage', + 'sections.settings' +]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/section/', { + controller: function ($scope, $window, SECTIONS_LIST_REL_PATH, Section) { + $window.document.title = CmsConfig.getCmsName() + ' | Section'; + $scope.modelFactory = Section; + $scope.LIST_URL = CmsConfig.buildExternalUrl(SECTIONS_LIST_REL_PATH); + }, + templateUrl: CmsConfig.buildComponentPath('sections/sections-list/sections-list-page.html') + }); + }); + +'use strict'; + +angular.module('sections.settings', []) + .value('SECTIONS_LIST_REL_PATH', '/section/'); + +'use strict'; + +angular.module('sections', [ + 'sections.list', + 'sections.edit' +]); + +'use strict'; + +angular.module('bulbs.cms.sendToEditorModal.api', [ + 'bulbs.cms.dateTimeFilter', + 'bulbs.cms.site.config', + 'bulbs.cms.utils', + 'lodash', +]) + .service('SendToEditorApi', [ + '_', '$http', 'CmsConfig', 'dateTimeFormatFilter', 'moment', 'Utils', + function (_, $http, CmsConfig, dateTimeFormatFilter, moment, Utils) { + + var endpoint = function (article) { + return CmsConfig.buildApiUrlRoot('content', article.id, 'send/'); + }; + + var parsePayload = function (payload) { + var data = _.cloneDeep(payload.data); + return data; + }; + + return { + sendToEditor: function (article, status, notes) { + return $http.post( + endpoint(article), { + status: status, + notes: notes + } + ).then(function (response) { + return parsePayload(response); + }); + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.sendToEditorModal', [ + 'bulbs.cms.sendToEditorModal.api', + 'bulbs.cms.site.config', + 'Raven', + 'ui.bootstrap', + 'ui.bootstrap.modal' +]) + .directive('sendToEditorModalOpener', [ + '$modal', '$q', 'CmsConfig', 'SendToEditorApi', 'Raven', + function ($modal, $q, CmsConfig, SendToEditorApi, Raven) { + return { + restrict: 'A', + scope: { + modalArticle: '=', + modalOnBeforeOpen: '&', + modalOnCancel: '&', + modalOnOk: '&' + }, + link: function (scope, element) { + + scope.statuses = CmsConfig.getArticleEditoralStatuses(); + scope.modalInstance = false; + + var setupAndOpenModal = function () { + scope.clearError = function () { + scope.errorMessage = ''; + }; + + scope.sendToEditor = function (status, note) { + scope.clearError(); + + return SendToEditorApi.sendToEditor( + scope.modalArticle, + status, + 'Status: ' + status + '\n\n' + note + ) + .then(scope.modalInstance.close) + .catch(function (response) { + Raven.captureMessage('Error attempting to send to editor', { + response: response + }); + scope.errorMessage = 'An error occurred!'; + }); + }; + + scope.modalInstance = $modal + .open({ + scope: scope, + templateUrl: CmsConfig.buildComponentPath( + 'send-to-editor-modal', + 'send-to-editor-modal.html' + ) + }); + + scope.modalInstance.result + .then(scope.modalOnOk) + .catch(scope.modalOnCancel) + .finally(function () { + scope.modalInstance = false; + }); + }; + + element.addClass('send-to-editor-modal-opener'); + element.on('click', function () { + + if (!scope.modalInstance) { + + $q.when(scope.modalOnBeforeOpen()) + .then(function (result) { + + if (result !== false) { + setupAndOpenModal(); + } + }); + } + }); + } + }; + } + ]); + +'use strict'; + +angular.module('specialCoverage.edit.directive', [ + 'apiServices.campaign.factory', + 'apiServices.specialCoverage.factory', + 'bulbs.cms.site.config', + 'campaignAutocomplete', + 'copyButton', + 'customSearch', + 'lodash', + 'specialCoverage.settings', + 'topBar', + 'ui.bootstrap.tooltip', + 'videoList', + 'bulbs.cms.superFeatures.tab', +]) + .directive('specialCoverageEdit', function (CmsConfig) { + return { + controller: function (_, $location, $q, $scope, $modal, Campaign, + SPECIAL_COVERAGE_LIST_REL_PATH, SpecialCoverage) { + + $scope.ACTIVE_STATES = SpecialCoverage.ACTIVE_STATES; + $scope.LIST_URL = CmsConfig.buildExternalUrl(SPECIAL_COVERAGE_LIST_REL_PATH); + + $scope.needsSave = false; + + $scope.tunicCampaignIdMapping = {}; + + var modelId = $scope.getModelId(); + if (modelId === 'new') { + // this is a new special coverage, build it + $scope.model = SpecialCoverage.$build(); + $scope.isNew = true; + } else { + // this is an existing special coverage, find it + $scope.model = SpecialCoverage.$find($scope.getModelId()).$then(function () { + $scope.model.$loadTunicCampaign().then(function (campaign) { + $scope.tunicCampaignIdMapping[campaign.id] = campaign; + }); + }); + } + + + window.onbeforeunload = function (e) { + if (!_.isEmpty($scope.model.$dirty()) || $scope.isNew || $scope.needsSave) { + // unsaved changes, show confirmation alert + return 'You have unsaved changes.'; + } + }; + + $scope.$on('$destroy', function() { + // ensure even is cleaned up when we leave + delete window.onbeforeunload; + }); + + $scope.saveModel = function () { + var promise; + + if ($scope.model) { + // have model, use save promise as deferred + promise = $scope.model.$save().$asPromise().then(function (data) { + if (modelId === 'new') { + $location.path('/cms/app/special-coverage/edit/' + data.id + '/'); + } + $scope.isNew = false; + $scope.needsSave = false; + }); + } else { + // no model, this is an error, defer and reject + var deferred = $q.defer(); + deferred.reject(); + promise = deferred.promise; + } + + return promise; + }; + + $scope.previewLinkModal = function () { + return $modal.open({ + templateUrl: '/views/modals/preview-link-modal.html', + scope: $scope, + resolve: {} + }); + }; + + $scope.tunicCampaignFormatter = function (campaignId) { + if (campaignId in $scope.tunicCampaignIdMapping) { + var campaign = $scope.tunicCampaignIdMapping[campaignId]; + return campaign.name + ' - ' + campaign.number; + } + }; + + $scope.searchCampaigns = function (searchTerm) { + return $scope.model.$searchCampaigns({search: searchTerm}).then(function (campaigns) { + campaigns.forEach(function (campaign) { + $scope.tunicCampaignIdMapping[campaign.id] = campaign; + }); + // Formatter expects list of IDs + return campaigns.map(function (campaign) { return campaign.id; }); + }); + }; + }, + restrict: 'E', + scope: { + getModelId: '&modelId' + }, + templateUrl: CmsConfig.buildComponentPath('special-coverage/special-coverage-edit/special-coverage-edit.html') + }; + }); + +'use strict'; + +angular.module('specialCoverage.edit', [ + 'bulbs.cms.site.config', + 'specialCoverage.edit.directive' +]) + .config(function ($routeProvider) { + $routeProvider + .when('/cms/app/special-coverage/edit/:id/', { + controller: function ($routeParams, $scope, $window, CmsConfig) { + $window.document.title = CmsConfig.getCmsName() + ' | Edit Special Coverage'; + $scope.routeId = $routeParams.id; + }, + template: '' + }); + }); + +'use strict'; + +angular.module('specialCoverage.list', [ + 'apiServices.specialCoverage.factory', + 'bulbs.cms.site.config', + 'listPage', + 'moment', + 'specialCoverage.settings' +]) + .config(function ($injector, $routeProvider, CmsConfigProvider) { + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + + $routeProvider + .when('/cms/app/special-coverage/', { + controller: function ($scope, $window, CmsConfig, + SPECIAL_COVERAGE_LIST_REL_PATH, SpecialCoverage) { + $window.document.title = CmsConfig.getCmsName() + ' | Special Coverage'; + $scope.modelFactory = SpecialCoverage; + $scope.LIST_URL = CmsConfig.buildExternalUrl(SPECIAL_COVERAGE_LIST_REL_PATH); + }, + templateUrl: CmsConfig.buildComponentPath('special-coverage/special-coverage-list/special-coverage-list-page.html') + }); + }); + +'use strict'; + +angular.module('specialCoverage.settings', []) + .value('SPECIAL_COVERAGE_LIST_REL_PATH', '/special/'); + +'use strict'; + +angular.module('specialCoverage', [ + 'specialCoverage.list', + 'specialCoverage.edit' +]); + +'use strict'; + +angular.module('bulbs.cms.staticImage', [ + 'bulbs.cms.site.config' +]) + .directive('staticImage', [ + 'CmsConfig', + function (CmsConfig) { + return { + templateUrl: CmsConfig.buildComponentPath( + 'static-image', + 'static-image.html' + ), + restrict: 'E', + scope: { + image: '=', + ratio: '@' + }, + link: function (scope, element, attrs) { + + scope.$watch('image', function () { + if (scope.image && scope.image.id) { + scope.imageUrl = CmsConfig.buildImageApiUrl( + scope.image.id, + scope.ratio || '16x9', + '1200.jpg' + ); + } else { + scope.imageUrl = false; + } + }); + } + }; + } + ]); + +'use strict'; + +angular.module('statusFilter.config', [ + 'bulbs.cms.site.config', + 'contentServices.listService', + 'moment' +]) + .provider('StatusFilterOptions', function (moment) { + var _statuses = [ + {label: 'Draft', key: 'status', value: 'Draft'}, + {label: 'Awaiting Review', key: 'status', value: 'Waiting for Editor'}, + {label: 'Published', key: 'before', value: function () { return moment().format('YYYY-MM-DDTHH:mmZ'); }}, + {label: 'Scheduled', key: 'after', value: function () { return moment().format('YYYY-MM-DDTHH:mmZ'); }}, + {label: 'All', key: null, value: null} + ]; + + this.setStatuses = function (statuses) { + _statuses = statuses; + }; + + this.$get = function () { + return { + getStatuses: function () { + return _statuses; + } + }; + }; + }); + +'use strict'; + +angular.module('statusFilter.directive', [ + 'bulbs.cms.site.config', + 'contentServices.listService', + 'statusFilter.config' +]) + .directive('statusFilter', function ($location, _, StatusFilterOptions, ContentListService, CmsConfig) { + return { + templateUrl: CmsConfig.buildComponentPath('status-filter/status-filter.html'), + restrict: 'E', + scope: {}, + controller: 'ContentlistCtrl', + link: function postLink(scope, element, attrs) { + scope.options = StatusFilterOptions.getStatuses(); + + /** + * Test if a particular option is currently active by comparing it to + * $location.search(). + * + * @param {object} option - option parameters to test for. + * @returns true if option is in $location.search, false otherwise. + */ + scope.isActive = function (option) { + if (option.key && option.key in $location.search() && + $location.search()[option.key] === getValue(option)) { + return true; + } else if (!option.key) { //all + var possibleKeys = _.pluck(scope.options, 'key'); + var searchKeys = _.keys($location.search()); + if (_.intersection(possibleKeys, searchKeys).length > 0) { + return false; + } else { + return true; + } + } + return false; + }; + + scope.filterByStatus = function (option) { + var search = {}; + var value; + if (option.key) { + value = getValue(option); + search[option.key] = value; + } + + return ContentListService.$updateContent(search, false); + }; + + function getValue(option) { + var value; + if (typeof option.value === 'function') { + value = option.value(); + } else { + value = option.value; + } + return value; + } + + } + }; + }); + +'use strict'; + +angular.module('statusFilter', [ + 'statusFilter.directive' +]); + +'use strict'; + +angular.module('bulbs.cms.superFeatures.api', [ + 'bulbs.cms.dateTimeFilter', + 'bulbs.cms.site.config', + 'bulbs.cms.utils', + 'lodash', + 'moment' +]) + .service('SuperFeaturesApi', [ + '_', '$http', '$sce', 'CmsConfig', 'dateTimeFormatFilter', 'moment', 'Utils', + function (_, $http, $sce, CmsConfig, dateTimeFormatFilter, moment, Utils) { + + var superFeatureEndpoint = CmsConfig.buildApiUrlRoot.bind(null, 'super-feature'); + var contentEndpoint = CmsConfig.buildApiUrlRoot.bind(null, 'content'); + + var parsePayload = function (payload) { + var data = _.cloneDeep(payload); + + if (payload.published) { + data.published = moment.tz(payload.published, CmsConfig.getTimezoneName()); + } + + return data; + }; + + var cleanData = function (data) { + var payload = _.chain(data) + .omit('published') + .cloneDeep() + .value(); + + if (data.published) { + payload.published = data.published.format(); + } + + return payload; + }; + + return { + createSuperFeature: function (data) { + var payload = cleanData(data); + return $http.post( + contentEndpoint(Utils.param({ + doctype: CmsConfig.getSuperFeaturesType() + })), + payload) + .then(function (response) { + return parsePayload(response.data); + }); + }, + deleteSuperFeature: function (data) { + return $http.delete(contentEndpoint(data.id, '/')); + }, + fields: [{ + title: 'Super Feature Name', + sorts: 'title', + content: function (superFeature) { + return $sce.trustAsHtml(superFeature.title); + } + }, { + title: 'Total Nested Pages', + content: 'children_count' + }, { + title: 'Publish Date', + content: function (superFeature) { + var now = moment(); + var cellContent = ''; + + if (!superFeature.published) { + cellContent = 'Draft'; + } else if (now.isSameOrAfter(superFeature.published)) { + cellContent = dateTimeFormatFilter(superFeature.published); + } else if (now.isBefore(superFeature.published)) { + cellContent = dateTimeFormatFilter( + superFeature.published, + '[Scheduled for] M/D/YY h:mma z' + ); + } + + return cellContent; + } + }], + getSuperFeature: function (id) { + return $http.get(superFeatureEndpoint(id, '/')).then(function (response) { + return parsePayload(response.data); + }); + }, + getSuperFeatures: function (params) { + return $http.get(superFeatureEndpoint(Utils.param(params))) + .then(function (response) { + return { + results: response.data.results.map(function (result) { + return parsePayload(result); + }) + }; + }); + }, + getSuperFeatureRelations: function (id) { + return $http.get(superFeatureEndpoint(id, 'relations/')) + .then(function (response) { + return { + results: response.data.map(function (result) { + return parsePayload(result); + }) + }; + }); + }, + name: 'Super Feature', + namePlural: 'Super Features', + updateSuperFeature: function (data) { + var payload = cleanData(data); + return $http.put(contentEndpoint(data.id, '/'), payload) + .then(function (response) { + return parsePayload(response.data); + }); + }, + updateSuperFeatureRelationsOrdering: function (id, relations) { + var remappedRelations = relations.map(function (relation) { + return _.pick(relation, 'id', 'ordering'); + }); + return $http.put( + superFeatureEndpoint(id, 'relations', 'ordering/'), + remappedRelations + ); + }, + updateAllRelationPublishDates: function (id) { + return $http.put(superFeatureEndpoint(id, 'set-children-dates/')); + } + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.superFeatures.edit', [ + 'bulbs.cms.breadcrumb', + 'bulbs.cms.contentSearch', + 'bulbs.cms.dynamicContent', + 'bulbs.cms.site.config', + 'bulbs.cms.superFeatures.api', + 'bulbs.cms.superFeatures.relations' +]) + .directive('superFeaturesEdit', [ + 'CmsConfig', 'ContentFactory', 'SuperFeaturesApi', 'Utils', + function (CmsConfig, ContentFactory, SuperFeaturesApi, Utils) { + return { + link: function (scope) { + + scope.breadcrumbs = [{ + label: 'Super Features', + href: '/cms/app/super-features' + }, { + label: function () { + return scope.article.title; + } + }]; + + var addParentToBreadcrumb = function (article) { + if (angular.isNumber(article.parent)) { + SuperFeaturesApi.getSuperFeature(article.parent) + .then(function (superFeature) { + scope.breadcrumbs.splice(1, 0, { + label: superFeature.title, + href: '/cms/app/edit/' + superFeature.id + '/' + CmsConfig.getSuperFeaturesType() + }); + + addParentToBreadcrumb(superFeature); + }); + } + }; + + addParentToBreadcrumb(scope.article); + + scope.maxRecircItems = 3; + scope.fullRecircContents = []; + + var retrieveContent = function (contentId) { + return ContentFactory.one('content', contentId).get(); + }; + + scope.includeRecirc = function (contentId) { + var recirc = scope.article.recirc_query; + + if (angular.isUndefined(recirc.included_ids)) { + recirc.included_ids = []; + } + + var newRecircIdsLength = recirc.included_ids.push(contentId); + retrieveContent(contentId).then(function (content) { + scope.fullRecircContents[newRecircIdsLength - 1] = content; + }); + }; + + scope.removeRecirc = function (index) { + Utils.removeFrom(scope.article.recirc_query.included_ids, index); + Utils.removeFrom(scope.fullRecircContents, index); + }; + + if (scope.article.recirc_query.included_ids) { + scope.article.recirc_query.included_ids.forEach(function (contentId, i) { + retrieveContent(contentId).then(function (content) { + scope.fullRecircContents[i] = content; + }); + }); + } + }, + // no scope here so we have access to the content edit scope without + // having to make changes to the brittle content edit controller, + // maybe we remove this wrapper someday when we refactor content edit + scope: false, + restrict: 'E', + templateUrl: CmsConfig.buildComponentPath( + 'super-features', + 'super-features-edit', + 'super-features-edit.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.superFeatures.list', [ + 'bulbs.cms.site.config', + 'bulbs.cms.superFeatures.api', + 'bulbsCmsApp.nonRestmodListPage', + 'lodash', + 'moment', + 'ngRoute', + 'statusFilter.config' +]) + .config([ + '_', '$injector', '$routeProvider', 'CmsConfigProvider', + 'StatusFilterOptionsProvider', + function (_, $injector, $routeProvider, CmsConfigProvider, + StatusFilterOptionsProvider) { + + var CmsConfig = $injector.invoke(CmsConfigProvider.$get); + var StatusFilterOptions = $injector.invoke(StatusFilterOptionsProvider.$get); + + $routeProvider + .when('/cms/app/super-features/', { + controller: [ + '$scope', '$window', 'SuperFeaturesApi', + function ($scope, $window, SuperFeaturesApi) { + $window.document.title = CmsConfig.getCmsName() + ' | Super Feature'; + $scope.modelFactory = SuperFeaturesApi; + + $scope.editPageUrlBuilder = function (item) { + return '/cms/app/edit/' + item.id + '/' + (item.polymorphic_ctype || CmsConfig.getSuperFeaturesType()); + }; + + // TODO: using status filters from content page here since list + // page needs them in a different format. when we move content + // list page to the generic list page directive, this can be + // removed. + $scope.statusFilterOptions = StatusFilterOptions.getStatuses() + .filter(function (option) { + return option.label !== 'All'; + }) + .map(function (option) { + var params = {}; + + if (option.key) { + params[option.key] = _.isFunction(option.value) ? option.value() : option.value; + } + + return { + title: option.label, + params: params + }; + }); + } + ], + templateUrl: CmsConfig.buildComponentPath( + 'super-features', + 'super-features-list', + 'super-features-list.html' + ) + }); + } + ]); + +'use strict'; + +angular.module('bulbs.cms.superFeatures.relations', [ + 'bettyEditable', + 'bulbs.cms.dateTimeFilter', + 'bulbs.cms.site.config', + 'bulbs.cms.superFeatures.api', + 'bulbs.cms.sendToEditorModal', + 'bulbs.cms.titleModal', + 'bulbs.cms.utils', + 'confirmationModal', + 'lodash', + 'moment', + 'Raven', + 'statusFilter.config' +]) + .directive('superFeaturesRelations', [ + '_', '$q', 'CmsConfig', 'moment', 'Raven', 'SuperFeaturesApi', + 'StatusFilterOptions', 'Utils', + function (_, $q, CmsConfig, moment, Raven, SuperFeaturesApi, + StatusFilterOptions, Utils) { + + return { + link: function (scope, element, attrs) { + var reportError = function (message, data) { + Raven.captureMessage(message, data); + scope.errorMessage = message; + }; + + scope.clearError = function () { + scope.errorMessage = ''; + }; + + scope.$watch('relations', function (newRelations, oldRelations) { + if (!angular.equals(newRelations, oldRelations)) { + scope.clearError(); + } + }, true); + + var normalizeOrderings = function (relations) { + relations.forEach(function (relation, i) { + relation.ordering = i + 1; + }); + }; + + SuperFeaturesApi.getSuperFeatureRelations(scope.article.id) + .then(function (response) { + scope.relations = response.results; + normalizeOrderings(scope.relations); + }) + .catch(function (response) { + var message = 'An error occurred retrieving relations!'; + reportError(message, { response: response }); + }); + + scope.statuses = StatusFilterOptions.getStatuses() + .filter(function (status) { + // remove default status + return !!status.value; + }); + + var relationFormPrefix = 'relationForm_'; + var orderingFormPrefix = 'orderingInputForm_'; + + scope.wrapperForm = {}; + scope.makeRelationFormName = function (relation) { + return relationFormPrefix + relation.id; + }; + scope.makeOrderingFormName = function (relation) { + return orderingFormPrefix + relation.id; + }; + scope.getRelationForm = function (relation) { + return scope.wrapperForm[scope.makeRelationFormName(relation)]; + }; + scope.getOrderingForm = function (relation) { + return scope.wrapperForm[scope.makeOrderingFormName(relation)]; + }; + + scope.isAtLeastOneRelationFormDirty = function () { + return Object.keys(scope.wrapperForm) + .reduce(function (isDirty, key) { + if (key.startsWith(relationFormPrefix)) { + return isDirty || scope.wrapperForm[key].$dirty; + } + return isDirty; + }, false); + }; + + var lock = Utils.buildLock(); + scope.transactionsLocked = lock.isLocked; + var reorder = function (operation) { + + return function () { + var funcArgs = arguments; + + var payload = scope.relations.map(function (relation) { + return _.pick(relation, 'id', 'ordering'); + }); + + operation.bind(null, payload).apply(null, funcArgs); + normalizeOrderings(payload); + + return SuperFeaturesApi.updateSuperFeatureRelationsOrdering( + scope.article.id, + payload + ) + .then(function () { + operation.bind(null, scope.relations).apply(null, funcArgs); + normalizeOrderings(scope.relations); + }) + .catch(function (response) { + var message = 'An error occurred attempting to reorder a child!'; + reportError(message, { response: response }); + }); + }; + }; + + scope.moveRelation = lock(reorder(Utils.moveTo)); + + scope.addRelation = lock(function (title) { + + return SuperFeaturesApi.createSuperFeature({ + parent: scope.article.id, + superfeature_type: scope.article.default_child_type, + title: title, + ordering: (_.max(scope.relations, 'ordering').ordering || 0) + 1 + }) + .then(function (relation) { + scope.relations.push(relation); + }) + .catch(function (response) { + var message = 'An error occurred attempting to add a child page!'; + reportError(message, { response: response }); + }); + }); + + scope.updateRelationsPublishDates = lock(function () { + + return SuperFeaturesApi + .updateAllRelationPublishDates(scope.article.id) + .then(function (response) { + scope.relations.forEach(function (relation) { + relation.published = moment.tz( + scope.article.published, + CmsConfig.getTimezoneName() + ); + }); + }) + .catch(function (response) { + var message = 'An error occurred attempting to update child publish dates!'; + reportError(message, { response: response }); + }); + }); + + scope.saveRelation = lock(function (relation) { + + var relationCopy = angular.copy(relation); + relationCopy.parent = scope.article.id; + + return SuperFeaturesApi.updateSuperFeature(relationCopy) + .catch(function (response) { + var titleDisplay = relation.title ? '"' + relation.title + '"' : 'a relation'; + var message = 'An error occurred attempting to update ' + titleDisplay + '!'; + reportError(message, { response: response }); + }) + .finally(function () { + scope.getRelationForm(relation).$setPristine(); + }); + }); + + scope.deleteRelation = lock(function (relation) { + + return SuperFeaturesApi.deleteSuperFeature(relation) + .then(function () { + var index = scope.relations.indexOf(relation); + reorder(Utils.removeFrom)(index); + }) + .catch(function (response) { + var titleDisplay = relation.title ? '"' + relation.title + '"' : 'a relation'; + var message = 'An error occurred attempting to delete ' + titleDisplay + '!'; + reportError(message, { response: response }); + }); + }); + }, + restrict: 'E', + scope: { + article: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'super-features', + 'super-features-relations', + 'super-features-relations.html' + ) + }; + } + ]); + +'use strict'; + +angular.module('bulbs.cms.superFeatures.tab.item', [ + 'bulbs.cms.site.config', + 'bulbs.cms.superFeatures.api', + 'filters.moment' +]) + .directive('superFeaturesTabItem', function (CmsConfig, SuperFeaturesApi) { + return { + link: function (scope) { + + var requestSuperFeature = function () { + + if (angular.isNumber(scope.model)) { + SuperFeaturesApi.getSuperFeature(scope.model) + .then(function (superFeature) { + scope.superFeature = superFeature; + }); + } + }; + + scope.$watch('model', requestSuperFeature); + }, + restrict: 'E', + scope: { + model: '=' + }, + templateUrl: CmsConfig.buildComponentPath( + 'super-features', + 'super-features-tab', + 'super-features-tab-item', + 'super-features-tab-item.html' + ) + }; + }); + +'use strict'; + +angular.module('bulbs.cms.superFeatures.tab', [ + 'autocompleteBasic', + 'bulbs.cms.site.config', + 'bulbs.cms.utils', + 'jquery', + 'ui.sortable', + 'bulbs.cms.superFeatures.tab.item', + 'bulbs.cms.superFeatures.api' +]) + .directive('superFeaturesTab', function ($, SuperFeaturesApi, CmsConfig) { + return { + controller: function (_, $scope, Utils, Video) { + + $scope.moveUp = function (index) { + Utils.moveTo($scope.superFeatures, index, index - 1); + $scope.onUpdate(); + }; + + $scope.moveDown = function (index) { + Utils.moveTo($scope.superFeatures, index, index + 1); + $scope.onUpdate(); + }; + + $scope.delete = function (index) { + Utils.removeFrom($scope.superFeatures, index); + $scope.onUpdate(); + }; + + $scope.addSuperFeature = function (superFeature) { + $scope.addSuperFeatureCallback({ superFeature: superFeature }); + $scope.onUpdate(); + }; + + $scope.searchSuperFeature = function (query) { + return SuperFeaturesApi.getSuperFeatures({search:query}).then( + function(response) { + return response.results; + }); + }; + + }, + link: function (scope, element, attr) { + + scope.sortableOptions = { + beforeStop: function (e, ui) { + ui.helper.css('margin-top', 0); + }, + change: function (e, ui) { + ui.helper.css('margin-top', $(window).scrollTop()); + }, + containment: 'super-feature-list', + distance: 3, + opacity: 0.75, + placeholder: 'dropzone', + start: function (e, ui) { + ui.helper.css('margin-top', $(window).scrollTop()); + } + }; + }, + restrict: 'E', + scope: { + addSuperFeatureCallback: '&addSuperFeature', + superFeatures: '=', + onUpdate: '&' + }, + templateUrl: CmsConfig.buildComponentPath( + 'super-features', + 'super-features-tab', + 'super-features-tab.html' + ) + }; + }); + +'use strict'; + +angular.module('bulbs.cms.superFeatures', [ + 'bulbs.cms.superFeatures.list', + 'bulbs.cms.superFeatures.edit' +]); + +'use strict'; + +angular.module('templateTypeField.directive', [ + 'bulbs.cms.site.config' +]) + .directive('templateTypeField', function (CmsConfig) { + return { + controller: function (_, $scope, ContentFactory, TEMPLATE_TYPES) { + $scope.templateTypes = _.filter(TEMPLATE_TYPES, {content_type: $scope.content.polymorphic_ctype}); + }, + restrict: 'E', + scope: { + content: '=' + }, + templateUrl: CmsConfig.buildComponentPath('template-type-field/template-type-field.html') + }; + }); + +'use strict'; + +angular.module('templateTypeField', [ + 'templateTypeField.directive' +]) + .value('TEMPLATE_TYPES', [{ + name: 'Small Width', + slug: 'small-width', + content_type: 'content_content' + }, { + name: 'Large Width', + slug: 'large-width', + content_type: 'content_content' + }]); + +'use strict'; + +angular.module('bulbs.cms.titleModal', [ + 'bulbs.cms.site.config', + 'ui.bootstrap', + 'ui.bootstrap.modal' +]) + .directive('titleModalOpener', [ + '$modal', 'CmsConfig', + function ($modal, CmsConfig) { + return { + restrict: 'A', + scope: { + modalBodyBefore: '@', + modalBodyAfter: '@', + modalCancelText: '@', + modalOkText: '@', + modalOnCancel: '&', + modalOnOk: '&', + modalTitle: '@' + }, + link: function (scope, element) { + + element.addClass('title-modal-opener'); + element.on('click', function () { + + if (!scope.modalInstance) { + + scope.modalInstance = $modal + .open({ + scope: scope, + templateUrl: CmsConfig.buildComponentPath( + 'title-modal', + 'title-modal.html' + ) + }); + + scope.modalInstance.result + .then(scope.modalOnOk) + .catch(scope.modalOnCancel) + .finally(function () { + scope.modalInstance = false; + }); + } + }); + } + }; + } + ]); + +'use strict'; + +/** + * Renders a topbar template based on a given path relative to "/components/". + */ +angular.module('topBar.directive', [ + 'bulbs.cms.site.config' +]) + .directive('topBar', function (CmsConfig) { + return { + restrict: 'E', + scope: { + logoHref: '@', + itemsDropdownTitle: '@', + itemsDropdown: '=', + itemsTop: '=', + saveFunction: '=', + saveDisableWhen: '&' + }, + templateUrl: CmsConfig.buildComponentPath('top-bar/top-bar-base.html'), + link: function (scope) { + scope.NAV_LOGO = CmsConfig.getNavLogoPath(); + } + }; + }); + +'use strict'; + +angular.module('topBar.item.factory', []) + .factory('TopBarItem', function () { + + var TopBarItem = function (params) { + this.displayText = params.displayText || ''; + this.displayIconClasses = params.displayIconClasses || ''; + this.containerClasses = params.containerClasses || ''; + this.clickFunction = params.clickFunction || function () {}; + }; + + return TopBarItem; + }); + +'use strict'; + +angular.module('topBar', [ + 'topBar.directive', + 'topBar.item.factory' +]); + +'use strict'; + +/** + * Restmod mixin that looks for fieldDisplays in $configs, objects with a title and + * optionally a value and sorts property. + * + * title is used to label a given field, should be unique. + * + * value is an optional field transformed into a new property function called + * evalute; when invoked with a record, value string will evaluate with $parse, + * where 'record' is the given record. + * + * sorts is an optional field that can be a string or a function. As a string it + * should be the name of a property to order by. As a function, it should take + * a direction--'asc'/undefined for the default direction, 'desc' for the opposite + * direction--and return an ordering string. + * + * Field display objects are available at the model level as the $fieldDisplays function. + * Returns a list of field displays to be used in templates. + */ +angular.module('apiServices.mixins.fieldDisplay', [ + 'restmod' +]) + .factory('FieldDisplay', function($parse, restmod) { + + /** + * Generates a function that can be passed a record and evalutes given value + * string agaist that record to return the string to be displayed to the user. + * + * @param {string} value - string that will be evaluted with record as an instance + * of model, e.g. the value string 'record.name' would print out the instance's + * name property. + * @returns {function} takes a record and is evaluates the given value with given + * record. + */ + var parserWrap = function (value) { + // return a function that can be called with given string to generate parser + return (function (value) { + // return a function that will be called in template + var parsed = $parse(value); + return function (record) { + // use angular's $parse to create a function that will eval in the correct scope + return parsed({record: record}); + }; + })(value); + }; + + /** + * Default sorting string builder. If field display object sorts property is a + * function, that will override the functionality provided by this function. Use + * this for more complex sorting strings, such as those that have multiple paramters. + * + * @param {String} sorts - sorts property provided by field display object. Should be + * the non-negated property name to sort on. + * @returns {function} evaluated with a direction string, either 'asc'/undefined for the + * default sorting direction, or 'desc' for opposite sorting direction. + */ + var getOrdering = function (sorts) { + return (function (sorts) { + return function (direction) { + var ordering = ''; + if (direction === 'desc') { + // do opposite of default sort + ordering = '-' + sorts; + } else { + // do default sort, only supports 1 parameter + ordering = sorts; + } + return ordering; + }; + })(sorts); + }; + + return restmod.mixin(function () { + + this.define('Scope.$fieldDisplays', function () { + var fieldDisplays = this.getProperty('fieldDisplays'); + if (fieldDisplays) { + var i; + for (i = 0; i < fieldDisplays.length; i++) { + var fieldDisplay = fieldDisplays[i]; + + // set up evaluation function if a value was provided + if (fieldDisplay.value) { + fieldDisplay.evaluate = parserWrap(fieldDisplay.value); + } + + // set up storting function if sorts was provided + if (fieldDisplay.sorts) { + if (typeof fieldDisplay.sorts === 'function') { + // sort function was provided, use that + fieldDisplay.getOrdering = fieldDisplay.sorts; + } else { + // function not provided, use default one + fieldDisplay.getOrdering = getOrdering(fieldDisplay.sorts); + } + } + } + } + return fieldDisplays; + }); + + }); + }); + +'use strict'; + +angular.module('apiServices', [ + 'restmod', + 'restmod.styles.drfPaged' +]) + .constant('API_URL_ROOT', '/cms/api/v1/') + .config(function (API_URL_ROOT, restmodProvider) { + restmodProvider.rebase('DjangoDRFPagedApi', { + $config: { + style: 'BulbsApi', + urlPrefix: API_URL_ROOT + } + }); + }); + +'use strict'; + +angular.module('apiServices.campaign.factory', [ + 'apiServices', + 'apiServices.mixins.fieldDisplay', + 'filters.moment' +]) + .factory('Campaign', function (restmod) { + return restmod.model('campaign').mix('FieldDisplay', 'NestedDirtyModel', { + $config: { + name: 'Campaign', + plural: 'Campaigns', + primaryKey: 'id', + fieldDisplays: [{ + title: 'Campaign', + value: 'record.campaignLabel', + sorts: 'campaign_label' + }, { + title: 'Sponsor', + value: 'record.sponsorName', + sorts: 'sponsor_name' + }, { + title: 'Start Date', + value: 'record.startDate.format("MM/DD/YY") || "--"', + sorts: 'start_date' + }, { + title: 'End Date', + value: 'record.endDate.format("MM/DD/YY") || "--"', + sorts: 'end_date' + }] + }, + + pixels: { + init: [{}], + }, + + // fields from frontend to backend + end_date: { + encode: 'moment_to_date_string', + }, + start_date: { + encode: 'moment_to_date_string', + }, + + // fields from backend to frontend + endDate: { + decode: 'date_string_to_moment', + }, + startDate: { + decode: 'date_string_to_moment' + }, + + $extend: { + Model: { + simpleSearch: function (searchTerm) { + return this.$search({search: searchTerm, ordering: 'campaign_label'}).$asPromise(); + } + } + } + }); + }); + +'use strict'; + +angular.module('apiServices.customSearch.count.factory', [ + 'apiServices', + 'apiServices.customSearch.settings' +]) + .factory('CustomSearchCount', function (_, CustomSearchSettings, restmod) { + + var Count = restmod.model(CustomSearchSettings.countEndpoint).mix({ + count: 0 + }); + + return { + $retrieveResultCount: function (query) { + return Count.$create(query).$asPromise() + .then(function (model) { + return model.$response.data.root.count; + }); + } + }; + }); + +'use strict'; + +/** + * Wrapper functions for custom search endpoints. + */ +angular.module('apiServices.customSearch.factory', [ + 'apiServices.customSearch.count.factory', + 'apiServices.customSearch.groupCount.factory', + 'apiServices.customSearch.settings', + 'lodash', + 'restmod' +]) + .factory('CustomSearch', function (_, restmod, CustomSearchCount, CustomSearchGroupCount, + CustomSearchSettings) { + + var CustomSearch = restmod.model(CustomSearchSettings.searchEndpoint).mix({ + $config: { + jsonRootSingle: 'results' + }, + $hooks: { + 'before-save': function (_req) { + _req.url += '/?page=' + _req.data.page; + } + } + }); + + return { + $retrieveResultCount: CustomSearchCount.$retrieveResultCount, + $retrieveGroupCount: CustomSearchGroupCount.$retrieveResultCount, + $retrieveContent: function (query) { + return CustomSearch.$create(query).$asPromise() + .then(function (model) { + return model.$response.data; + }); + } + }; + }); + +'use strict'; + +angular.module('apiServices.customSearch.groupCount.factory', [ + 'apiServices', + 'apiServices.customSearch.settings' +]) + .factory('CustomSearchGroupCount', function (_, CustomSearchSettings, restmod) { + + var Count = restmod.model(CustomSearchSettings.groupCountEndpoint).mix({ + count: 0 + }); + + return { + $retrieveResultCount: function (query) { + return Count.$create(query).$asPromise() + .then(function (model) { + return model.$response.data.root.count; + }); + } + }; + }); + +'use strict'; + +angular.module('apiServices.customSearch.settings', []) + .service('CustomSearchSettings', function () { + + return { + searchEndpoint: 'custom-search-content', + groupCountEndpoint: 'custom-search-content/group_count', + countEndpoint: 'custom-search-content/count' + }; + }); + +'use strict'; + +angular.module('apiServices.featureType.factory', [ + 'apiServices' +]) + .factory('FeatureType', [ + 'restmod', + function (restmod) { + return restmod.model('things').mix('NestedDirtyModel', { + $config: { + name: 'FeatureType', + plural: 'FeatureTypes', + primaryKey: 'id', + }, + + $extend: { + Model: { + simpleSearch: function (searchTerm) { + return this.$search({type: 'feature_type', q: searchTerm}).$asPromise(); + } + } + } + }); + }] + ); +'use strict'; + +angular.module('apiServices.notification.factory', [ + 'apiServices', + 'apiServices.customSearch.count.factory', + 'apiServices.mixins.fieldDisplay', + 'filters.moment', +]) + .factory('Notification', function (_, CustomSearchCount, restmod) { + var notificationEndpoint = 'notification'; + + return restmod.model(notificationEndpoint).mix('FieldDisplay', 'NestedDirtyModel', { + $config: { + name: 'Notification', + plural: 'Notifications', + primaryKey: 'id', + fieldDisplays: [{ + title: 'Internal Name', + value: 'record.internalTitle || "--"', + sorts: 'internal_title' + }, { + title: 'State', + value: 'record.isPublished ? "Published" : "Unpublished"', + sorts: 'is_published' + }, { + title: 'Created On', + value: 'record.createdOn.format("MM/DD/YYYY")', + sorts: 'created_on' + }] + }, + + // fields from frontend to backend + created_on: { + encode: 'moment_to_date_string', + }, + + // fields from backend to frontend + createdOn: { + decode: 'date_string_to_moment' + } + + }); + }); + +'use strict'; + +angular.module('apiServices.answer.factory', [ + 'apiServices', + 'bulbs.cms.config', + 'lodash' +]) +.factory('Answer', ['$http', '$q', '_', 'CmsConfig', function ($http, $q, _, CmsConfig) { + + var answerUrl = CmsConfig.buildApiUrlRoot('poll-answer/'); + var error = function(message) { + return new Error('Poll Error: ' + message); + }; + + function deleteAnswers(deletedAnswers) { + var deletePromise = _.map(deletedAnswers, function(deletedAnswer) { + return $http.delete(answerUrl + deletedAnswer.id); + }); + $q.all(deletePromise).then(function(response) { + return response; + }); + } + + function putAnswer(oldAnswers, newAnswer) { + var oldAnswer = _.filter(oldAnswers, {id: newAnswer.id})[0]; + if(newAnswer.answer_text !== oldAnswer.answer_text) { + return $http.put(answerUrl + newAnswer.id, { + answer_text: newAnswer.answer_text + }).then(function(response) { + return response.data; + }); + } + } + + function postAnswer(answer, pollId) { + if(!_.isNumber(pollId) || _.isUndefined(answer.answer_text)) { + throw error('poll id and answer_text fields required'); + } + return $http.post(answerUrl, { + poll: pollId, + answer_text: answer.answer_text + }).then(function(response) { + return response.data; + }); + } + + function updatePollAnswers(scope) { + deleteAnswers(scope.deletedAnswers); + _.forEach(scope.answers, function(answer) { + if(answer.notOnSodahead) { + postAnswer(answer, scope.model.id); + } else { + putAnswer(scope.model.answers, answer); + } + }); + } + + return { + postAnswer: postAnswer, + updatePollAnswers: updatePollAnswers + }; +}]); + +'use strict'; + +angular.module('apiServices.poll.factory', [ + 'apiServices', + 'apiServices.mixins.fieldDisplay', + 'bulbsCmsApp.nonRestmodListPage', + 'filters.moment', + 'lodash' +]) +.factory('Poll', + ['$filter', '$http', '$q', '_', 'moment', 'Utils', + function ($filter, $http, $q, _, moment, Utils) { + + var error = function(message) { + return new Error('Poll Error: ' + message); + }; + + var fields = [{ + title: 'Poll Name', + sorts: 'title', + }, { + title: 'Publish Date', + sorts: 'publish_date', + content: function (poll) { + return poll.published ? poll.published.format('MM/DD/YY h:mma') : '—'; + }, + }, { + title: 'Close Date', + sorts: 'end_date', + content: function (poll) { + return poll.end_date ? poll.end_date.format('MM/DD/YY h:mma') : '—'; + }, + }]; + var name = 'Poll'; + var namePlural = 'Polls'; + var pollUrl = '/cms/api/v1/poll/'; + + function parsePayload (payload) { + var data = _.clone(payload); + var filter = $filter('date_string_to_moment'); + data.end_date = filter(data.end_date); + data.published = filter(data.published); + return data; + } + + function cleanPayload (originalPayload) { + var momentToDateString = $filter('moment_to_date_string'); + var payload = _.clone(originalPayload); + + if(_.isUndefined(payload.title) && _.isUndefined(payload.question_text)) { + throw error('title and question text required'); + } + + if(payload.end_date) { + if(!moment.isMoment(payload.end_date)) { + throw error('end_date must be a moment object'); + } + payload.end_date = momentToDateString(payload.end_date); + } + + if (payload.published) { + if(!moment.isMoment(payload.published)) { + throw error('published must be a moment object'); + } + payload.published = momentToDateString(payload.published); + } + + return _.pick(payload, [ + 'title', + 'question_text', + 'published', + 'end_date' + ]); + } + + function getPoll(pollId) { + return $http.get(pollUrl + pollId + '/') + .then(function (response) { + return parsePayload(response.data); + }); + } + + function getPolls(params) { + var url = pollUrl + Utils.param(params); + return $http.get(url) + .then(function (response) { + response.data.results = response.data.results.map(function (poll) { + return parsePayload(poll); + }); + return response.data; + }); + } + + function postPoll(data) { + var payload = cleanPayload(data); + + return $http.post(pollUrl, payload) + .then(function(response) { + return response.data; + }); + } + + function updatePoll(data) { + var payload = cleanPayload(data); + + return $http.put(pollUrl + data.id + '/', payload) + .then(function(response) { + return response.data; + }); + } + + function deletePoll(pollId) { + return $http.delete(pollUrl + pollId + '/') + .then(function(response) { + return response; + }); + } + + return { + getPoll: getPoll, + getPolls: getPolls, + fields: fields, + name: name, + namePlural: namePlural, + postPoll: postPoll, + updatePoll: updatePoll, + deletePoll: deletePoll + }; +}]); + +'use strict'; + +angular.module('apiServices.lineItem.factory', [ + 'apiServices', + 'apiServices.mixins.fieldDisplay' +]) + .factory('LineItem', function (_, restmod) { + var contributorEndpoint = 'contributions/line-items'; + + return restmod.model(contributorEndpoint).mix('FieldDisplay', 'NestedDirtyModel', { + $config: { + name: 'Line Item', + plural: 'Line Items', + primaryKey: 'id', + fieldDisplays: [ + { + title: 'Contributor', + value: 'record.contributor.fullName' + }, + { + title: 'Amount $', + value: 'record.amount' + }, + { + title: 'Note', + value: 'record.note' + }, + { + title: 'Date', + value: 'record.paymentDate.format("MM/DD/YY") || "--"', + } + ] + }, + + paymentDate: { + decode: 'date_string_to_moment', + encode: 'moment_to_date_string' + }, + payment_date: { + decode: 'date_string_to_moment', + encode: 'moment_to_date_string', + } + + }); + }); +'use strict'; + +angular.module('apiServices.rateOverride.factory', [ + 'apiServices', + 'apiServices.mixins.fieldDisplay' +]) + .factory('RateOverride', function (_, restmod) { + var rateOverrideEndpoint = 'contributions/rate-overrides'; + + return restmod.model(rateOverrideEndpoint).mix('FieldDisplay', 'NestedDirtyModel', { + $config: { + name: 'Rate Override', + plural: 'Rate Overrides', + primaryKey: 'id', + fieldDisplays: [ + { + title: 'Name', + value: 'record.contributor.fullName' + }, + { + title: 'Role', + value: 'record.role.name' + } + ] + }, + role: { + init: {} + }, + $hooks: { + 'before-save': function(_req) { + var featureTypes = _req.data.feature_types; + if (featureTypes.length > 0) { + for (var key in featureTypes) { + var obj = featureTypes[key]; + if (obj.hasOwnProperty('featureType')) { + if (obj.featureType.hasOwnProperty('name')) { + _req.data.feature_types[key].feature_type = obj.featureType.name; + } else if (obj.featureType.hasOwnProperty('value')) { + _req.data.feature_types[key].feature_type.slug = obj.featureType.value; + } + } + } + } + }, + 'before-render': function(record) { + if (record.hasOwnProperty('feature_types')) { + for (var key in record.feature_types) { + if (record.feature_types[key].hasOwnProperty('feature_type')){ + if (record.feature_types[key].feature_type.hasOwnProperty('name')) { + record.feature_types[key].feature_type = record.feature_types[key].feature_type.name; + } + } + } + } + } + } + }); + }); +'use strict'; + +angular.module('apiServices.reporting.factory', [ + 'apiServices', + 'apiServices.mixins.fieldDisplay' +]) + .factory('Role', function (_, restmod) { + var roleEndpoint = 'contributions/role'; + return restmod.model(roleEndpoint).mix('FieldDisplay', 'NestedDirtyModel', { + flat_rates: { + hasMany: restmod.model(null, {}).mix('NestedDirtyModel'), + path: 'flat_rates', + params: {}, + hooks: { + 'after-fetch-many': function() { + if (this.length > 0) { + this.$owner.flat_rate = this[0]; + } else { + this.$owner.flat_rate = this.$owner.flat_rates.$create({ rate: 0 }); + } + } + } + }, + hourly_rates: { + hasMany: restmod.model(null, {rate: 0}).mix('NestedDirtyModel'), + path: 'hourly_rates', + params: {}, + hooks: { + 'after-fetch-many': function() { + if (this.length > 0) { + this.$owner.hourly_rate = this[0]; + } else { + this.$owner.hourly_rate = this.$owner.hourly_rates.$create({ rate: 0 }); + } + } + } + }, + feature_type_rates: { + hasMany: restmod.model(null, {}).mix('NestedDirtyModel'), + path: 'feature_type_rates', + params: {}, + hooks: { + 'after-fetch-many': function() { + var next = this.$metadata.next; + if (!_.isUndefined(next) && next !== null) { + this.$owner.feature_type_rates.$page++; + this.$owner.feature_type_rates.$fetch(); + } + } + } + }, + $config: { + name: 'Role', + plural: 'Roles', + primaryKey: 'id', + fieldDisplays: [ + { + title: 'Role', + value: 'record.name' + }, + { + title: 'Payment Type', + value: 'record.paymentType' + } + ] + }, + }); + }); +'use strict'; + +angular.module('apiServices.section.factory', [ + 'apiServices', + 'apiServices.customSearch.count.factory', + 'apiServices.mixins.fieldDisplay' +]) + .factory('Section', function (_, CustomSearchCount, restmod) { + var sectionEndpoint = 'section'; + + return restmod.model(sectionEndpoint).mix('FieldDisplay', 'NestedDirtyModel', { + $config: { + name: 'Section', + plural: 'Sections', + primaryKey: 'id', + fieldDisplays: [{ + title: 'Section Name', + value: 'record.name', + sorts: 'name' + }, { + title: 'Article Count', + value: 'record.$resultCount' + }] + }, + query: { + init: {} + }, + promoted: { + init: true + }, + $hooks: { + 'after-fetch': function () { + this.$refreshResultCount(); + }, + 'after-fetch-many': function () { + _.each(this, function (record) { + record.$refreshResultCount(); + }); + } + }, + $extend: { + Record: { + /** + * Getter for section content count. + * + * @returns {String} section content count. + */ + $refreshResultCount: function () { + var record = this; + return CustomSearchCount.$retrieveResultCount(this.query) + .then(function (count) { + record.$resultCount = count; + }); + } + } + }, + }); + }); + +'use strict'; + +angular.module('apiServices.specialCoverage.factory', [ + 'apiServices', + 'apiServices.campaign.factory', + 'apiServices.mixins.fieldDisplay', + 'cms.tunic.config', + 'filters.moment', + 'VideohubClient.api' +]) + .factory('SpecialCoverage', function (_, $http, $parse, $q, restmod, TunicConfig, Video) { + var ACTIVE_STATES = { + INACTIVE: 'Inactive', + PROMOTED: 'Pin to HP' + }; + + return restmod.model('special-coverage').mix('FieldDisplay', 'NestedDirtyModel', { + $config: { + name: 'Special Coverage', + plural: 'Special Coverages', + primaryKey: 'id', + fieldDisplays: [{ + title: 'List Title', + value: 'record.name', + sorts: 'name' + }, { + title: 'Sponsor', + value: 'record.campaign.sponsorName || "--"', + sorts: 'campaign__sponsor_name' + }, { + title: 'Campaign', + value: 'record.campaign.campaignLabel || "--"', + sorts: 'campaign__campaign_label' + }, { + title: 'Start Date', + value: 'record.startDate.format("MM/DD/YY") || "--"', + sorts: 'start_date' + }, { + title: 'End Date', + value: 'record.endDate.format("MM/DD/YY") || "--"', + sorts: 'end_date' + }] + }, + + // fields from frontend to backend + end_date: { + encode: 'moment_to_date_string', + }, + start_date: { + encode: 'moment_to_date_string', + }, + + // fields from backend to frontend + endDate: { + decode: 'date_string_to_moment', + }, + startDate: { + decode: 'date_string_to_moment' + }, + + campaign: { + belongsTo: 'Campaign', + prefetch: true, + key: 'campaign' + }, + listUrl: { + mask: 'CU' + }, + query: { + init: {} + }, + videos: { + belongsToMany: 'Video', + keys: 'videos' + }, + super_features: { + init: [], + keys: 'super_features' + }, + active: { + init: true + }, + promoted: { + init: false + }, + + $hooks: { + 'after-fetch': function () { + // auto fetch all video records when first fetching + this.$loadVideosData(); + }, + 'after-save': function () { + // auto fetch all video records when saving/updating + this.$loadVideosData(); + } + }, + + $extend: { + Record: { + /** + * Load video data by filling in video models listed in videos property. + */ + $loadVideosData: function () { + _.each(this.videos, function (video) { + video.$fetch(); + }); + }, + /** + * Load campaign data from Tunic endpoint + */ + $loadTunicCampaign: function () { + if (_.isNumber(this.tunicCampaignId)) { + return $http.get(TunicConfig.buildBackendApiUrl('campaign/' + this.tunicCampaignId + '/')).then(function (result) { + return result.data; + }); + } + return $q.reject(); + }, + /** + * Load campaign search results from Tunic endpoint + */ + $searchCampaigns: function (params) { + + return $http.get(TunicConfig.buildBackendApiUrl('campaign/'), { + params: params, + }).then(function (response) { + return response.data.results; + }); + }, + /** + * Add a video by id. + * + * @param {Number} id - id of video to add. + * @returns {Boolean} true if video was added, false otherwise. + */ + addVideo: function (video) { + var added = false; + + // check that video is not already in list + var existingVideo = _.find(this.videos, function (existingVideo) { + return video.id === existingVideo.id; + }); + + if (!existingVideo) { + // not in list, add it to list + this.videos.push(video); + added = true; + } + + return added; + }, + addSuperFeature: function (superFeature) { + + var existingSuperFeature = this.superFeatures + .find(function(existingSuperFeatureId) { + return superFeature.id === existingSuperFeatureId; + }); + + if (!existingSuperFeature) { + this.superFeatures.push(superFeature.id); + } + }, + /** + * Getter/setter for active state, a front end standin for the active + * and promoted flags. + * + * @param {String} [activeState] - set this value when using as setter. + * @returns {String} current activeState. + */ + $activeState: function (activeState) { + if (_.isString(activeState)) { + if (activeState === ACTIVE_STATES.ACTIVE) { + this.active = true; + this.promoted = false; + } else if (activeState === ACTIVE_STATES.PROMOTED) { + this.active = true; + this.promoted = true; + } else { + this.active = false; + this.promoted = false; + } + } else { + activeState = ACTIVE_STATES.INACTIVE; + if (this.active && this.promoted) { + activeState = ACTIVE_STATES.PROMOTED; + } else if (this.active) { + activeState = ACTIVE_STATES.ACTIVE; + } + } + return activeState; + } + }, + Model: { + ACTIVE_STATES: _.clone(ACTIVE_STATES) + } + }, + }); + }); + +'use strict'; + +angular.module('bulbs.cms.base.config', [ + 'bulbs.cms.config', + 'bulbs.cms.customSearch.config', + 'ngClipboard' +]) + .config([ + 'CmsConfigProvider', 'CustomSearchConfigProvider', 'ngClipProvider', + function (CmsConfigProvider, CustomSearchConfigProvider, ngClipProvider) { + + CmsConfigProvider + .setContentPartialsPath('/content_type_views') + .setComponentPath('/components') + .setDateTimeFormatHumanReadable('M/D/YY h:mma z') + .setDirectivePartialsPath('/views') + .setSharedPath('/shared') + .setTopBarMapping('nav', '/views/nav.html') + .setTopBarMapping('reportbar', '/views/reportbar.html') + .setTopBarMapping('toolbar', '/views/toolbar.html') + .setUnpublishedPath('unpublished'); + + CustomSearchConfigProvider + .addConditionField('Content Type', 'content-type', 'name', 'doctype') + .addConditionField('Feature Type', 'feature-type', 'name', 'slug') + .addConditionField('Tag', 'tag', 'name', 'slug') + .addConditionType('is any of', 'any') + .addConditionType('is all of', 'all') + .addConditionType('is none of', 'none') + .addTimePeriod('Past Day', 'Past day') + .addTimePeriod('Past Week', 'Past week') + .addTimePeriod('Past Month', 'Past month'); + + ngClipProvider.setPath('/bower_components/zeroclipboard/dist/ZeroClipboard.swf'); + } + ]); + +'use strict'; + +angular.module('bulbs.cms.config', [ + 'bulbs.cms.utils', + 'lodash' +]) + .provider('CmsConfig', [ + '_', 'UtilsProvider', + function (_, Utils) { + + var CmsConfigError = BulbsCmsConfigError.build('CmsConfig'); + var checkOrError = function (value, test, errorMsg) { + if (test(value)) { + return value; + } + throw new CmsConfigError(errorMsg); + }; + var pathBuilder = function (start) { + return function () { + return Utils.path.join(arguments); + }.bind(null, start); + }; + + // url for api root + var apiUrlRoot = ''; + // article statuses for sending to editoral + var articleEditoralStatuses = []; + // true to automatically add current user to author list when creating content + var autoAddAuthor = false; + // path to components + var componentPath = ''; + // path to edit pages for specific types of content, maps to ctype + var contentPartialsPath = ''; + // human readable date time format to use around the CMS + var dateTimeFormatHumanReadable = ''; + // path to directives from backend + // TODO : remove once apps are pulled into here + var directivePartialsPath = ''; + // name of CMS to display in title and in interface + var cmsName = ''; + // url for content + var contentApiUrl = ''; + // url for external links, those that are accessible to the public + var externalUrl = ''; + // max number of states to store in an article's history + var firebaseMaxArticleHistory = 25; + // path to site root in firebase instance + var firebaseSiteRoot = ''; + // url of firebase instance to use + var firebaseUrl = ''; + // url of image api + var imageApiUrl = ''; + // key to access image api + var imageApiKey = ''; + // path to inline editor buttons configuration + var inlineObjectsPath = ''; + // url for internal links, those that are not accessible to the public + var internalUrl = ''; + // name of directive to use for live blog author selection + var liveBlogAuthorSelectorDirectiveName = ''; + // path to cms logo static asset + var navLogoPath = ''; + // path to shared directory + // TODO : remove once app is fully in pods + var sharedPath = ''; + // super features ctype + var superFeaturesType = ''; + // name of timezone for to use for times in the cms + var timezoneName = 'America/Chicago'; + // mappings for top bar templates + var topBarMappings = {}; + // path from internal url that points to an endpoint for unpublished content + var unpublishedPath = ''; + // path to video embeds + var videoPath = ''; + // thumbnail for inline video uploads + var videoThumbnailUrl = ''; + + this.addArticleEditoralStatus = function (label, value) { + checkOrError(label, _.isString, 'article editoral status label must be a string!'); + checkOrError(value, _.isString, 'article editoral status value must be a string!'); + + articleEditoralStatuses.push({ + label: label, + value: value + }); + + return this; + }; + + this.setApiUrlRoot = function (value) { + apiUrlRoot = checkOrError( + value, _.isString, + 'api url root must be a string!' + ); + return this; + }; + + this.setAutoAddAuthor = function (value) { + autoAddAuthor = checkOrError( + value, _.isBoolean, + 'auto add author must be a boolean!' + ); + return this; + }; + + this.setComponentPath = function (value) { + componentPath = checkOrError( + value, _.isString, + 'component path must be a string!' + ); + return this; + }; + + this.setContentPartialsPath = function (value) { + contentPartialsPath = checkOrError( + value, _.isString, + 'content partials path must be a string!' + ); + return this; + }; + + this.setDateTimeFormatHumanReadable = function (value) { + dateTimeFormatHumanReadable = checkOrError( + value, _.isString, + 'date time format human readable must be a string!' + ); + return this; + }; + + this.setDirectivePartialsPath = function (value) { + directivePartialsPath = checkOrError( + value, _.isString, + 'directive partials path must be a string!' + ); + return this; + }; + + this.setCmsName = function (value) { + cmsName = checkOrError( + value, _.isString, + 'cms name must be a string!' + ); + return this; + }; + + this.setContentApiUrl = function (value) { + contentApiUrl = checkOrError( + value, _.isString, + 'content api url must be a string!' + ); + return this; + }; + + this.setExternalUrl = function (value) { + externalUrl = checkOrError( + value, _.isString, + 'external url must be a string!' + ); + return this; + }; + + this.setFirebaseMaxArticleHistory = function (value) { + firebaseMaxArticleHistory = checkOrError( + value, _.isNumber, + 'firebase max article history must be a number!' + ); + return this; + }; + + this.setFirebaseSiteRoot = function (value) { + firebaseSiteRoot = checkOrError( + value, _.isString, + 'firebase site url must be a string!' + ); + return this; + }; + + this.setFirebaseUrl = function (value) { + firebaseUrl = checkOrError( + value, _.isString, + 'firebase url must be a string!' + ); + return this; + }; + + this.setImageApiUrl = function (value) { + imageApiUrl = checkOrError( + value, _.isString, + 'image api url must be a string!' + ); + window.BC_ADMIN_URL = imageApiUrl; + return this; + }; + + this.setImageApiKey = function (value) { + imageApiKey = checkOrError( + value, _.isString, + 'image api key must be a string!' + ); + window.BC_API_KEY = imageApiKey; + return this; + }; + + this.setInlineObjectsPath = function (value) { + inlineObjectsPath = checkOrError( + value, _.isString, + 'inline objects path must be a string!' + ); + return this; + }; + + this.setInternalUrl = function (value) { + internalUrl = checkOrError( + value, _.isString, + 'internal url must be a string!' + ); + return this; + }; + + this.setLiveBlogAuthorSelectorDirectiveName = function (value) { + liveBlogAuthorSelectorDirectiveName = checkOrError( + value, _.isString, + 'live blog author selector directive name must be a string!' + ); + return this; + }; + + this.setNavLogoPath = function (value) { + navLogoPath = checkOrError( + value, _.isString, + 'nav logo path must be a string!' + ); + return this; + }; + + this.setSharedPath = function (value) { + sharedPath = checkOrError( + value, _.isString, + 'shared path must be a string!' + ); + return this; + }; + + this.setSuperFeaturesType = function (value) { + superFeaturesType = checkOrError( + value, _.isString, + 'super features type must be a string!' + ); + return this; + }; + + this.setTimezoneName = function (name) { + timezoneName = checkOrError( + name, moment.tz.zone, + 'given timezone name "' + name + '" is not a valid timezone!' + ); + return this; + }; + + this.setTopBarMapping = function (name, template) { + var key = checkOrError( + name, _.isString, + 'top bar mapping name must be a string!' + ); + var value = checkOrError( + template, _.isString, + 'top bar mapping template must be a string!' + ); + topBarMappings[key] = value; + return this; + }; + + this.setUnpublishedPath = function (value) { + unpublishedPath = checkOrError( + value, _.isString, + 'unpublished path must be a string!' + ); + return this; + }; + + this.setVideoPath = function (value) { + videoPath = checkOrError( + value, _.isString, + 'video path must be a string!' + ); + return this; + }; + + this.setVideoThumbnailUrl = function (value) { + videoThumbnailUrl = checkOrError( + value, _.isString, + 'video thumbnail url must be a string!' + ); + return this; + }; + + this.$get = [ + function () { + return { + buildApiUrlRoot: pathBuilder(apiUrlRoot), + buildComponentPath: pathBuilder(componentPath), + buildContentApiUrl: pathBuilder(contentApiUrl), + buildContentPartialsPath: pathBuilder(contentPartialsPath), + buildDirectivePartialsPath: pathBuilder(directivePartialsPath), + buildExternalUrl: pathBuilder(externalUrl), + buildFirebaseSiteUrl: pathBuilder(Utils.path.join(firebaseUrl, firebaseSiteRoot)), + buildFirebaseUrl: pathBuilder(firebaseUrl), + buildImageApiUrl: pathBuilder(imageApiUrl), + buildInternalUrl: pathBuilder(internalUrl), + buildSharedPath: pathBuilder(sharedPath), + buildUnpublishedUrl: pathBuilder(Utils.path.join(internalUrl, unpublishedPath)), + buildVideoUrl: pathBuilder(Utils.path.join(externalUrl, videoPath)), + buildVideoThumbnailUrl: pathBuilder(videoThumbnailUrl), + getAutoAddAuthor: _.constant(autoAddAuthor), + getCmsName: _.constant(cmsName), + getDateTimeFormatHumanReadable: _.constant(dateTimeFormatHumanReadable), + getFirebaseMaxArticleHistory: _.constant(firebaseMaxArticleHistory), + getImageApiKey: _.constant(imageApiKey), + getInlineObjecsPath: _.constant(inlineObjectsPath), + getLiveBlogAuthorSelectorDirectiveName: _.constant(liveBlogAuthorSelectorDirectiveName), + getNavLogoPath: _.constant(navLogoPath), + getSuperFeaturesType: _.constant(superFeaturesType), + getTimezoneName: _.constant(timezoneName), + getArticleEditoralStatuses: _.constant(_.cloneDeep(articleEditoralStatuses)), + getTopBarMapping: function (name) { + if (_.has(topBarMappings, name)) { + return topBarMappings[name]; + } + throw new CmsConfigError('no top bar mapping exists for name "' + name + '"!'); + } + }; + } + ]; + + return this; + } + ]); + +'use strict'; + +window.BulbsCmsError = function (name, message) { + if (typeof Error.captureStackTrace === 'function') { + Error.captureStackTrace(this, this.constructor); + } else { + this.stack = (new Error()).stack; + } + + this.name = name || 'CMS Error'; + this.message = message || 'Something was misconfigured.'; +}; +BulbsCmsError.prototype = Object.create(Error.prototype); +BulbsCmsError.prototype.constructor = window.BulbsCmsError; + +/** + * Create an error factory for a given provider. + * + * @param {string} name - used as name for instances. + * @returns factory that produces BulbsCmsError with the given name. + */ +BulbsCmsError.build = function (name) { + return function (message) { + return new BulbsCmsError(name, message); + }; +}; + +window.BulbsCmsConfigError = function (name, message) { + window.BulbsCmsError.call(this, name, message); +}; + +BulbsCmsConfigError.prototype = Object.create(window.BulbsCmsError.prototype); +BulbsCmsConfigError.prototype.constructor = window.BulbsCmsConfigError; + +BulbsCmsConfigError.build = function (name) { + return function (message) { + return new BulbsCmsConfigError( + 'Configuration Error (' + (name || 'name not given!') + ')', + message + ); + }; +}; + +'use strict'; + +angular.module('contentServices.factory', [ + 'bulbs.cms.site.config', + 'restangular' +]) + .factory('ContentFactory', [ + 'CmsConfig', 'Restangular', + function (CmsConfig, Restangular) { + return Restangular.withConfig(function (RestangularConfigurer) { + RestangularConfigurer.setBaseUrl(CmsConfig.buildApiUrlRoot()); + }); + } + ]); + + +'use strict'; + +angular.module('contentServices.listService', [ + 'contentServices.factory' +]) + .service('ContentListService', function (_, $location, $q, ContentFactory) { + + var ContentListService = this; + // bind data object to service so we can use 2-way data binding + // WARNING: DO NOT ACCESS THIS DIRECTLY! + this._serviceData = { + filters: $location.search() || {}, + content: [], + totalItems: 0 + }; + // shorthand + var _data = this._serviceData; + + /** + * Update filters used for searching content data. Note: this does not actually + * update content. + * + * @param {object} addFilters - filters to append to current filters. + * @param {Boolean} [merge=false] - false to overwrite current filters. + */ + ContentListService.updateFilters = function (addFilters, merge) { + if (merge) { + _data.filters = + _.assign($location.search() || {}, addFilters); + } else { + _data.filters = addFilters; + } + $location.search(_data.filters); + return _data.filters; + }; + + /** + * Update content by performing a search. + * + * @param {object} addFilters - filters to append to current filters before search. + * @param {Boolean} [merge=false] - true to merge query parameters. + * @returns {Promise} resolves with new content data. + */ + ContentListService.$updateContent = function (addFilters, merge) { + var updateParams = ContentListService.updateFilters(addFilters || _data.filters, merge); + return ContentFactory.all('content').getList(updateParams) + .then(function (data) { + _data.content = data; + _data.totalItems = data.metadata.count; + // resolve promise with updated content list service data + return _data; + }); + }; + + /** + * Access data object, this will have a two-way data binding. + */ + ContentListService.getData = function () { + return _data; + }; + + }); + +'use strict'; + +angular.module('contentServices', [ + 'contentServices.factory', + 'contentServices.listService' +]); + +'use strict'; + +angular.module('copyButton', [ + 'bulbs.cms.site.config' +]) + .directive('copyButton', function (CmsConfig) { + return { + controller: function ($scope, $timeout) { + + $scope.okCopy = false; + $scope.okCopyButton = function () { + $scope.okCopy = true; + $timeout(function () { + $scope.okCopy = false; + }, 1000); + }; + }, + restrict: 'E', + scope: { + buttonClassesDefault: '@', + buttonClassesSuccess: '@', + buttonText: '@', + content: '@' + }, + templateUrl: CmsConfig.buildSharedPath('copy-button/copy-button.html') + }; + }); + +'use strict'; + +angular.module('filters.moment', [ + 'moment' +]) + .filter('date_string_to_moment', function(moment) { + return function (dateStr) { + // Try to parse non-empty strings + if (dateStr && dateStr.length) { + var m = moment(new Date(dateStr)); + if (m.isValid()) { + return m; + } + } + return null; + }; + }) + .filter('moment_to_date_string', function(moment) { + return function (momentObj) { + if (moment.isMoment(momentObj) && momentObj.isValid()) { + return momentObj.format(); + } else { + // Blank time string == not set + return ''; + } + }; + }); + +'use strict'; + +angular.module('listPage', [ + 'bulbs.cms.site.config', + 'bulbs.cms.utils', + 'confirmationModal', + 'copyButton', + 'lodash' +]) + .directive('listPage', function (CmsConfig) { + return { + controller: function (_, $scope, $location, $parse, Utils) { + $scope.pathJoin = Utils.path.join; + + $scope.name = $scope.modelFactory.identity(); + $scope.namePlural = $scope.modelFactory.identity(true); + $scope.fields = $scope.modelFactory.$fieldDisplays(); + $scope.$list = $scope.modelFactory.$collection(); + + // different types of filters that get combined to make seach query params + $scope.orderingFilter = {}; + $scope.searchFilter = {}; + $scope.toggledFilters = {}; + + $scope.copyContentInContext = function (record) { + var value = ''; + if ($scope.toolCopyContent) { + value = $parse($scope.toolCopyContent)({record: record}); + } + return value; + }; + + $scope.$retrieve = _.debounce(function (addParams) { + $scope.loadingResults = true; + var allParams = _.merge( + {}, + $scope.orderingFilter, + $scope.toggledFilters, + $scope.searchFilter, + addParams + ); + return $scope.$list.$refresh(allParams) + .$then(function () { + $scope.loadingResults = false; + }); + }, 250); + + // search functionality + $scope.$search = function (query) { + $scope.searchFilter = {}; + + if (query) { + $scope.searchFilter[$scope.searchParameter] = query; + } + + // go to page 1, new results may not have more than 1 page + $scope.$list.$page = 1; + + $scope.$retrieve(); + }; + + // toggled filters, only one set of these can be applied at a time + $scope.filterButtonsParsed = $scope.filterButtons(); + $scope.$toggleFilters = function (params) { + $scope.toggledFilters = params; + + // go to page 1, new results may not have more than 1 page + $scope.$list.$page = 1; + + $scope.$retrieve(); + }; + + // sorting functionality, only one field can be sorted at a time + $scope.sortingField = null; + $scope.sortDirection = 'asc'; + $scope.$sort = function (field) { + var direction; + if (field.title === $scope.sortingField) { + // clicked on same field, make direction opposite of what it is now + direction = $scope.sortDirection === 'desc' ? 'asc' : 'desc'; + } else { + // clicked on a different field, start with the opposite of default + direction = 'desc'; + } + + // do ordering request + (function (field, direction) { + $scope.orderingFilter = {ordering: field.getOrdering(direction)}; + $scope.$retrieve() + .$then(function () { + $scope.sortingField = field.title; + $scope.sortDirection = direction; + }); + })(field, direction); + }; + + $scope.$add = function () { + $location.path($scope.cmsEditPageUrl({ item: { id: 'new' } })); + }; + + $scope.$remove = function (item) { + item.$destroy(); + }; + + $scope.goToEditPage = function (item) { + $location.path($scope.cmsEditPageUrl({ item: item })); + }; + + // set the active filter, either the first button with active === true, + // or empty string for all + $scope.activeFilterButton = + _.chain($scope.filterButtonsParsed) + .findWhere({active: true}) + .tap(function (button) { + // cheat here and set the params for the first retrieve + if (button) { + $scope.toggledFilters = button.params; + } + }) + .result('title') + .value() || + ''; + // do initial retrieval + $scope.$retrieve(); + }, + link: function (scope, element, attrs) { + scope.showAddButton = !('disableAddButton' in attrs); + }, + restrict: 'E', + scope: { + cmsEditPageUrl: '&', + filterButtons: '&', + modelFactory: '=', + searchParameter: '@', + toolCopyContent: '@', + utilityButtons: '=' + }, + templateUrl: CmsConfig.buildSharedPath('list-page/list-page.html') + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp.nonRestmodListPage', [ + 'bulbs.cms.site.config', + 'confirmationModal', + 'copyButton', + 'lodash', + 'bulbs.cms.utils' +]) + .directive('nonRestmodListPage', function (CmsConfig) { + return { + controller: function (_, $scope, $location, $parse, Utils) { + + $scope.pathJoin = Utils.path.join; + + // different types of filters that get combined to make seach query params + $scope.orderingFilter = {}; + $scope.searchFilter = {}; + $scope.toggledFilters = {}; + $scope.pageNumber = 1; + + $scope.copyContentInContext = function (record) { + var value = ''; + if ($scope.toolCopyContent) { + value = $parse($scope.toolCopyContent)({record: record}); + } + return value; + }; + + $scope.$retrieve = _.debounce(function (addParams) { + $scope.loadingResults = true; + var allParams = _.merge( + {}, + $scope.orderingFilter, + $scope.toggledFilters, + $scope.searchFilter, + addParams + ); + return $scope.getItems({params: allParams}) + .then(function (response) { + $scope.items = response.results; + $scope.loadingResults = false; + }); + }, 250); + + // search functionality + $scope.$search = function (query) { + $scope.searchFilter = {}; + + if (query) { + $scope.searchFilter[$scope.searchParameter] = query; + } + + // go to page 1, new results may not have more than 1 page + $scope.pageNumber = 1; + + $scope.$retrieve(); + }; + + // toggled filters, only one set of these can be applied at a time + $scope.filterButtonsParsed = $scope.filterButtons(); + $scope.$toggleFilters = function (params) { + $scope.toggledFilters = params; + + // go to page 1, new results may not have more than 1 page + $scope.pageNumber = 1; + + $scope.$retrieve(); + }; + + // sorting functionality, only one field can be sorted at a time + $scope.sortingField = null; + $scope.sortDirection = 'asc'; + $scope.$sort = function (fieldName) { + var direction; + if (fieldName === $scope.sortingField) { + // clicked on same field, make direction opposite of what it is now + direction = $scope.sortDirection === 'desc' ? '' : '-'; + } else { + // clicked on a different field, start with the opposite of default + direction = '-'; + } + + // do ordering request + $scope.orderingFilter = {ordering: direction + fieldName}; + $scope.$retrieve($scope.orderingFilter.ordering) + .then(function () { + $scope.sortingField = fieldName; + $scope.sortDirection = direction === '-' ? 'desc' : 'asc'; + }); + }; + + $scope.cellContents = function (item, field) { + var cellContents = '-'; + + if (_.isFunction(field.content)) { + cellContents = field.content(item); + } else if (_.isString(field.content)) { + cellContents = item[field.content]; + } else if (field.sorts) { + cellContents = item[field.sorts]; + } + return cellContents; + }; + + $scope.$add = function () { + $location.path($scope.cmsEditPageUrl({ item: { id: 'new' } })); + }; + + $scope.$remove = function (removedItem) { + $scope.destroyItem({item: removedItem}); + _.remove($scope.items, function(item) { + return item === removedItem; + }); + }; + + $scope.goToEditPage = function (item) { + $location.path($scope.cmsEditPageUrl({ item: item })); + }; + + // set the active filter, either the first button with active === true, + // or empty string for all + $scope.activeFilterButton = + _.chain($scope.filterButtonsParsed) + .findWhere({active: true}) + .tap(function (button) { + // cheat here and set the params for the first retrieve + if (button) { + $scope.toggledFilters = button.params; + } + }) + .result('title') + .value() || + ''; + + // do initial retrieval + $scope.$retrieve(); + }, + link: function (scope, element, attrs) { + scope.showAddButton = !('disableAddButton' in attrs); + }, + restrict: 'E', + scope: { + cmsEditPageUrl: '&', // url to edit page, will be postfixed with id or 'new' + destroyItem: '&', // returns promise, deletes given item + getItems: '&', // function returns promise, recieves search params + filterButtons: '&', // settings for filter buttons + // { title:'human readable', active:true, params:queryParams object } + modelFields: '&', // list of objects { sorts: x, title: y} + modelName: '&', // list page title + modelNamePlural: '&', // list page title pluralized + searchParameter: '@', // key for text search param + toolCopyContent: '@', // content to copy with copy buttons, where `record` is the record being copied, leave empty to hide copy button + }, + templateUrl: CmsConfig.buildSharedPath('non-restmod-list-page/non-restmod-list-page.html') + }; + }); + +'use strict'; + +angular.module('cms.tunic.config', [ + 'lodash' +]) + .provider('TunicConfig', [ + '_', + function TunicConfigProvider (_) { + // relative api path, rel to backendRoot + var apiPath = ''; + // root for all backend requests + var backendRoot = ''; + // Tunic API request token + var requestToken = ''; + + var error = function (message) { + return new Error('Configuration Error (TunicConfig) ' + message); + }; + + this.setApiPath = function (value) { + if (_.isString(value)) { + apiPath = value; + } else { + throw error('apiPath must be a string!'); + } + return this; + }; + + this.setBackendRoot = function (value) { + if (_.isString(value)) { + backendRoot = value; + } else { + throw error('backendRoot must be a string!'); + } + return this; + }; + + this.setRequestToken = function (value) { + if (_.isString(value)) { + requestToken = value; + } else { + throw error('requestToken must be a string!'); + } + return this; + }; + + this.$get = function () { + return { + getRequestToken: _.constant(requestToken), + /** + * Create an absolute api url. + * + * @param {string} relUrl - relative url to get the absolute api url for. + * @returns absolute api url. + */ + buildBackendApiUrl: function (relUrl) { + return backendRoot + apiPath + (relUrl || ''); + }, + + /** + * Check if a given url should be intercepted by this library's interceptor. + * + * @param {string} url - Url to test against matchers. + * @returns {boolean} true if url should be intercepted, false otherwise. + */ + shouldBeIntercepted: function (url) { + var urlTest = backendRoot + apiPath; + return urlTest !== '' && url.startsWith(urlTest); + } + }; + }; + } + ]); + +'use strict'; + +angular.module('cms.tunic.interceptor', [ + 'cms.tunic.config' +]) + .service('TunicInterceptor', [ + 'TunicConfig', + function (TunicConfig) { + + this.request = function (config) { + if (TunicConfig.shouldBeIntercepted(config.url)) { + config.headers = config.headers || {}; + config.headers.Authorization = 'Token ' + TunicConfig.getRequestToken(); + } + return config; + }; + + return this; + } + ]); + +'use strict'; + +angular.module('cms.tunic', [ + 'cms.tunic.config', + 'cms.tunic.interceptor' +]); + +'use strict'; + +angular.module('bulbs.cms.utils', [ + 'lodash' +]) + .provider('Utils', [ + '_', + function (_) { + + var Utils = this; + + Utils.slugify = function (text) { + // https://gist.github.com/mathewbyrne/1280286 + return text.toString().toLowerCase() + .replace(/\s+/g, '-') // Replace spaces with - + .replace(/[^\w\-]+/g, '') // Remove all non-word chars + .replace(/\-\-+/g, '-') // Replace multiple - with single - + .replace(/^-+/, '') // Trim - from start of text + .replace(/-+$/, ''); // Trim - from end of text + }; + + /** + * In given list, swaps item at indexFrom with item at indexTo. + * + * @param {Array} list - list to swap items in. + * @param {Number} indexFrom - Index to move content from. + * @param {Number} indexTo - Index to move content to. + * @param {Boolean} allowOutOfBounds - true to perform move when given + * indexTo that's out of bounds. + * @returns {Boolean} true if item was moved, false otherwise. + */ + Utils.moveTo = function (list, indexFrom, indexTo, allowOutOfBounds) { + var ret = false; + var modIndexTo = indexTo; + + if (allowOutOfBounds) { + if (indexTo < 0) { + modIndexTo = 0; + } else if (indexTo >= list.length) { + modIndexTo = list.length - 1; + } + } + + if (indexFrom >= 0 && indexFrom < list.length && + modIndexTo >= 0 && modIndexTo < list.length) { + var splicer = list.splice(indexFrom, 1, list[modIndexTo]); + if (splicer.length > 0) { + list[modIndexTo] = splicer[0]; + ret = true; + } + } + + return ret; + }; + + /** + * In given list, remove an item at given index. + * + * @param {Array} list - list to remove item from. + * @param {Number} index - index of item to remove. + * @returns {Boolean} true if item was removed, false otherwise. + */ + Utils.removeFrom = function (list, index) { + return list.splice(index, 1).length > 0; + }; + + /** + * Transform an object into url params. + * ONLY knows what to do with a flat params object. + * Similar to jQuery.param + * @param {Object} params - Object of params to serialize. + * @returns {String} query - a url querystring (is prefixed with '?') + */ + Utils.param = function (params) { + if (!params) { + params = {}; + } + var keys = Object.keys(params); + var query = ''; + if (keys.length > 0) { + query += '?'; + query += keys.map(function (key) { + return key + '=' + params[key]; + }) + .join('&'); + } + return query; + }; + + Utils.path = { + /** + * Join path strings. + * + * @param {...String} A variable number of strings to join into path. + * @returns {String} joined path. + */ + join: function () { + var sep = '/'; + var replace = new RegExp(sep + '{1,}', 'g'); + var argsArr = _.flattenDeep(arguments); + + // if there's a protocol, make sure to ignore it when replacing sep + var protocolPrefix = ''; + if (argsArr.length > 0 && typeof(argsArr[0]) === 'string') { + var matches = argsArr[0].match(/^(https?:)?\/\//); + if (matches) { + protocolPrefix = matches[0]; + argsArr[0] = argsArr[0].replace(protocolPrefix, ''); + } + } + return protocolPrefix + argsArr.join(sep).replace(replace, sep); + } + }; + + Utils.$get = [ + '$q', + function ($q) { + /** + * Create a lock to apply to wrapped functions so that only one of the + * functions using this lock can be run at a time. + * + * @returns {function} function that can be used to wrap functions that + * should be locked by this instance of a lock. The wrapped function can + * then be called as the original function would be called, but the result + * will be wrapped in a promise. + */ + Utils.buildLock = function () { + var locked = false; + + var wrapper = function (func) { + + return function () { + if (!locked) { + locked = true; + + return $q + .when(func.apply(null, arguments)) + .finally(function () { + locked = false; + }); + } else { + return $q.reject(); + } + }; + }; + + wrapper.isLocked = function () { + return locked; + }; + + return wrapper; + }; + + return Utils; + } + ]; + + return Utils; + } + ]); + +'use strict'; + +angular.module('videoList.video.directive', [ + 'bulbs.cms.site.config', + 'filters.moment' +]) + .directive('videoListVideo', function (CmsConfig) { + return { + restrict: 'E', + scope: { + model: '=' + }, + templateUrl: CmsConfig.buildSharedPath('video-list/video-list-video/video-list-video.html') + }; + }); + +'use strict'; + +angular.module('videoList', [ + 'autocompleteBasic', + 'bulbs.cms.site.config', + 'bulbs.cms.utils', + 'jquery', + 'ui.sortable', + 'VideohubClient.api', + 'VideohubClient.settings', + 'videoList.video.directive' +]) + .directive('videoList', function ($, CmsConfig) { + return { + controller: function (_, $scope, Utils, Video, VIDEOHUB_DEFAULT_CHANNEL) { + + $scope.videoChannel = VIDEOHUB_DEFAULT_CHANNEL; + + $scope.moveUp = function (index) { + Utils.moveTo($scope.videos, index, index - 1); + $scope.onUpdate(); + }; + + $scope.moveDown = function (index) { + Utils.moveTo($scope.videos, index, index + 1); + $scope.onUpdate(); + }; + + $scope.delete = function (index) { + Utils.removeFrom($scope.videos, index); + $scope.onUpdate(); + }; + + $scope.addVideo = function (video) { + $scope.addVideoCallback({video: video}); + $scope.onUpdate(); + }; + + $scope.searchVideos = function (query) { + return Video.$postSearch({ + query: query, + channel: VIDEOHUB_DEFAULT_CHANNEL + }); + }; + + }, + link: function (scope, element, attr) { + + scope.sortableOptions = { + beforeStop: function (e, ui) { + ui.helper.css('margin-top', 0); + }, + change: function (e, ui) { + ui.helper.css('margin-top', $(window).scrollTop()); + }, + containment: 'video-list', + distance: 3, + opacity: 0.75, + placeholder: 'dropzone', + start: function (e, ui) { + ui.helper.css('margin-top', $(window).scrollTop()); + } + }; + }, + restrict: 'E', + scope: { + addVideoCallback: '&addVideo', + videos: '=', + onUpdate: '&' + }, + templateUrl: CmsConfig.buildSharedPath('video-list/video-list.html') + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('activeNav', function ($location) { + return { + template: '
  • {{label}}
  • ', + restrict: 'E', + scope: {}, + replace: true, + link: function postLink(scope, element, attrs) { + scope.href = attrs.href; + scope.label = attrs.label; + if ($location.path().indexOf(scope.href) === 0) { + element.addClass('active'); + } + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('addImage', function ($http, $window) { + return { + restrict: 'E', + templateUrl: '/views/add-image.html', + scope: { + article: '=' + }, + link: function (scope, element, attrs) { + var attrName = attrs.attrName || 'images'; + scope.article[attrName] = scope.article[attrName] || []; + + if (attrs.caption === 'false') { scope.hideCaption = true; } + scope.format = attrs.format || 'jpg'; + scope.crop = attrs.crop || '16x9'; + scope.placeholderText = attrs.placeholderText || 'Optional Image'; + + scope.addAnImage = function () { + $window.uploadImage({ + onSuccess: function (data) { + scope.$apply(function () { + scope.article[attrName].push({ + id: data.id.toString(), + alt: null, + caption: null + }); + setTimeout($window.picturefill, 200); + }); + }, + onError: function (data) { + scope.$apply(function () { + $window.alert('Error: ', data); + }); + }, + onProgress: function (data) { + + } + }); + }; + } + + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('articlecontainer', function () { + return { + restrict: 'E', + templateUrl: '/views/promotion-tool-article-container.html', + scope: { + 'article': '=' + }, + link: function postLink(scope, element, attrs) { + scope.ratio = attrs.ratio; + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('authorsField', function ($, CmsConfig, userNameDisplayFilter, Utils) { + return { + templateUrl: '/views/taglike-autocomplete-field.html', + restrict: 'E', + replace: true, + scope: { + article: '=', + inputLabelText: '@', + inputLabelTextSub: '@' + }, + link: function postLink(scope, element, attrs) { + scope.name = 'author'; + scope.label = scope.inputLabelText || 'Authors'; + scope.labelSub = scope.inputLabelTextSub; + scope.placeholder = 'Authors'; + scope.resourceUrl = CmsConfig.buildApiUrlRoot( + 'author', + Utils.param({ + ordering: 'name', + search: '' + }) + ); + scope.display = userNameDisplayFilter; + + scope.$watch('article.authors', function () { + scope.objects = scope.article.authors; + }, true); + + scope.add = function (o, input) { + for (var t in scope.article.authors) { + if (scope.article.authors[t].id === o.id) { return; } + } + scope.article.authors.push(o); + $(input).val(''); + }; + + scope.delete = function (e) { + var author = $(e.target).parents('[data-taglikeobject]').data('taglikeobject'); + var id = author.id; + var newauthors = []; + for (var i in scope.article.authors) { + if (scope.article.authors[i].id !== id) { + newauthors.push(scope.article.authors[i]); + } + } + scope.article.authors = newauthors; + }; + + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('bulbsAutocomplete', function ($http, $location, $compile, $timeout, $, Login, Raven) { + + var autocomplete_dropdown_template = + ''; + + return { + restrict: 'AC', + link: function postLink(scope, element, attrs) { + var $elem = $(element).find('input'); + $elem.attr('autocomplete', 'off'); + var dropdown = $($compile(autocomplete_dropdown_template)(scope)); + + $(dropdown).css({ + position: 'absolute', + top: $elem.position().top + $elem.outerHeight(), + left: $elem.position().left, + minWidth: $elem.outerWidth(), + display: 'none' + }); + $elem.parent().append(dropdown); + $(dropdown).fadeIn('fast'); + + // Observe the element's dimensions. + scope.$watch( + function () { + return { + top: $elem.position().top + $elem.outerHeight(), + left: $elem.position().left, + minWidth: $elem.outerWidth() + }; + }, + function (newValue, oldValue) { + $(dropdown).css({ + top: newValue.top, + left: newValue.left, + minWidth: newValue.minWidth + }); + }, + true + ); + + var inputCounter = 0, inputTimeout; + + $elem.on('focus', function (e) { + $elem.on('input', function () { + var val = $elem.val(); + if (val === '') { + scope.autocomplete_list = []; + } else { + $timeout.cancel(inputTimeout); + inputTimeout = $timeout(function () { getAutocompletes(val); }, 200); + + if (inputCounter > 2) { + getAutocompletes(val); + } + } + }); + $(dropdown).fadeIn('fast'); + }); + + function getAutocompletes(val) { + $timeout.cancel(inputTimeout); + inputCounter = 0; + $http({ + method: 'GET', + url: scope.resourceUrl + val + }).success(function (data) { + var results = data.results || data; + scope.autocomplete_list = results.splice(0, 5); + }).error(function (data, status, headers, config) { + Raven.captureMessage('Error in getAutocompletes', {extra: data}); + }); + } + + $elem.on('blur', function (e) { + $(dropdown).fadeOut('fast'); + }); + + $(dropdown).on('mouseover', '.entry', function (e) { + $(dropdown).find('.selected').removeClass('selected'); + $(this).addClass('selected'); + }); + + $elem.on('keyup', function (e) { + if (e.keyCode === 40) { //down + if ($('div.selected', dropdown).length === 0) { + $('div.entry', dropdown).first().addClass('selected'); + } else { + var curDownSelect = $('div.selected', dropdown); + var curDownSelectNext = curDownSelect.next('div'); + if (curDownSelectNext.length === 0) { + $('div.entry', dropdown).first().addClass('selected'); + } else { + curDownSelectNext.addClass('selected'); + } + curDownSelect.removeClass('selected'); + } + } + if (e.keyCode === 38) { //up + if ($('div.selected', dropdown).length === 0) { + $('div.entry', dropdown).last().addClass('selected'); + } else { + var curSelect = $('div.selected', dropdown); + var curSelectNext = curSelect.prev('div'); + if (curSelectNext.length === 0) { + $('div.entry', dropdown).last().addClass('selected'); + } else { + curSelectNext.addClass('selected'); + } + curSelect.removeClass('selected'); + } + } + if (e.keyCode === 13) { + var selected = $('div.selected', dropdown); + if (selected.length === 0) { scope.onClick($elem.val(), true); } + selected.click(); + } + }); + + scope.onClick = function (o, freeForm) { + scope.add(o, $elem, freeForm || false); + scope.autocomplete_list = []; + //if (!scope.$$phase) scope.$apply(); + }; + + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('cmsNotification', function () { + return { + restrict: 'E', + templateUrl: '/views/cms-notification.html', + scope: { + cmsNotification: '=' + }, + controller: 'CmsNotificationCtrl' + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('cmsNotifyContainer', function () { + return { + restrict: 'E', + scope: {}, + templateUrl: '/views/cms-notify-container.html', + controller: 'CmsNotifyContainerCtrl' + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('contributorField', function ($) { + return { + templateUrl: '/views/textlike-autocomplete-field.html', + restrict: 'E', + replace: true, + scope: { + override: '=' + }, + link: function postLink(scope, element, attrs) { + scope.name = 'contributor'; + scope.label = 'Contributors'; + scope.placeholder = 'Contributors'; + scope.resourceUrl = '/cms/api/v1/author/?ordering=name&search='; + + scope.$watch('override.contributor', function () { + if ((scope.override.hasOwnProperty('contributor')) && (scope.override.contributor !== null)) { + scope.model = scope.override.contributor.full_name || scope.override.contributor.fullName; + } + }); + + scope.display = function (o) { + return (o && o.full_name) || ''; + }; + + scope.add = function(o, input) { + if ((scope.override.hasOwnProperty('contributor')) && scope.override.contributor !== null) { + if (scope.override.contributor.id === o.id) { + return; + } + } + + scope.override.contributor = o; + $('#feature-type-container').removeClass('newtag'); + $('#feature-type-container').addClass('newtag'); + }; + + scope.delete = function (e) { + scope.override.contributor = null; + scope.model = null; + }; + + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('createContent', function ($http, $window, $, IfExistsElse, Login, + ContentFactory, CmsConfig, Raven) { + return { + restrict: 'E', + templateUrl: CmsConfig.buildDirectivePartialsPath('create-content.html'), + controller: function ($scope) { + $scope.gotTags = false; + $scope.gotUser = false; + $scope.gotSave = false; + $scope.$watch(function () { + return $scope.gotTags && $scope.gotUser && $scope.gotSave; + }, function (val) { + if (val) { + saveArticle($scope.init); + } + }); + + $scope.newArticle = function (e) { + var init = {'title': $scope.newTitle}; + angular.extend($scope.init, init); + + if ($scope.tag) { + IfExistsElse.ifExistsElse( + ContentFactory.all('tag').getList({ + ordering: 'name', + search: $scope.tag, + types: 'core_section' + }), + {slug: $scope.tag}, + function (tag) { $scope.init.tags = [tag]; $scope.gotTags = true; }, + function (value) { console.log('couldnt find tag ' + value.slug + ' for initial value'); }, + function (data, status, headers, config) { Raven.captureMessage('Error Creating Article', {extra: data}); } + ); + } else { + $scope.gotTags = true; + } + + if (CmsConfig.getAutoAddAuthor()) { + ContentFactory.one('me').get().then(function (data) { + $scope.init.authors = [data]; + $scope.gotUser = true; + }); + } else { + $scope.gotUser = true; + } + + $scope.gotSave = true; + }; + + function saveArticle() { + $('button.go').removeClass('btn-danger').addClass('btn-success').html(' Going'); + $http({ + url: '/cms/api/v1/content/?doctype=' + $scope.contentType, + method: 'POST', + data: $scope.init + }).success(function (resp) { + var new_id = resp.id; + var new_path = '/cms/app/edit/' + new_id + '/'; + if ($scope.rating_type) { + new_path += '?rating_type=' + $scope.rating_type; + } + $window.location.href = $window.location.origin + new_path; + }).error(function (data, status, headers, config) { + if (status === 403) { + $('button.go') + .html(' Please Log In'); + } else { + $('button.go').removeClass('btn-success').addClass('btn-danger').html(' Error'); + } + $scope.gotSave = false; + }); + } + + + }, + link: function (scope, element, attrs) { + //HEY THIS SUCKS + //TODO: This sucks! + scope.panel = 1; + + $(element).find('a.create-content').on('click', function (e) { + $('a.create-content.active').removeClass('active'); + $(this).addClass('active'); + }); + + $(element).find('a.create-content').on('click', function (e) { + scope.contentTypeLabel = $(this).text(); + scope.contentType = $(this).data('content_type') || null; + scope.init = $(this).data('init') || {}; + scope.tag = $(this).data('tag') || null; + scope.rating_type = $(this).data('rating_type') || null; + scope.$apply(); + + if ($(this).hasClass('go-next')) { + e.preventDefault(); + + $('#create button.next-pane').click(); + return false; + } + + return true; + }); + + $('button.next-pane:not(.hide)').on('click', function (e) { + scope.panel = 2; + $('.new-title').focus(); + }); + + $(element).on('keydown', '.editor', function (e) { + if (e.keyCode === 13 && scope.newTitle) { + $(element).find('.go').click(); + } + }); + + $('#create').on('hidden.bs.modal', function () { + scope.newTitle = ''; + scope.panel = 1; + }); + + } + + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('devicepreview', function ($) { + return { + restrict: 'E', + templateUrl: '/views/devicepreview.html', + link: function (scope, element, attrs) { + + var pP = $('#page-prev'), + tN = pP.find('.nav a'), + cO = pP.find('.tab-content .active'); + + tN.click(function (e) { + var newId = $(this).attr('href').split('#')[1]; + e.preventDefault(); + cO.attr('id', newId); + }); + + $('#page-prev').on('show.bs.collapse', function () { + $(this).find('.fa').removeClass('fa-plus-square-o').addClass('fa-minus-square-o'); + }); + + $('#page-prev').on('hide.bs.collapse', function () { + $(this).find('.fa').removeClass('fa-minus-square-o').addClass('fa-plus-square-o'); + }); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('encodeStatus', function ($http, $interval, $, Zencoder) { + return { + templateUrl: '/views/encode-status.html', + restrict: 'E', + link: function postLink(scope, element, attrs) { + scope.encodingVideos = {}; + + scope.$watch(function () { + return Zencoder.encodingVideos; + }, function () { + updateEncodeStatuses(); + }, true); + + $interval(function () { + $('iframe').filter(function () { return this.src.match(/\/video\/embed\/?/); }).each(function () { + var idRegex = /\/video\/embed\/?\?id=(\d+)/; + var id = idRegex.exec(this.src)[1]; + if (!(id in Zencoder.encodingVideos)) { + Zencoder.getVideo(id).then(function (data) { + Zencoder.encodingVideos[id] = data.data; + }); + } + }); + updateEncodeStatuses(); + }, 5000); + + function updateEncodeStatuses() { + var updateEncodeStatus = function (i) { + return (function (videoid) { + if (Zencoder.encodingVideos[videoid].encode_status_endpoints && + Zencoder.encodingVideos[videoid].encode_status_endpoints.json) { + + $http({ + method: 'GET', + url: Zencoder.encodingVideos[videoid].encode_status_endpoints.json, + headers: { + 'X-CSRFToken': undefined + }, + }).success(function (data) { + scope.encodingVideos[videoid].job_status = data; + if (data.state === 'finished') { + scope.encodingVideos[videoid].finished = true; + } + + }); + } + })(i); + }; + + for (var i in Zencoder.encodingVideos) { + if (scope.encodingVideos[i] && scope.encodingVideos[i].finished) { + continue; + } + scope.encodingVideos[i] = Zencoder.encodingVideos[i]; + updateEncodeStatus(i); + } + } + + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('featuretypeField', function (IfExistsElse, ContentFactory, Raven, $) { + return { + templateUrl: '/views/textlike-autocomplete-field.html', + restrict: 'E', + scope: { + article: '=', + hideLabel: '=' + }, + replace: true, + link: function postLink(scope, element, attrs) { + scope.name = 'feature_type'; + scope.label = 'Feature Type'; + scope.placeholder = 'Feature Type'; + scope.resourceUrl = '/cms/api/v1/things/?type=feature_type&q='; + + scope.$watch('article.feature_type', function () { + scope.model = scope.article.feature_type; + }); + + scope.display = function (o) { + return (o && o.name) || ''; + }; + + scope.add = function (o, input, freeForm) { + var fVal = freeForm ? o : o.name; + IfExistsElse.ifExistsElse( + ContentFactory.all('things').getList({ + type: 'feature_type', + q: fVal + }), + {name: fVal}, + function (ft) { scope.article.feature_type = ft.name; $('#feature-type-container').removeClass('newtag'); }, + function (value) { scope.article.feature_type = value.name; $('#feature-type-container').addClass('newtag'); }, + function (data, status) { Raven.captureMessage('Error Adding Feature Type', {extra: data}); } + ); + }; + + scope.delete = function (e) { + scope.article.feature_type = null; + }; + + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('hideIfForbidden', function ($http) { + function hideElement(element) { + element.addClass('hidden'); + } + + return { + restrict: 'A', + link: function postLink(scope, element, attrs) { + $http({ + method: 'OPTIONS', + url: attrs.optionsUrl, + noPermissionIntercept: true + }).success(function (data, status) { + //I guess 403s aren't errors? I dont know. + if (status === 403) { + hideElement(element); + } + }).error(function (data, status) { + if (status === 403) { + hideElement(element); + } + }); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('lazyInclude', function ($, $compile, $q, $http, $templateCache, + Gettemplate) { + /* + this is like ng-include but it doesn't compile/render the included template + until the child element is visible + intended to help with responsiveness by cutting down requests and rendering time + */ + + return { + restrict: 'A', + scope: true, + link: function (scope, element, attrs) { + var templateUrl = '/views/' + attrs.template; + var $element = $(element); + + scope.$evalAsync(function () { + scope.$watch(function () { + return $element.is(':visible'); + }, function (visible) { + if (visible && !scope.loaded) { + scope.loaded = true; + Gettemplate.get(templateUrl).then(function (html) { + var template = angular.element(html); + var compiledEl = $compile(template)(scope); + element.html(compiledEl); + element.css('height', 'auto'); + }); + } + }); + }); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('loggedInUser', function (CurrentUserApi) { + return { + restrict: 'E', + replace: true, + templateUrl: '/views/logged-in-user.html', + scope: {}, + link: function (scope, element, attrs) { + CurrentUserApi.getCurrentUserWithCache() + .then(function (user) { + scope.currentUser = user; + + scope.logout = CurrentUserApi.logout; + }); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('navBar', function (CmsConfig, CurrentUserApi) { + return { + restrict: 'E', + scope: false, + templateUrl: function (tElement, tAttrs) { + return CmsConfig.getTopBarMapping(tAttrs.view); + }, + link: function (scope) { + scope.NAV_LOGO = CmsConfig.getNavLogoPath(); + + CurrentUserApi.getCurrentUserWithCache() + .then(function (user) { + scope.currentUser = user; + }); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('responsiveImage', function ($window, $) { + return { + link: function (scope, element, attrs) { + attrs.$observe('imageId', function (val) { + $(element).find('img').remove(); + element.attr('data-image-id', attrs.imageId); + element.attr('data-crop', attrs.crop); + if (!attrs.imageId || !$(element).is(':visible')) { + return; + } + $window.pictureFillElement(element[0]); + element.show(); + }); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('roleField', function ($http, $, Raven) { + return { + templateUrl: '/views/rolefield.html', + restrict: 'E', + replace: true, + scope: { + model: '=' + }, + + link: function (scope, element, attrs) { + var resourceUrl = '/cms/api/v1/contributions/role/'; + + scope.roleValue = null; + scope.roleOptions = []; + + scope.$watch('model.role', function () { + for (var i = 0; i < scope.roleOptions.length; i++) { + if (scope.roleOptions[i].id === Number(scope.roleValue)) { + scope.model.role = scope.roleOptions[i]; + } + } + scope.roleValue = scope.model.role.id; + }); + + $http({ + method: 'GET', + url: resourceUrl + }).success(function (data) { + scope.roleOptions = data.results || data; + }).error(function (data, status, headers, config) { + Raven.captureMessage('Error fetching Roles', {extra: data}); + }); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('saveButtonOld', function ($q, $timeout, $window) { + return { + replace: true, + restrict: 'E', + templateUrl: '/views/save-button.html', + scope: { + 'getPromise': '&', + 'saveCbk': '&onSave', + 'config': '=?', + 'colors': '@?colorStyling' + }, + link: function (scope, element, attrs) { + scope.colors_tmp = scope.colors; + + attrs.$observe('config', function (val) { + if (!angular.isDefined(val)) { + scope.config = { + idle: ' Save', + busy: 'Saving', + finished: 'Saved', + error: 'Error' + }; + } + }); + + scope.save = function () { + if (attrs.confirmClickWith) { + var message = attrs.confirmClickWith; + if (!$window.confirm(message)) { return; } + } + + scope.colors = scope.colors_tmp; + element + .prop('disabled', true) + .html(' ' + scope.config.busy); + + var save_promise = scope.getPromise(); + + var saveSuccess = function (result) { + scope.colors = scope.colors_tmp; + element + .prop('disabled', false) + .html(' ' + scope.config.finished); + + return $timeout(function () { + element.html(scope.config.idle); + }, 1000) + .then(function () { + return result; + }); + }; + + if (save_promise) { + var promise = save_promise + .then(saveSuccess) + .catch( + function (reason) { + scope.colors = 'btn-danger'; + element + .prop('disabled', false) + .html(' ' + scope.config.error); + + return $q.reject(reason); + }); + if (scope.saveCbk) { + scope.saveCbk({promise: promise}); + } + } else { + // no save promise was set, just run success function + saveSuccess(); + } + }; + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('sectionsField', function (_, IfExistsElse, ContentFactory, Raven, + $) { + return { + templateUrl: '/views/taglike-autocomplete-field.html', + restrict: 'E', + replace: true, + link: function postLink(scope, element, attrs) { + scope.name = 'section'; + scope.label = 'Sections'; + scope.placeholder = 'Enter a section'; + scope.resourceUrl = '/cms/api/v1/tag/?ordering=name&types=core_section&search='; + scope.display = function (o) { + return o.name; + }; + + scope.$watch('article.tags', function () { + scope.objects = _.where(scope.article.tags, {type: 'core_section'}); + }, true); + + scope.add = function (o, input, freeForm) { + var tagVal = freeForm ? o : o.name; + IfExistsElse.ifExistsElse( + ContentFactory.all('tag').getList({ + ordering: 'name', + search: tagVal, + types: 'core_section' + }), + {name: tagVal}, + function (tag) { scope.article.tags.push(tag); }, + function () { console.log('Can\'t create sections.'); }, + function (data, status) { Raven.captureMessage('Error Adding Section', {extra: data}); } + ); + $(input).val(''); + }; + + scope.delete = function (e) { + var tag = $(e.target).parents('[data-taglikeobject]').data('taglikeobject'); + var name = tag.name; + var newtags = []; + for (var i in scope.article.tags) { + if (scope.article.tags[i].name !== name) { + newtags.push(scope.article.tags[i]); + } + } + scope.article.tags = newtags; + }; + + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('slideshowPane', function ($http, $window, $compile, $) { + return { + restrict: 'E', + templateUrl: '/views/slideshow-pane.html', + scope: { + article: '=', + image: '=', + index: '=' + }, + link: function (scope, element, attrs) { + var $element = $(element); + + if (attrs.caption === 'false') { + scope.hideCaption = true; + } + + scope.format = attrs.format || 'jpg'; + scope.crop = attrs.crop || '16x9'; + + scope.removeImage = function (index) { + scope.article.slides.splice(index, 1); + }; + + scope.editImage = function (index) { + var loadingClass = 'loading-class'; + + $window.openImageDrawer( + scope.article.slides[index].id, + function (data) { + function removeLoadingGif() { + $element.find('.image .' + loadingClass).remove(); + } + + removeLoadingGif(); + + if ($element.find('.image').data('imageId') === data.id) { + return; + } + + $element.find('.image img').on('load', removeLoadingGif); + $element.find('.image img').after( + '' + ); + + scope.article.slides[index].id = data.id.toString(); + scope.$apply(); + $window.picturefill(); + if ($element.find('.image img')[0].complete) { removeLoadingGif(); } + }, + function () { return; }, + function (oldImage) { + scope.article.slides[index] = oldImage; + $window.picturefill(); + } + ); + }; + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('tagsField', function (_, IfExistsElse, ContentFactory, Raven, $) { + return { + templateUrl: '/views/taglike-autocomplete-field.html', + restrict: 'E', + scope: { + article: '=' + }, + replace: true, + link: function postLink(scope, element, attrs) { + scope.name = 'tag'; + scope.label = 'Tags'; + scope.placeholder = 'Enter a tag'; + scope.resourceUrl = '/cms/api/v1/tag/?ordering=name&types=content_tag&search='; + scope.display = function (o) { + return o.name; + }; + + scope.$watch('article.tags', function () { + scope.objects = _.where(scope.article.tags, {type: 'content_tag'}); + }, true); + + scope.add = function (o, input, freeForm) { + var tagVal = freeForm ? o : o.name; + IfExistsElse.ifExistsElse( + ContentFactory.all('tag').getList({ + ordering: 'name', + search: tagVal + }), + {name: tagVal}, + function (tag) { scope.article.tags.push(tag); }, + function (value) { scope.article.tags.push({name: value.name, type: 'content_tag', new: true}); }, + function (data, status) { Raven.captureMessage('Error Adding Tag', {extra: data}); } + ); + $(input).val(''); + }; + + scope.delete = function (e) { + var tag = $(e.target).parents('[data-taglikeobject]').data('taglikeobject'); + var name = tag.name; + var newtags = []; + for (var i in scope.article.tags) { + if (scope.article.tags[i].name !== name) { + newtags.push(scope.article.tags[i]); + } + } + scope.article.tags = newtags; + }; + + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('targeting', function () { + return { + restrict: 'E', + templateUrl: '/views/targeting.html', + link: function (scope, element, attrs) { + scope.addTargetingRow = function (index) { + scope.targetingArray.push([]); + }; + scope.removeTargetingRow = function (index) { + scope.targetingArray.splice(index, 1); + }; + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp').directive( + 'videoUpload', + function ($http, $window, $timeout, $sce, $) { + return { + templateUrl: '/views/mainvideo.html', + scope: { + 'article': '=' + }, + restrict: 'E', + link: function (scope, element, attrs) { + console.log('video upload here'); + console.log(scope.video_id); + scope.$watch('article.video', function () { + if (scope.article.video) { + scope.embedUrl = $sce.trustAsUrl('/video/embed?id=' + scope.article.video); + $http({ + method: 'GET', + url: '/videos/api/video/' + scope.article.video + '/' + }).success(function (data) { + console.log('getting video from API'); + console.log(data); + scope.video = data; + $window.initVideoWidget(data.id); + }); + } + }); + + scope.$watch('video', function () { + + }); + + var progressEl = element.find('div.progress'); + var progressBar = element.find('div.progress-bar'); + var fakeInput = element.find('input.fake-input'); + scope.lastProgress = 0; + + scope.addVideo = function () { + console.log('chooseFile'); + $window.uploadVideo(element.find('.video-container')[0], { + onSuccess: function (videoid) { + scope.$apply(function () { + console.log('addVideo onSuccess callback'); + console.log(videoid); + scope.article.video = videoid; + }); + }, + onError: function (data) { + console.log('addVideo onError callback'); + console.log(data); + }, + onProgress: function (data) { + console.log('addVideo onProgress callback'); + console.log(data); + } + }); + }; + + scope.clearVideo = function (areYouSure) { + if (areYouSure) { + $('#s3upload-file-input').val(''); + scope.article.video = null; + } else { + $('#confirm-clear-video-modal').modal('show'); + } + }; + + function abortUpload() { + setProgress(0); + if (scope.req) { + scope.req.abort(); + } + scope.video = {}; + setProgress(0); + } + + function abortEncode() { + $.ajax('https://app.zencoder.com/api/v2/jobs/' + scope.video.job_id + '/cancel.json?api_key=' + $window.videoAttrs.zencoderApiKey, { + type: 'PUT', + success: function (data) { + scope.video.status = 3; + fakeInput.val('Encoding failed! Please try again.'); + } + }); + } + + scope.abort = function () { + if (scope.encoding) { + abortEncode(); + return; + } else { + abortUpload(); + return; + } + }; + + function setProgress(progress) { + if (progress === 0 || progress === 100) { + progressEl.hide(); + return; + } + if (scope.lastProgress === 0 || Math.abs(progress - scope.lastProgress) > 2) { + progressBar.css('width', Math.floor(progress) + '%'); + scope.lastProgress = progress; + progressEl.show(); + } + } + + function updateEncodeProgress() { + progressBar.addClass('progress-bar-success'); + + delete $http.defaults.headers.common['X-Requested-With']; + $http({ + url: 'https://app.zencoder.com/api/v2/jobs/' + scope.video.job_id + '/progress.json', + method: 'GET', + params: { + api_key: $window.videoAttrs.zencoderApiKey + }, + useXDomain: true + }).success(function (data) { + if (data.state === 'waiting' || data.state === 'pending' || data.state === 'processing') { + scope.video.status = 2; + if (data.progress > 5) { + setProgress(data.progress); + $timeout(updateEncodeProgress, 500); + } else { + $timeout(updateEncodeProgress, 2000); + } + } else { + setProgress(0); + if (data.state === 'finished') { + scope.video.status = 1; + } + if (data.state === 'failed' || data.state === 'cancelled') { + scope.video.status = 3; + fakeInput.val('Encoding failed! Please try again.'); + } + } + }).error(function (data) { + $('.alert-danger').fadeIn().delay(1000).fadeOut(); + }); + } + + var initialCheckRan = false; + scope.$watch('video', function () { + if (scope.video && scope.video.job_id && !initialCheckRan) { + updateEncodeProgress(); + initialCheckRan = true; + } + }); + + + } + + + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('videoEmbed', function (CmsConfig) { + return { + template: '
    ', + restrict: 'E', + link: function postLink(scope, element, attrs) { + scope.videoUrl = CmsConfig.buildVideoUrl(attrs.videoId); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('videoField', function (Zencoder) { + return { + templateUrl: '/views/video-field.html', + restrict: 'E', + scope: { + article: '=' + }, + link: function postLink(scope, element, attrs) { + scope.removeVideo = function () { + scope.article.video = null; + }; + + scope.uploadVideo = function () { + Zencoder.onVideoFileUpload().then( + function (success) { + console.log(success); + scope.article.video = success.attrs.id; + }, + angular.noop, + function (progress) { + console.log(progress); + scope.uploadProgress = progress; + } + ); + }; + + scope.thumbnailModal = function () { + Zencoder.openVideoThumbnailModal(scope.article.video).result.then( + function (resolve) { + console.log('thumbnail modal resolve'); + console.log(resolve); + //article.poster_url = resolve; + }, + function (reject) { + console.log('thumbnail modal rejected'); + } + ); + }; + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('autocompleteMenu', function ($timeout, $animate, $compile) { + return { + restrict: 'E', + replace: true, + transclude: true, + controller: angular.noop, + scope: { + items: '=items', + pIndex: '=index', + select: '&select', + }, + link: function ($scope, element, attrs) { + + $scope.selectItem = function (index) { + $scope.select(index); + } + + $scope.setIndex = function (index) { + $scope.index = index; + + if (attrs.index) { + $scope.pIndex = parseInt(index, 10); + } + } + + if (attrs.index) { + $scope.$watch('pIndex', function(value){ + $scope.index = parseInt(value, 10); + }) + } + + $scope.label = function(index) { + var viewValue = $scope.items[index][attrs.labelAttr]; + if (typeof(viewValue) === "function") { + viewValue = viewValue(); + } + return viewValue; + } + + }, + template: '' + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .directive('autocomplete', function ($timeout, $animate, $compile) { + return { + restrict: 'E', + replace: true, + require: 'ngModel', + transclude: true, + controller: function ($scope, $element, $attrs, $injector) { + $scope.service = $injector.get($attrs.service); + $scope.placeholder = $attrs.placeholder || ''; + }, + link: function ($scope, element, attrs, ngModel, transclude) { + + var isMenuAppended = false; + var inputEl = element.find('input'); + var timeoutId = null; + + ngModel.$render = function() { + if (ngModel.$viewValue) { + var viewValue = ngModel.$viewValue[attrs.labelAttr]; + if (typeof(viewValue) === "function") { + viewValue = viewValue(); + } + element.find('input').val(viewValue); + inputEl.attr('disabled', 'disabled'); + } + } + + $scope.openMenu = function(e) { + inputEl.removeAttr('disabled'); + inputEl[0].focus(); + } + + inputEl.on('blur keyup change', function() { + if (inputEl.attr('disabled') !== undefined) { + return; + } + appendMenu(); + var value = inputEl.val(); + if (value) { + if (timeoutId) { + $timeout.cancel(timeoutId); + } + timeoutId = $timeout(function(){ queryData(value)}, 150); + } + }); + + var menuScope = $scope.$new(); + menuScope.items = []; + menuScope.index = 0; + menuScope.select = function(index) { + ngModel.$setViewValue(menuScope.items[index]); + reset(); + } + + var menuEl = angular.element(document.createElement('autocomplete-menu')); + menuEl.attr({ + 'items': 'items', + 'select': 'select(index)', + 'index': 'index', + 'label-attr': attrs.labelAttr, + }); + transclude(menuScope, function(clone){ menuEl.append(clone) }); + $compile(menuEl)(menuScope); + + element.find('input').on('keyup', function(e) { + switch(e.which) { + case 27: // ESC + if (inputEl.val() === '') { + reset(); + } else { + inputEl.val(''); + } + break; + case 40: // DOWN + $scope.$apply(function() { + menuScope.index = (menuScope.index + 1) % menuScope.items.length; + }); + break; + case 38: // UP + $scope.$apply(function() { + if(menuScope.index) { + menuScope.index = menuScope.index - 1; + } else { + menuScope.index = menuScope.items.length - 1; + } + }); + break; + case 13: // RETURN + if (menuScope.index >= 0) { + ngModel.$setViewValue(menuScope.items[menuScope.index]); + reset(); + } + break; + default: + return; + } + }); + + function queryData(query) { + var searchParams = {} + searchParams[attrs.searchParam || 'search'] = query; + $scope['service'].getList(searchParams).then(function (results) { + + if(results.length > 5) { + menuScope.items = results.slice(0, 5); + } else { + menuScope.items = results; + } + timeoutId = null; + }); + } + + function appendMenu() { + if (!isMenuAppended) { + isMenuAppended = true; + menuScope.index = 0; + $animate.enter(menuEl, element.parent(), element); + } + styleMenu(); + } + + function reset() { + ngModel.$render(); + menuScope.items = []; + menuScope.index = 0; + $animate.leave(menuEl).finally(function() { + isMenuAppended = false; + }); + } + + function styleMenu() { + var parentStyles = window.getComputedStyle(element[0]); + var offset = element.offset(); + + offset.left = 'auto'; + offset.right = 'auto'; + offset.top = element.outerHeight(); + offset.minWidth = element.outerWidth(); + + angular.forEach(offset, function (value, key) { + if (!isNaN(value) && angular.isNumber(value)) { + value = value + "px" + } + menuEl[0].style[key] = value; + menuEl.css('z-index', 1000); + }); + } + }, + templateUrl: '/views/autocomplete.html' + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('BadrequestmodalCtrl', function ($scope, $modalInstance, detail) { + $scope.detail = detail; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ChangelogmodalCtrl', function ($scope, $modalInstance, _, ContentFactory, article) { + $scope.article = article; + $scope.users = {}; + + ContentFactory.all('log').getList({content: article.id}).then(function (data) { + $scope.changelog = data; + + var userIds = _.unique(_.pluck(data, 'user')); + var resp = function (data) { + $scope.users[data.id] = data; + }; + + for (var i in userIds) { + ContentFactory.one('author', userIds[i]).get().then(resp); + } + }); + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('CmsNotificationCtrl', function ($scope, moment) { + + var valid = function () { + $scope.postDateValid = $scope.postDate && (!$scope.notifyEndDate || $scope.postDate < $scope.notifyEndDate); + $scope.notifyEndDateValid = $scope.notifyEndDate && $scope.postDate && $scope.notifyEndDate > $scope.postDate; + $scope.titleValid = $scope.cmsNotification.title && $scope.cmsNotification.title.length > 0 && $scope.cmsNotification.title.length <= 110; + + $scope.cmsNotificationValid = $scope.postDateValid && $scope.notifyEndDateValid && $scope.titleValid; + }; + + // Note: use middle man for handling dates since a bug in angular.js version causes moment to not work with + // angular.copy. So instead of keeping notification dates as moments, keep them as strings and use moment objects + // for interactions. + + $scope.postDate = $scope.cmsNotification.post_date ? moment($scope.cmsNotification.post_date) : null; + $scope.$watch('postDate', function () { + if ($scope.postDate) { + // set notification's post date as the string version of the moment object + $scope.cmsNotification.post_date = $scope.postDate.format(); + // automatically set the notify end date as 3 days after post date + $scope.notifyEndDate = $scope.postDate.clone().add({days: 3}); + } else { + $scope.cmsNotification.post_date = null; + } + }); + + $scope.notifyEndDate = $scope.cmsNotification.notify_end_date ? moment($scope.cmsNotification.notify_end_date) : null; + $scope.$watch('notifyEndDate', function () { + if ($scope.notifyEndDate) { + // set notification's post date as the string version of the moment object + $scope.cmsNotification.notify_end_date = $scope.notifyEndDate.format(); + } else { + $scope.cmsNotification.notify_end_date = null; + } + }); + + // keep track of changes to this notification + $scope.cmsNotificationDirty = false; + $scope.$watch('cmsNotification', function (newValue, oldValue) { + if (!angular.equals(newValue, oldValue)) { + $scope.cmsNotificationDirty = true; + + // do some validation here + valid(); + + } + }, true); + + // do initial validation + valid(); + + /** + * Save this notification using the parent scope. + */ + $scope.saveCmsNotification = function () { + + if ($scope.$parent.userIsSuperuser && $scope.cmsNotificationDirty && $scope.cmsNotificationValid) { + + $scope.$parent.$saveCmsNotification($scope.cmsNotification) + .then(function (newCmsNotification) { + $scope.cmsNotification = newCmsNotification; + $scope.cmsNotificationDirty = false; + }) + .catch(function (error) { + console.log('CMS Alert save failed', error); + }); + + } + + }; + + /** + * Delete this notification using the parent scope. + */ + $scope.deleteCmsNotification = function () { + + if ($scope.$parent.userIsSuperuser) { + + $scope.$parent.$deleteCmsNotification($scope.cmsNotification) + .catch(function (error) { + console.log('CMS Alert delete failed', error); + }); + + } + + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('CmsNotificationsCtrl', function ($q, $window, $scope, CmsConfig, + CmsNotificationsApi, CurrentUserApi, _, moment) { + + $window.document.title = CmsConfig.getCmsName() + ' | CMS Alerts'; + + // get user info + CurrentUserApi.getCurrentUserWithCache().then(function (user) { + if (user.is_superuser) { + $scope.userIsSuperuser = true; + } + + // get list of CMS notifications + CmsNotificationsApi.getList().then(function (cmsNotifications) { + // filter out CMS notifications for regular users that have a post date in the future + var removeIndicies = []; + _.each(cmsNotifications, function (cmsNotification, i) { + if (!user.is_superuser && moment(cmsNotification.post_date).isAfter(moment())) { + removeIndicies.push(i); + } + }); + _.each(removeIndicies, function (i) { + cmsNotifications.splice(i, 1); + }); + + $scope.cmsNotifications = cmsNotifications; + }); + }); + + /** + * Create a new notification, plain old object since we don't want to save invalid objects to the db. + * + * @return new notification with only nulled date properties. + */ + $scope.newCmsNotification = function () { + + var cmsNotification = { + post_date: null, + notify_end_date: null + }; + + $scope.cmsNotifications.unshift(cmsNotification); + + return cmsNotification; + + }; + + /** + * Save given notification to the database. + * + * @param notification Notification to save. + * @return promise that resolves when notification is saved. + */ + $scope.$saveCmsNotification = function (cmsNotification) { + + var saveDefer = $q.defer(), + savePromise = saveDefer.promise; + + if ($scope.userIsSuperuser) { + if ('id' in cmsNotification) { + // this thing already exists, update it + cmsNotification.put().then(function (updatedCmsNotification) { + saveDefer.resolve(updatedCmsNotification); + }); + } else { + // a new notification, post it to the list + $scope.cmsNotifications.post(cmsNotification) + .then(function (newCmsNotification) { + // save succeeded, replace notification with restangularized notification + var i = $scope.cmsNotifications.indexOf(cmsNotification); + $scope.cmsNotifications[i] = newCmsNotification; + saveDefer.resolve(newCmsNotification); + }) + .catch(function (error) { + saveDefer.reject(error); + }); + } + } else { + saveDefer.reject('Insufficient permissions.'); + } + + return savePromise; + + }; + + /** + * Delete given notification from the database. + * + * @param cmsNotification Notification to delete. + * @return promise that resolves when notification is deleted. + */ + $scope.$deleteCmsNotification = function (cmsNotification) { + + var deleteDefer = $q.defer(), + deletePromise = deleteDefer.promise, + removeFromList = function (index) { + $scope.cmsNotifications.splice(index, 1); + deleteDefer.resolve(); + }; + + if ($scope.userIsSuperuser) { + // find notification in list + var i = $scope.cmsNotifications.indexOf(cmsNotification); + if (i > -1) { + // notification in list, check if it is a restangular object and has a remove function + if (_.isFunction(cmsNotification.remove)) { + // has remove, call it and resolve promise + cmsNotification.remove() + .then(function () { + removeFromList(i); + }) + .catch(function (error) { + deleteDefer.reject(error); + }); + } else { + // does not have remove, this is a previously unsaved notification, just remove it from list + removeFromList(i); + } + } else { + deleteDefer.reject('Cannot find notification in notification list. Unable to delete.'); + } + } else { + deleteDefer.reject('You do not have sufficient permissions to delete a notification.'); + } + + return deletePromise; + + }; + + }); + +'use strict'; + +// note: this jshint exception is for URLify, which is just a function not a constructor +/* jshint newcap: false */ + +/** + * Controller for CMS notifications bar that is displayed to users. + */ +angular.module('bulbsCmsApp') + .controller('CmsNotifyContainerCtrl', function ($scope, ipCookie, moment, CmsNotificationsApi, URLify, _) { + + var genCookieKey = function (id) { + return 'dismissed-cms-notification-' + id; + }; + var updateCmsNotificationsDisplay = function (cmsNotifications) { + var now = moment(); + $scope.cmsNotifications = _.filter(cmsNotifications, function (cmsNotification) { + // show CMS notifications where there is no dismiss cookie and post_date < now < notify_end_date + if (!ipCookie(genCookieKey(cmsNotification.id)) && + moment(cmsNotification.post_date).isBefore(now) && + moment(cmsNotification.notify_end_date).isAfter(now)) { + return true; + } + }); + }; + + // show CMS notifications + CmsNotificationsApi.getList().then(function (cmsNotifications) { + updateCmsNotificationsDisplay(cmsNotifications); + }); + + $scope.dismissCmsNotification = function (cmsNotification) { + // add dismiss cookie + var cookieKey = URLify(genCookieKey(cmsNotification.id)); + ipCookie(cookieKey, true, { + expires: moment(cmsNotification.notify_end_date).add({days: 1}).diff(moment(), 'days'), + path: '/cms/app' + }); + + // hide notification + updateCmsNotificationsDisplay($scope.cmsNotifications); + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ContenteditCtrl', function ( + $scope, $routeParams, $http, $window, + $location, $timeout, $interval, $compile, $q, $modal, + $, _, CmsConfig, moment, keypress, Raven, PNotify, + IfExistsElse, VersionStorageApi, ContentFactory, FirebaseApi, + FirebaseArticleFactory, Login, VersionBrowserModalOpener) + { + var listener = new keypress.Listener(); + listener.simple_combo('cmd s', function (e) { $scope.saveArticle(); }); + listener.simple_combo('ctrl s', function (e) { $scope.saveArticle(); }); + + var saveHTML = ' Save'; + var navbarSave = '.navbar-save'; + + var getArticleCallback = function (data) { + $window.article = $scope.article = data; //exposing article on window for debugging + + $scope.last_saved_article = angular.copy(data); + + FirebaseApi.$connection + .onConnect(function () { + $scope.firebaseConnected = true; + }) + .onDisconnect(function () { + $scope.firebaseConnected = false; + }); + + // get article and active users, register current user as active + FirebaseArticleFactory + .$retrieveCurrentArticle() + .then(function ($article) { + + var $activeUsers = $article.$activeUsers(), + $versions = $article.$versions(), + currentUser, + savePNotify; + + $versions.$loaded(function () { + $versions.$watch(function (e) { + if (e.event === 'child_added') { + + // order versions newest to oldest then grab the top one which should be the new version + var newVersion = _.sortBy($versions, function (version) { + return -version.timestamp; + })[0]; + + if (currentUser && newVersion.user.id !== currentUser.id) { + + // close any existing save pnotify + if (savePNotify) { + savePNotify.remove(); + } + + var msg = '' + + newVersion.user.displayName + + ' -- ' + + moment(newVersion.timestamp).format('MMM Do YYYY, h:mma') + + '
    '; + if ($scope.articleIsDirty) { + msg += ' You have unsaved changes that may conflict when you save.'; + } + msg += ' Open the version browser to see their latest version.'; + + // this isn't the current user that saved, so someone else must have saved, notify this user + savePNotify = new PNotify({ + title: 'Another User Saved!', + text: msg, + type: 'error', + mouse_reset: false, + hide: false, + confirm: { + confirm: true, + buttons: [{ + text: 'Open Version Browser', + addClass: 'btn-primary', + click: function (notice) { + notice.mouse_reset = false; + notice.remove(); + VersionBrowserModalOpener.open($scope, $scope.article); + } + }, { + addClass: 'hide' + }] + }, + buttons: { + closer_hover: false, + sticker: false + } + }); + } + } + }); + }); + + // register a watch on active users so we can update the list in real time + $activeUsers.$watch(function () { + + // unionize user data so that we don't have a bunch of the same users in the list + $scope.activeUsers = + _.chain($activeUsers) + // group users by their id + .groupBy(function (user) { + return user.id; + }) + // take first user in grouping and use that data along with a count of the number of times they show + // up in the list (number of sessions they have running) + .map(function (group) { + var groupedUser = group[0]; + groupedUser.count = group.length; + + if (currentUser && groupedUser.id === currentUser.id) { + groupedUser.displayName = 'You'; + } + + return groupedUser; + }) + // sort users by their display names + .sortBy(function (user) { + return user.displayName === 'You' ? '' : user.displayName; + }) + // now we have a list of unique users along with the number of sessions they have running, sorted by + // their display names + .value(); + + }); + + // register current user active with this article + $article.$registerCurrentUserActive() + .then(function (user) { + currentUser = user; + }); + + // who knows what kind of promises you might have in the future? so return the article object for chains + return $article; + + }); + + }; + + var getContent = function () { + return ContentFactory.one('content', $routeParams.id) + .get() + .then(getArticleCallback); + }; + + var saveArticleErrorCbk = function (data) { + $(navbarSave) + .removeClass('btn-success') + .addClass('btn-danger') + .html(' Error'); + if (status === 400) { + $scope.errors = data; + } + $scope.saveArticleDeferred.reject(); + }; + + /** + * Last thing to happen on a successful save. + */ + var saveArticleSuccessCbk = function (resp) { + // store a version with version api + VersionStorageApi.$create($scope.article, $scope.articleIsDirty); + + $(navbarSave).html(' Saved!'); + setTimeout(function () { + $(navbarSave).html(saveHTML); + }, 2500); + $window.article = $scope.article = resp; + $scope.last_saved_article = angular.copy(resp); + $scope.articleIsDirty = false; + $scope.articleIsNew = false; + $scope.errors = null; + + if ($routeParams.id === 'new') { + $location.path('/cms/app/edit/' + $scope.article.id + '/' + $routeParams.contentType); + } + + $location.search('rating_type', null); //maybe just kill the whole query string with $location.url($location.path()) + $scope.saveArticleDeferred.resolve(resp); + }; + + $scope.PARTIALS_URL = '/views/'; + $scope.buildContentPartialsPath = CmsConfig.buildContentPartialsPath; + $scope.page = 'edit'; + $scope.contentEditGlobals = { + canSave: true + }; + + $scope.$watch('article.title', function () { + $window.document.title = CmsConfig.getCmsName() + ' | Editing ' + ($scope.article && $('' + $scope.article.title + '').text()); + }); + + $scope.saveArticleDeferred = $q.defer(); + + $scope.saveArticleIfDirty = function () { + /*this is only for operations that trigger a saveArticle (e.g. send to editor) + if the article isn't dirty, we don't want to fire saveArticle + and possibly trigger the last-modified-guard or whatever else*/ + if ($scope.articleIsDirty) { + return $scope.saveArticle(); + } else { + //resolves immediately with article as the resolved value + //(saveArticle resolves to article as well) + return $q.when($scope.article); + } + }; + + $scope.saveArticle = function () { + if ($scope.contentEditGlobals.saveError) { + return $scope.saveArticleDeferred.reject(); + } + + $(navbarSave) + .removeClass('btn-danger') + .addClass('btn-success') + .html(' Saving'); + + if ($routeParams.id === 'new') { + $scope.postValidationSaveArticle(); + } else { + ContentFactory.one('content', $routeParams.id).get() + .then(function (data) { + if (data.last_modified && + $scope.article.last_modified && + moment(data.last_modified) > moment($scope.article.last_modified)) { + $scope.saveArticleDeferred.reject(); + $modal.open({ + templateUrl: '/views/modals/last-modified-guard-modal.html', + controller: 'LastmodifiedguardmodalCtrl', + scope: $scope, + resolve: { + articleOnPage: function () { return $scope.article; }, + articleOnServer: function () { return data; } + } + }); + } else { + $scope.postValidationSaveArticle(); + } + }) + .catch(saveArticleErrorCbk); + } + + return $scope.saveArticleDeferred.promise; + }; + + $scope.postValidationSaveArticle = function () { + if ($scope.article.status !== 'Published' && + $scope.article.polymorphic_ctype !== CmsConfig.getSuperFeaturesType()) { + $scope.article.slug = $window.URLify($scope.article.title, 50); + } + + var params = {}; + if ($routeParams.id === 'new') { + params['doctype'] = $scope.article.polymorphic_ctype; + } + + $scope.article.save(params) + .then(saveArticleSuccessCbk) + .catch(saveArticleErrorCbk); + + return $scope.saveArticleDeferred.promise; + }; + + // keep track of if article is dirty or not + $scope.articleIsDirty = $routeParams.id === 'new'; + $scope.$watch(function () { + return !angular.equals($scope.article, $scope.last_saved_article); + }, function (isDirty) { + $scope.articleIsDirty = isDirty; + }); + + $scope.$watch('articleIsDirty', function () { + if ($scope.articleIsDirty) { + $window.onbeforeunload = function () { + return 'You have unsaved changes. Do you want to continue?'; + }; + } else { + $window.onbeforeunload = function () {}; + } + }); + + $scope.publishSuccessCbk = function () { + return getContent(); + }; + + $scope.trashSuccessCbk = function () { + //delaying this so the user isn't sent back before the trashed content is removed from the listing view + $timeout(function () { + $window.history.back(); + }, 1500); + }; + + var initialize = function () { + if ($routeParams.id === 'new') { + $scope.articleIsNew = true; + $scope.article = ContentFactory.oneUrl('content'); + $scope.article['polymorphic_ctype'] = $routeParams.contentType; + } else { + getContent(); + } + }; + + initialize(); + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ContentlistCtrl', function ( + $scope, $http, $timeout, $location, $window, $q, $, CmsConfig, + ContentListService) { + + $scope.contentData = []; + ContentListService.$updateContent({ + page: 1, + exclude: CmsConfig.getSuperFeaturesType() + }) + .then(function (data) { + $scope.contentData = data; + }); + + $window.document.title = CmsConfig.getCmsName() + ' | Content'; + + $scope.pageNumber = $location.search().page || '1'; + $scope.myStuff = false; + $scope.search = $location.search().search; + $scope.collapse = {}; + + $scope.goToPage = function () { + ContentListService.$updateContent({page: $scope.pageNumber}, true); + }; + + $scope.publishSuccessCbk = function (data) { + var i; + for (i = 0; i < $scope.contentData.content.length; i++) { + if ($scope.contentData.content[i].id === data.article.id) { + break; + } + } + + for (var field in data.response) { + $scope.contentData.content[i][field] = data.response[field]; + } + + return $q.when(); + }; + + $scope.trashSuccessCbk = function () { + $timeout(function () { + ContentListService.$updateContent(); + $('#confirm-trash-modal').modal('hide'); + }, 1500); + }; + + $('body').on('shown.bs.collapse', '.panel-collapse', function (e) { + $scope.$digest(); + }); + + }) + .directive('ngConfirmClick', [ // Used on the unpublish button + function () { + return { + link: function (scope, element, attr) { + var msg = attr.ngConfirmClick || 'Are you sure?'; + var clickAction = attr.confirmedClick; + element.bind('click', function () { + if (window.confirm(msg)) { + scope.$eval(clickAction); + } + }); + } + }; + } + ]); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ContentworkflowCtrl', function ($scope, $http, $modal, $window, moment, + VersionBrowserModalOpener, TemporaryUrlModalOpener, + CmsConfig) { + $scope.TIMEZONE_LABEL = moment.tz(CmsConfig.getTimezoneName()).format('z'); + + $scope.trashContentModal = function (articleId) { + return $modal.open({ + templateUrl: '/views/modals/confirm-trash-modal.html', + controller: 'TrashcontentmodalCtrl', + scope: $scope, + resolve: { + articleId: function () { + return articleId; + } + } + }); + }; + + $scope.pubTimeModal = function (article) { + return $modal.open({ + templateUrl: '/views/modals/publish-date-modal.html', + controller: 'PubtimemodalCtrl', + scope: $scope, + resolve: { + article: function () { return article; } + } + }); + }; + + $scope.sendToEditorModal = function (article) { + return $modal.open({ + templateUrl: '/views/modals/send-to-editor-modal.html', + controller: 'SendtoeditormodalCtrl', + scope: $scope, + resolve: { + article: function () { return article; } + } + }); + }; + + $scope.changelogModal = function (article) { + return $modal.open({ + templateUrl: '/views/modals/changelog-modal.html', + controller: 'ChangelogmodalCtrl', + scope: $scope, + resolve: { + article: function () { return article; } + } + }); + }; + + $scope.thumbnailModal = function (article) { + // open thumbnail modal along with its controller + return $modal.open({ + templateUrl: '/views/modals/thumbnail-modal.html', + controller: 'ThumbnailModalCtrl', + scope: $scope, + resolve: { + article: function () { return article; } + } + }); + }; + + $scope.versionBrowserModal = function (article) { + VersionBrowserModalOpener.open($scope, article); + }; + + $scope.temporaryUrlModal = function (article) { + TemporaryUrlModalOpener.open($scope, article); + }; + + $scope.descriptionModal = function (article) { + return $modal.open({ + templateUrl: '/views/modals/description-modal.html', + controller: 'DescriptionModalCtrl', + scope: $scope, + size: 'lg', + resolve: { + article: function () { return article; } + } + }); + }; + + $scope.getStatus = function (article) { + if (!article || !article.published) { + return 'unpublished'; + } else if (moment(article.published) > moment()) { + return 'scheduled'; + } else { + return 'published'; + } + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ContributionsCtrl', function ( + $scope, $routeParams, $http, $window, + $location, $timeout, $compile, $q, $modal, + _, CmsConfig, ContributionRoleService, ContentService) + { + + $scope.NAV_LOGO = CmsConfig.getNavLogoPath(); + $scope.contentId = parseInt($routeParams.id, 10); + $scope.paymentType = ''; + $scope.contributions = []; + $scope.contributionLabels = []; + $scope.roles = []; + $scope.collapsed = []; + $scope.page = 'contributions'; + + $scope.clean = true; + + $scope.save = save; + $scope.add = add; + $scope.remove = remove; + $scope.updateLabel = updateLabel; + + $scope.isFlatRate = function(contribution) { + if (contribution.hasOwnProperty('roleObject')) { + if (contribution.roleObject.payment_type === 'Flat Rate'){ + return true; + } + } + + return false; + }; + + $scope.isFeatureType = function(contribution) { + if (contribution.hasOwnProperty('roleObject')) { + if (contribution.roleObject.payment_type === 'FeatureType'){ + $scope.setFeatureRate(contribution); + return true; + } + } + + return false; + }; + + $scope.setFeatureRate = function(contribution) { + for (var i in contribution.roleObject.rates.feature_type) { + var featureTypeRate = contribution.roleObject.rates.feature_type[i]; + if ($scope.content.feature_type === featureTypeRate.feature_type) { + contribution.featureTypeRate = featureTypeRate.rate; + } + } + }; + + $scope.isHourly = function(contribution) { + if (contribution.hasOwnProperty('roleObject')) { + if (contribution.roleObject.payment_type === 'Hourly') { + return true; + } + } + + return false; + }; + + $scope.isManual = function(contribution) { + if (contribution.hasOwnProperty('roleObject')) { + if (contribution.roleObject.payment_type === 'Manual') { + return true; + } + } + + return false; + }; + + $scope.getHourlyPay = function (contribution) { + if (contribution.roleObject) { + if (!contribution.roleObject.rate) { + return 0; + } + return ((contribution.roleObject.rate/60) * (contribution.minutes_worked || 0)); + } + }; + + function save() { + // I know, I'm not supposed to do DOM manipulation in controllers. TOO BAD. + angular.element('#save-btn').html(' Saving'); + $scope.contributions.save($scope.contributions).then(function (contributions) { + angular.element('#save-btn').addClass('btn-success').removeClass('btn-danger'); + angular.element('#save-btn').html(' Save'); + $scope.clean = true; + }, function(res) { + angular.element('#save-btn').addClass('btn-danger').removeClass('btn-success'); + angular.element('#save-btn').html(' Error'); + }); + } + + function add() { + $scope.contributions.push({ + contributor: null, + content: $scope.contentId, + rate: { + rate: 0 + }, + role: null + }); + $scope.collapsed.push(false); + } + + $scope.$watch('contributions', function(newContributions, oldContributions) { + if (oldContributions.length > 0) { + $scope.clean = false; + } + }, true); + + function getRoles() { + return ContributionRoleService.getList().then(function (roles) { + $scope.roles = roles; + getContributions(); + }); + } + + function getContributions() { + return ContentService.one($scope.contentId).all('contributions').getList().then(function (contributions) { + for (var i in contributions) { + if (contributions[i] === null || contributions[i].role === undefined) { + continue; + } else { + + if ((contributions[i].hasOwnProperty('rate')) && + (typeof(contributions[i].rate) === 'object') && + (contributions[i].rate !== null)) { + contributions[i].rate = contributions[i].rate.rate; + } + + if (typeof(contributions[i].role) === 'object') { + contributions[i].paymentType = contributions[i].role.payment_type; + contributions[i].roleObject = contributions[i].role; + contributions[i].role = contributions[i].role.id; + } + } + } + $scope.contributions = contributions; + $scope.collapsed = new Array(contributions.length); + $scope.contributions.forEach(function (item, index) { + + $scope.contributionLabels[index] = _.find($scope.roles, function (role) { + return role.id === item.role; + }).name; + $scope.collapsed[index] = true; + }); + }); + } + + function getContent() { + ContentService.one($scope.contentId).get().then(function (content) { + $scope.content = content; + $scope.article = { + id: content.id + }; + }); + } + + function remove(index) { + $scope.contributions.splice(index, 1); + $scope.collapsed.splice(index, 1); + } + + function updateLabel(index) { + $scope.contributionLabels[index] = _.find($scope.roles, function (role) { + + $scope.contributions[index].roleObject = role; + $scope.contributions[index].paymentType = role.payment_type; + return role.id === $scope.contributions[index].role; + }).name; + } + + getRoles(); + getContent(); + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('DescriptionModalCtrl', function ($scope, $modalInstance, article) { + $scope.article = article; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ForbiddenmodalCtrl', function ($scope, detail) { + $scope.detail = detail; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('LastmodifiedguardmodalCtrl', function ($scope, $modalInstance, _, moment, ContentFactory, articleOnPage, articleOnServer) { + $scope.articleOnServer = articleOnServer; + + ContentFactory.all('log').getList({content: articleOnPage.id}).then(function (log) { + var latest = _.max(log, function (entry) { return moment(entry.action_time); }); + var lastSavedById = latest.user; + ContentFactory.one('author', lastSavedById).get().then(function (data) { + $scope.lastSavedBy = data; + }); + }); + + $scope.loadFromServer = function () { + + // pull article from server and replace whatever data we need to show the newest version + _.each($scope.articleOnServer, function (value, key) { + $scope.article[key] = value; + }); + $scope.articleIsDirty = true; + + $modalInstance.close(); + + }; + + $scope.saveAnyway = function () { + $modalInstance.close(); + $scope.$parent.postValidationSaveArticle(); + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('LineitemexportmodalCtrl', function ($scope, $http, CmsConfig, moment) { + var now = moment().tz(CmsConfig.getTimezoneName()); + $scope.start = moment([now.year(), now.month()]); + $scope.end = moment([now.year(), now.month() + 1]); + + $scope.apiUrl = '/cms/api/v1/contributions/line-item-reporting/?format=csv'; + + $scope.updateDownloadUrl = function () { + var start_string = $scope.start.format('YYYY-MM-DD'); + var end_string = $scope.end.format('YYYY-MM-DD'); + return $scope.apiUrl + '&start=' + start_string + '&end=' + end_string; + }; + + $scope.downloadUrl = $scope.updateDownloadUrl(); + + $scope.$watchCollection('[start, end]', function () { + $scope.downloadUrl = $scope.updateDownloadUrl(); + }); + + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('LoginmodalCtrl', function ($scope, Login, $modalInstance, $) { + $scope.login = function () { + var username = $('input[name=\'username\']').val(); + var password = $('input[name=\'password\']').val(); + Login.login(username, password).then( + function () { $modalInstance.close(); }, + function () { $modalInstance.dismiss(); } + ); + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('PubtimemodalCtrl', function ($scope, $http, $modal, + $modalInstance, $, CmsConfig, moment, Login, article, Raven) { + + $scope.article = article; + + $scope.pubButton = { + idle: 'Publish', + busy: 'Publishing', + finished: 'Published!', + error: 'Error!' + }; + + $scope.$watch('pickerValue', function (newVal) { + var pubTimeMoment = moment(newVal); + $scope.datePickerValue = moment() + .year(pubTimeMoment.year()) + .month(pubTimeMoment.month()) + .date(pubTimeMoment.date()); + $scope.timePickerValue = moment() + .hour(pubTimeMoment.hour()) + .minute(pubTimeMoment.minute()); + }); + + var modelDateFormat = 'YYYY-MM-DDTHH:mmZ'; + + $scope.setTimeShortcut = function (shortcut) { + if (shortcut === 'now') { + var now = moment(); + $scope.pickerValue = now; + } + if (shortcut === 'midnight') { + var midnight = moment().hour(24).minute(0); + $scope.pickerValue = midnight; + } + }; + + $scope.setDateShortcut = function (shortcut) { + var today = moment.tz(CmsConfig.getTimezoneName()); + if (shortcut === 'today') { + $scope.datePickerValue = moment().year(today.year()).month(today.month()).date(today.date()); + } + if (shortcut === 'tomorrow') { + $scope.datePickerValue = moment().year(today.year()).month(today.month()).date(today.date() + 1); + } + }; + + $scope.setPubTime = function () { + //we're planning on making feature_type a db required field + //but for now we're just validating on the front-end on publish + if (!$scope.article.feature_type) { + $modalInstance.dismiss(); + $modal.open({ + templateUrl: '/views/modals/pubtime-validation-modal.html' + }); + return; + } + + var newDate = moment($scope.datePickerValue); + var newTime = moment($scope.timePickerValue); + var newDateTime = moment.tz(CmsConfig.getTimezoneName()) + .year(newDate.year()) + .month(newDate.month()) + .date(newDate.date()) + .hour(newTime.hour()) + .minute(newTime.minute()) + .format(modelDateFormat); + var data = {published: newDateTime}; + + return $http({ + url: '/cms/api/v1/content/' + $scope.article.id + '/publish/', + method: 'POST', + data: data + }); + }; + + $scope.setPubTimeCbk = function (publish_promise) { + publish_promise + .then(function (result) { + $scope.article.published = result.data.published; + if ($scope.publishSuccessCbk) { + $scope.publishSuccessCbk({article: $scope.article, response: result.data}); + } + $modalInstance.close(); + }) + .catch(function (reason) { + Raven.captureMessage('Error Setting Pubtime', {extra: reason.data}); + $modalInstance.dismiss(); + }); + }; + + $scope.unpubButton = { + idle: 'Unpublish', + busy: 'Unpublishing', + finished: 'Unpublished!', + error: 'Error' + }; + + + $scope.unpublish = function () { + return $http({ + url: '/cms/api/v1/content/' + $scope.article.id + '/publish/', + method: 'POST', + data: {published: false} + }); + }; + + $scope.unpublishCbk = function (unpub_promise) { + unpub_promise + .then(function (result) { + if ($scope.publishSuccessCbk) { + $scope.publishSuccessCbk({article: $scope.article, response: result.data}); + } + $modalInstance.close(); + }) + .catch(function (reason) { + if ($scope.publishSuccessCbk) { + $scope.publishSuccessCbk({article: $scope.article, response: reason.data}); + } + $modalInstance.dismiss(); + }); + }; + + if ($scope.article.published) { + $scope.pickerValue = moment.tz($scope.article.published, CmsConfig.getTimezoneName()); + } else { + $scope.setTimeShortcut('now'); + } + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ReportemailmodalCtrl', function ($scope, $http, moment) { + var reportEmailURL = '/cms/api/v1/contributor-email/'; + var now = moment().tz('America/Chicago'); + + $scope.monthOptions = moment.monthsShort(); + $scope.reportDeadline = now.add(1, 'days'); + $scope.reportMonth = $scope.monthOptions[now.month() - 1]; + $scope.reportYear = now.year(); + + $scope.openReportDeadline = function($event) { + $event.preventDefault(); + $event.stopPropagation(); + $scope.startReportDeadline = true; + }; + + var getReportStart = function() { + return moment().month($scope.reportMonth).year($scope.reportYear).startOf('month'); + }; + + $scope.sendEmail = function () { + var data = { + deadline: $scope.reportDeadline, + start: getReportStart() + }; + $http.post(reportEmailURL, data); + }; + }); +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ReportingCtrl', function ($http, $scope, $modal, $window, $, + $location, $filter, $interpolate, Login, moment, CmsConfig, + ContributionReportingService, ContentReportingService, + FreelancePayReportingService) { + + $window.document.title = CmsConfig.getCmsName() + ' | Reporting'; + + $scope.userFilter = ''; + $scope.userFilters = [ + { + name: 'All', + value: '' + }, + { + name: 'Staff', + value: 'staff' + }, + { + name: 'Freelance', + value: 'freelance' + } + ]; + + $scope.publishedFilter = ''; + $scope.publishedFilters = [ + { + name: 'All Content', + value: '' + }, + { + name: 'Published', + value: 'published' + } + ]; + + $scope.reports = { + 'Contributions': { + service: ContributionReportingService, + headings: [ + {'title': 'Content ID', 'expression': 'content.id'}, + {'title': 'Headline', 'expression': 'content.title'}, + {'title': 'FeatureType', 'expression': 'content.feature_type'}, + {'title': 'Contributor', 'expression': 'user.payroll_name'}, + {'title': 'Role', 'expression': 'role'}, + {'title': 'Pay', 'expression': 'pay'}, + {'title': 'Date', 'expression': 'content.published | date: \'MM/dd/yyyy\''} + ], + downloadURL: '/cms/api/v1/contributions/reporting/', + orderOptions: [ + { + label: 'Order by User', + key: 'user' + }, + { + label: 'Order by Content', + key: 'content' + }, + ] + }, + 'Content': { + service: ContentReportingService, + headings: [ + {'title': 'Content ID', 'expression': 'id'}, + {'title': 'Headline', 'expression': 'title'}, + {'title': 'Feature Type', 'expression': 'feature_type'}, + {'title': 'Video', 'expression': 'video_id'}, + {'title': 'Article Cost', 'expression': 'value'}, + {'title': 'Date Published', 'expression': 'published | date: \'MM/dd/yyyy\''} + ], + orderOptions: [], + downloadURL: '/cms/api/v1/contributions/contentreporting/', + }, + 'Freelance Pay': { + service: FreelancePayReportingService, + headings: [ + {'title': 'Contributor', 'expression': 'contributor.full_name'}, + {'title': 'Contribution #', 'expression': 'contributions_count'}, + {'title': 'Pay', 'expression': 'pay'}, + {'title': 'Payment Date', 'expression': 'payment_date | date: \'MM/dd/yyyy\''} + ], + orderOptions: [], + downloadURL: '/cms/api/v1/contributions/freelancereporting/' + } + }; + $scope.items = []; + $scope.headings = []; + $scope.orderOptions = []; + + $scope.startInitial = moment().startOf('month').format('YYYY-MM-DD'); + $scope.endInitial = moment().endOf('month').format('YYYY-MM-DD'); + + $scope.reportParams = { + pageNumber: 1, + start: $scope.startInitial, + end: $scope.endInitial, + }; + $scope.reportDisabled = true; + + $scope.pageTotal = null; + $scope.moreFilters = []; + + $scope.startOpen = false; + $scope.endOpen = false; + + $scope.setReport = function ($reportingService) { + if ($scope.reportDisabled === true) { + $scope.reportDisabled = false; + } + $scope.reportParams.pageNumber = 1; + $scope.report = $reportingService; + }; + + $scope.setUserFilter = function (value) { + $scope.userFilter = value; + loadReport($scope.report, $scope.reportParams.start, $scope.reportParams.end, $scope.orderBy); + }; + + $scope.setPublishedFilter = function (value) { + $scope.publishedFilter = value; + if (value === 'published') { + $scope.reportParams.end = moment().format('YYYY-MM-DD'); + } + loadReport($scope.report, $scope.reportParams.start, $scope.reportParams.end, $scope.orderBy); + }; + + $scope.openStart = function ($event) { + $event.preventDefault(); + $event.stopPropagation(); + $scope.startOpen = true; + }; + + $scope.openEnd = function ($event) { + $event.preventDefault(); + $event.stopPropagation(); + $scope.endOpen = true; + }; + + $scope.orderingChange = function () { + loadReport($scope.report, $scope.reportParams.start, $scope.reportParams.end, $scope.orderBy); + }; + + $scope.downloadIsValid = function () { + if ($scope.report !== 'undefined') { + return true; + } + return false; + }; + + $scope.$watch('report', function (report) { + if (!report) { + return; + } + $scope.orderOptions = report.orderOptions; + if(report.orderOptions.length > 0) { + $scope.orderBy = report.orderOptions[0]; + } else { + $scope.orderBy = null; + } + $scope.headings = []; + report.headings.forEach(function (heading) { + $scope.headings.push(heading.title); + }); + + loadReport(report, $scope.reportParams.start, $scope.reportParams.end, $scope.orderBy); + }); + + $scope.$watchCollection('[reportParams.start, reportParams.end]', function (params) { + if (!$scope.report) { + return; + } + var start = params[0]; + var end = params[1]; + + loadReport($scope.report, start, end, $scope.orderBy); + }); + + function loadReport(report, start, end, order, apiUrl) { + $scope.items = []; + + if (typeof(apiUrl) === 'undefined') { + $scope.apiURL = report.downloadURL; + } else { + $scope.apiURL = apiUrl; + } + + $scope.apiURL += ('?page=' + $scope.reportParams.pageNumber); + + + $scope.downloadURL = report.downloadURL + '?format=csv'; + if (end) { + var endParam = $filter('date')(end, 'yyyy-MM-dd'); + $scope.reportParams.end = endParam; + $scope.apiURL += ('&end=' + endParam); + $scope.downloadURL += ('&end=' + endParam); + } + + if (start) { + var startParam = $filter('date')(start, 'yyyy-MM-dd'); + $scope.reportParams.start = startParam; + $scope.apiURL += ('&start=' + startParam); + $scope.downloadURL += ('&start=' + startParam); + } + + if (order) { + $scope.apiURL += ('&ordering=' + order.key); + // $scope.downloadURL += ('&ordering=' + order.key); + // $scope.reportParams.ordering = order.key; + } + + if ($scope.publishedFilter) { + $scope.apiURL += ('&published=' + $scope.publishedFilter); + $scope.downloadURL += ('&published=' + $scope.publishedFilter); + $scope.reportParams.published = $scope.publishedFilter; + } + + if ($scope.userFilter) { + $scope.apiURL += ('&staff=' + $scope.userFilter); + $scope.downloadURL += ('&staff=' + $scope.userFilter); + $scope.reportParams.staff = $scope.userFilter; + } + + if ($scope.moreFilters) { + for (var key in $scope.moreFilters) { + if ($scope.moreFilters[key].type === 'authors') { + $scope.apiURL += ('&' + 'contributors=' + $scope.moreFilters[key].query); + $scope.downloadURL += ('&' + 'contributors=' + $scope.moreFilters[key].query); + $scope.reportParams.contributors = $scope.moreFilters[key].query; + } else { + $scope.apiURL += ('&' + $scope.moreFilters[key].type + '=' + $scope.moreFilters[key].query); + $scope.downloadURL += ('&' + $scope.moreFilters[key].type + '=' + $scope.moreFilters[key].query); + $scope.reportParams[$scope.moreFilters[key].type] = $scope.moreFilters[key].query; + } + } + } + + $http({ + method: 'GET', + url: $scope.apiURL + }).then(function (data) { + $scope.items = []; + $scope.pageTotal = data.data.count; + data.data.results.forEach(function (lineItem) { + var item = []; + report.headings.forEach(function (heading) { + var exp = $interpolate('{{item.' + heading.expression + '}}'); + var value = exp({item: lineItem}); + item.push(value); + }); + $scope.items.push(item); + }); + }); + } + + $scope.reportEmailModal = function () { + return $modal.open({ + templateUrl: '/views/modals/report-email-modal.html', + controller: 'ReportemailmodalCtrl', + }); + }; + + $scope.goToPage = function () { + loadReport($scope.report, $scope.reportParams.start, $scope.reportParams.end); + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('TargetingCtrl', function ($scope, $http, $window, $q, $location, + CmsConfig) { + + $window.document.title = CmsConfig.getCmsName() + ' | Targeting Editor'; + + var canceller; + $scope.search = function (url) { + if (!url) { return; } + + if (typeof(canceller) === 'undefined') { + canceller = $q.defer(); + } else { + canceller.resolve(); + canceller = $q.defer(); + } + + $http({ + method: 'GET', + url: '/ads/targeting', + timeout: canceller.promise, + params: {url: $scope.url} + }).success(function (data) { + $scope.targetingArray = []; + for (var k in data) { + $scope.targetingArray.push([k, data[k]]); + } + }).error(function (data, status, headers, config) { + if (status === 404) { + $scope.targetingArray = []; + $scope.targetingArray.push(['', '']); + } + }); + }; + + $scope.save = function () { + var data = {}; + for (var i in $scope.targetingArray) { + data[$scope.targetingArray[i][0]] = $scope.targetingArray[i][1]; + } + + return $http({ + method: 'POST', + url: '/ads/targeting?url=' + $scope.url, + data: data + }).success(function (data) { + $scope.targetingArray = []; + for (var k in data) { + $scope.targetingArray.push([k, data[k]]); + } + }); + + }; + + $scope.keyHandler = function (event, url) { + if (event.keyCode === 13) { // enter + this.search(url); + } else if (event.keyCode === 27) { // escape + event.currentTarget.value = ''; + } + }; + + //grab url query key + var search = $location.search(); + if (search && search.url) { + $scope.url = decodeURIComponent(search.url); + } + } +); + +'use strict'; + +angular.module('bulbsCmsApp') + .value('ARTICLE_TEMPORARY_URL_DAYS_VALID', 7) + .controller('TemporaryUrlModalCtrl', function ($scope, $routeParams, + CmsConfig, ContentFactory, ARTICLE_TEMPORARY_URL_DAYS_VALID, _, moment) { + + var content = ContentFactory.one('content', $routeParams.id); + + $scope.TEMP_LINK_DAYS_VALID = ARTICLE_TEMPORARY_URL_DAYS_VALID; + $scope.TEMP_URL_BASE = CmsConfig.buildUnpublishedUrl(); + + $scope.tokens = []; + content.getList('list_tokens').then(function (tokenList) { + $scope.tokens = tokenList; + + // make dates moments + var expiredIndicies = []; + _.each($scope.tokens, function (token, i) { + token.create_date = moment(token.create_date); + token.expire_date = moment(token.expire_date); + + if (moment().isAfter(token.expire_date)) { + // keep track of expired tokens for later removal + expiredIndicies.push(i); + } else { + // this is not expired, keep track of day diff + token.daysTillExpire = token.expire_date.diff(moment(), 'days') + 1; + } + }); + + // remove expired tokens from list, done this way so objects remain restangularized + for (var i = expiredIndicies.length - 1; i >= 0; i--) { + $scope.tokens.splice(expiredIndicies[i], 1); + } + }); + + $scope.createToken = function () { + + var now = moment(); + ContentFactory.one('content', $routeParams.id).post('create_token', { + 'create_date': now, + 'expire_date': now.clone().add({days: ARTICLE_TEMPORARY_URL_DAYS_VALID}) + }).then(function (token) { + // make dates moments + token.create_date = moment(token.create_date); + token.expire_date = moment(token.expire_date); + token.daysTillExpire = token.expire_date.diff(moment(), 'days') + 1; + + $scope.tokens.push(token); + $scope.newestToken = token; + }); + + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('ThumbnailModalCtrl', function ($scope, BettyCropper, $modalInstance, article) { + + $scope.article = article; + + /** + * Upload a new image to BettyCropper and set the scope's thumbnailTemp to that new image. + */ + $scope.selectCustomThumbnail = function () { + + // user is choosing a custom thumbnail + BettyCropper.upload().then(function (success) { + + $scope.article.thumbnail_override = success; + + }, function (error) { + console.log(error); + }, function (progress) { + console.log(progress); + }); + + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('TrashcontentmodalCtrl', function ($scope, $http, $modalInstance, $, Login, articleId, Raven) { + $scope.deleteButton = { + idle: 'Delete', + busy: 'Trashing', + finished: 'Trashed', + error: 'Error!' + }; + + $scope.trashContent = function () { + return $http({ + 'method': 'POST', + 'url': '/cms/api/v1/content/' + articleId + '/trash/' + }); + }; + + $scope.trashCbk = function (trash_promise) { + trash_promise + .then(function (result) { + console.log('trash success'); + $scope.trashSuccessCbk(); + $modalInstance.close(); + }) + .catch(function (reason) { + if (reason.status === 404) { + $scope.trashSuccessCbk(); + $modalInstance.close(); + return; + } + Raven.captureMessage('Error Deleting Article', {extra: reason}); + $modalInstance.dismiss(); + }); + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('UnpublishCtrl', function ($scope, $http, $q) { + + $scope.unpubButton = { + idle: 'Unpublish', + busy: 'Unpublishing', + finished: 'Unpublished!', + error: 'Error' + }; + + + $scope.unpublish = function () { + return $http({ + url: '/cms/api/v1/content/' + $scope.article.id + '/publish/', + method: 'POST', + data: {published: false} + }); + }; + + $scope.unpublishCbk = function (unpub_promise) { + unpub_promise + .then(function (result) { + if ($scope.publishSuccessCbk) { + $scope.publishSuccessCbk({article: $scope.article, response: result.data}); + } + }) + .catch(function (reason) { + if ($scope.publishSuccessCbk) { + $scope.publishSuccessCbk({article: $scope.article, response: reason.data}); + } + }); + }; + + }); + +'use strict'; + +/** + * This is a modal for browsing versions stored in localStorage by the LocalStorageBackup service. + */ +angular.module('bulbsCmsApp') + .controller('VersionBrowserModalCtrl', function ($scope, $modalInstance, _, moment, VersionStorageApi, + FirebaseApi, CmsConfig) { + + // if we have fire base, show the maximum number of versions allowed + FirebaseApi.$authorize().then(function () { + $scope.maxVersions = CmsConfig.getFirebaseMaxArticleHistory(); + }); + + VersionStorageApi.$all() + .then(function (versions) { + + // doubley ensure timestamp in desc since modal functionality depends on it, add some extra display stuff + $scope.versions = + _.chain(versions) + // loop through each version and add timestamp display property + .each(function (version) { + version.timestamp_display = moment(version.timestamp).format('MMM Do YYYY, h:mma'); + }) + // sort by timestamps desc, so most recent is on top + .sortBy(function (version) { + return -version.timestamp; + }) + .value(); + + // set initial preview to top item which should be the most recent + $scope.selectedVersion = $scope.versions[0]; + + // set preview in modal window based on timestamp + $scope.setPreview = function (version) { + $scope.selectedVersion = version; + }; + + // restore selected version preview + $scope.restoreSelected = function () { + + // loop through each key of selected version and replace corresponding value in article + _.each($scope.selectedVersion.content, function (value, key) { + $scope.article[key] = value; + }); + + // mark article as dirty now that we've restored an old version + $scope.articleIsDirty = true; + + // close modal + $modalInstance.close(); + }; + + }); + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .controller('VideothumbnailmodalCtrl', function ($scope, $http, + $modalInstance, CmsConfig, Zencoder, videoId) { + var DEFAULT_THUMBNAIL = 4; + var MAX_THUMBNAIL = 19; + $scope.uploadedImage = {id: null}; + $scope.mode = 'still'; + + Zencoder.getVideo(videoId).then( + function (response) { + $scope.video = response.data; + if (response.data.status === 'In Progress') { + $scope.inProgress = true; + $scope.video.poster = $scope.video.poster || null; + } else { + $scope.video.poster = $scope.video.poster || compilePosterUrl(DEFAULT_THUMBNAIL); + } + } + ); + + $scope.$watch('video.poster', function () { + if (!$scope.video || !$scope.video.poster) { return; } + var defaultUrl = CmsConfig.buildVideoThumbnailUrl(videoId, 'thumbnail_{{thumbnail}}.png'); + var thumbnailIndex = defaultUrl.indexOf('{{thumbnail}}'); + if ($scope.video.poster.indexOf(defaultUrl.substr(0, thumbnailIndex)) === 0) { + $scope.currentThumbnail = Number($scope.video.poster.substr(thumbnailIndex, 4)); + $scope.uploadedImage.id = null; + } else { + $scope.currentThumbnail = false; + } + }); + + $scope.$watch('uploadedImage.id', function () { + if ($scope.uploadedImage.id) { + $scope.video.poster = + CmsConfig.buildImageApiUrl('16x9', $scope.uploadedImage.id, '1200.jpg'); + } + }); + + $scope.nextThumb = function () { + $scope.video.poster = compilePosterUrl($scope.currentThumbnail < MAX_THUMBNAIL ? $scope.currentThumbnail + 1 : 0); + }; + + $scope.prevThumb = function () { + $scope.video.poster = compilePosterUrl($scope.currentThumbnail > 0 ? $scope.currentThumbnail - 1 : MAX_THUMBNAIL); + }; + + $scope.defaultThumb = function () { + $scope.video.poster = compilePosterUrl(DEFAULT_THUMBNAIL); + }; + + $scope.setPoster = function () { + Zencoder.setVideo($scope.video); + $modalInstance.close($scope.video.poster); + }; + + $scope.reencode = function () { + Zencoder.encode(videoId); + }; + + function compilePosterUrl(thumbnail) { + return CmsConfig.buildVideoThumbnailUrl(videoId, 'thumbnail_' + pad4(thumbnail) + '.png'); + } + + function pad4(num) { + var s = '0000' + num; + return s.substr(s.length - 4); + } + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .factory('AdApi', function (Restangular, adApiConfig) { + return Restangular.withConfig(function (RestangularConfigurer) { + RestangularConfigurer.setBaseUrl(adApiConfig.baseUrl); + }); + }) + .constant('adApiConfig', { + baseUrl: '/ads' + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .factory('BadRequestInterceptor', function ($q, $injector) { + return { + responseError: function (rejection) { + $injector.invoke(function ($modal) { + if (rejection.status === 400) { + var detail = rejection.data || {'something': ['Something was wrong with your request.']}; + $modal.open({ + templateUrl: '/views/modals/400-modal.html', + controller: 'BadrequestmodalCtrl', + resolve: { + detail: function () { return detail; } + } + }); + } + }); + return $q.reject(rejection); + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp').factory('BugReportInterceptor', function ($q, $window, PNotify) { + return { + responseError: function (rejection) { + if (rejection.status >= 500) { + var stack = { + animation: true, + dir1: 'up', + dir2: 'left' + }; + new PNotify({ + title: 'You found a bug!', + text: + 'Looks like something just went wrong, and we need your help to fix it! Report it, and we\'ll make sure it never happens again.', + type: 'error', + confirm: { + confirm: true, + align: 'left', + buttons: [{ + text: 'Report Bug', + addClass: 'btn-danger pnotify-report-bug', + click: function (notice) { + notice.remove(); + $window.showBugReportModal(); // see bugreporter.js + } + }, {addClass: 'hidden'}] // removing the "Cancel" button + }, + buttons: { + sticker: false + }, + icon: 'fa fa-bug pnotify-error-icon', + addclass: 'stack-bottomright', + stack: stack + }); + } + return $q.reject(rejection); + } + }; + }); +'use strict'; + +angular.module('bulbsCmsApp') + .config(function (RestangularProvider, bulbsApiConfig) { + // This is specific to Django Rest Framework + RestangularProvider.setResponseExtractor(function (response, operation, what, url) { + var newResponse = response; + if (operation === 'getList') { + if (typeof response.results !== 'undefined') { + newResponse = response.results; + newResponse.metadata = { + count: response.count, + next: response.next, + previous: response.previous + }; + } + } + return newResponse; + }); + if (bulbsApiConfig.requestSuffix) { + RestangularProvider.setRequestSuffix(bulbsApiConfig.requestSuffix); + } + }) + .constant('bulbsApiConfig', { + requestSuffix: '/' + }); +'use strict'; + +angular.module('bulbsCmsApp') + .factory('CmsNotificationsApi', function ($q, ContentFactory) { + return ContentFactory.service('cms_notifications'); + }); + +'use strict'; + +/** + * Service for authenticating and interacting with the root of this site in firebase. + */ +angular.module('bulbsCmsApp') + .factory('FirebaseApi', function (FirebaseRefFactory, $firebase, $rootScope, + $q, CurrentUserApi, CmsConfig) { + + // get root reference in firebase for this site + var rootRef = FirebaseRefFactory.newRef(CmsConfig.buildFirebaseSiteUrl()); + + // set up a promise for authorization + var authDefer = $q.defer(), + $authorize = authDefer.promise; + + // set up catch all for logging auth errors + $authorize + .catch(function (error) { + // if there's an error message log it + if (error) { + console.error('Firebase login failed:', error); + } + }); + + // log current session in when their current user data is available + CurrentUserApi.getCurrentUserWithCache().then(function (user) { + + // attempt to login if user has firebase token, if they don't auth promise will reject with no error message + // which is okay if we're in an environment where firebase isn't set up yet + if ('firebase_token' in user && user.firebase_token) { + + // authorize user + rootRef.auth(user.firebase_token, function (error) { + + if (error) { + + // authorization failed + authDefer.reject(error); + + } else { + + // authorization success, resolve deferred authorization with rootRef + authDefer.resolve(rootRef); + + } + + }); + + } else { + + // user doesn't have a firebase token, reject authorization without an error message + authDefer.reject(); + + } + + }); + + // emit events when firebase reconnects or disconnects, disconnect event should not be used in place of onDisconnect + // function provided by firebase reference objects + var connectedRef = FirebaseRefFactory.newRef(CmsConfig.buildFirebaseUrl('.info/connected')); + connectedRef.on('value', function (connected) { + + if (connected.val()) { + + $rootScope.$emit('firebase-reconnected'); + + } else { + + $rootScope.$emit('firebase-disconnected'); + + } + + $rootScope.$emit('firebase-connection-state-changed'); + + }); + + // connection object + var $connection = { + onConnect: function (callback) { + $rootScope.$on('firebase-reconnected', callback); + return $connection; + }, + onDisconnect: function (callback) { + $rootScope.$on('firebase-disconnected', callback); + return $connection; + }, + onChange: function (callback) { + $rootScope.$on('firebase-connection-state-changed', callback); + } + }; + + return { + + /** + * Authorization deferred promise that resolves with the root firebase reference, or rejects with an error + * message. + */ + $authorize: function () { return $authorize; }, + + /** + * Provides access to Firebase connection and disconnection event listeners. + */ + $connection: $connection + + }; + + }); + +'use strict'; + +/** + * Factory for getting references to articles as they are stored in firebase. + */ +angular.module('bulbsCmsApp') + .factory('FirebaseArticleFactory', function ($q, $firebase, $routeParams, _, moment, + FirebaseApi, CurrentUserApi, CmsConfig) { + + /** + * Create a new article. + * + * @param rootRef root reference of firebase db. + * @param articleId id of article to create. + * @return article object. + */ + var createArticle = function (rootRef, articleId) { + + var articleRef = rootRef.child('articles/' + articleId), + $activeUsers = $firebase(articleRef.child('users')).$asArray(), + $versions = $firebase(articleRef.child('versions')).$asArray(); + + var addCurrentUserToActiveUsers = function () { + + var registeredDeferred = $q.defer(), + registeredPromise = registeredDeferred.promise; + + CurrentUserApi.getCurrentUserWithCache() + .then(function (user) { + + var simplifiedUser = _.pick(user, [ + 'id', + 'displayName' + ]); + + $activeUsers + .$add(simplifiedUser) + .then(function (userRef) { + + // ensure user is removed on disconnect + userRef.onDisconnect().remove(); + + // resolve registration + registeredDeferred.resolve(simplifiedUser); + + }) + .catch(function (error) { + registeredDeferred.reject(error); + }); + + }) + .catch(function (error) { + registeredDeferred.reject(error); + }); + + return registeredPromise; + + }; + + var registerCurrentUserActive = function () { + + // ensure when reconnection occurs, user is added back to active users + FirebaseApi.$connection.onConnect(addCurrentUserToActiveUsers); + + // add current user and return promise + return addCurrentUserToActiveUsers(); + + }; + + return { + + /** + * Raw firebase article reference. + */ + ref: articleRef, + /** + * Get angularfire live array of article's currently active users. + */ + $activeUsers: function () { return $activeUsers; }, + /** + * Get angularfire live array of article versions. + */ + $versions: function () { return $versions; }, + /** + * Register a user as active with this article. + * + * @returns deferred promise that will resolve with the user reference as added to the active user list. + */ + $registerCurrentUserActive: registerCurrentUserActive, + + /** + * Create a new version for this article. + * + * @param articleData Content to store in the version. + * @returns deferred promise that will resolve with the version reference as added to the versions list. + * Promise is rejected if for some reason create did not occur (eg nothing changed since last version). + */ + $createVersion: function (articleData) { + + // defer for creation of version + var createDefer = $q.defer(), + $createPromise = createDefer.promise; + + // get simplified version of user then use that when creating version + CurrentUserApi.getCurrentUserWithCache().then(function (user) { + + var simplifiedUser = _.pick(user, [ + 'id', + 'displayName' + ]); + + // if we will have more than the max versions allowed, delete until we're one below the max + var numVersions = $versions.length; + if (numVersions + 1 > CmsConfig.getFirebaseMaxArticleHistory()) { + _.chain($versions) + // sort oldest to newest + .sortBy(function (version) { + return version.timestamp; + }) + // remove oldest versions until we're 1 below max versions + .every(function (version) { + $versions.$remove(version); + numVersions--; + return numVersions + 1 > CmsConfig.getFirebaseMaxArticleHistory(); + }); + } + + // make version data + var versionData = { + timestamp: moment().valueOf(), + user: simplifiedUser, + content: articleData + }; + + // add version to version data + $versions.$add(versionData) + .then(createDefer.resolve) + .catch(createDefer.reject); + + }); + + // return promise for this create + return $createPromise; + + } + + }; + + }; + + return { + + /** + * Retrieve an article object that is connected to firebase. + * + * @param articleId id of article to retrieve. + * @returns deferred promise that will resolve with the article object. + */ + $retrieveArticle: function (articleId) { + + var retrieveDeferred = $q.defer(), + retrievePromise = retrieveDeferred.promise; + + FirebaseApi.$authorize() + .then(function (rootRef) { + retrieveDeferred.resolve(createArticle(rootRef, articleId)); + }) + .catch(function (error) { + retrieveDeferred.reject(error); + }); + + return retrievePromise; + + }, + /** + * Retrieve current article object that is connected to firebase. + * + * @returns deferred promise that will resolve with the current article object. + */ + $retrieveCurrentArticle: function () { + + return this.$retrieveArticle($routeParams.id); + + } + + }; + + }); + +'use strict'; + +/** + * Factory for creating new references to firebase. + */ +angular.module('bulbsCmsApp') + .service('FirebaseRefFactory', function (Firebase) { + + return { + newRef: function (url) { + return new Firebase(url); + } + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .service('Gettemplate', function Gettemplate($templateCache, $q, $http) { + this.get = function (templateUrl) { + var template = $templateCache.get(templateUrl); + if (template) { + return $q.when(template); + } else { + var deferred = $q.defer(); + $http.get(templateUrl, {cache: true}).success(function (html) { + $templateCache.put(templateUrl, html); + deferred.resolve(html); + }); + + return deferred.promise; + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .service('IfExistsElse', function IfExistsElse($window, $http) { + // AngularJS will instantiate a singleton by calling "new" on this function + this.ifExistsElse = function (restQ, propertiesToValues, existsCbk, elseCbk, errorCbk) { + //another place where I need to figure out promises and make this way better + restQ.then(function (data) { + var resList = data.results || data; + for (var j = 0; j < resList.length; j++) { + var allFieldsMatch = true; + var datum = resList[j]; + for (var property in propertiesToValues) { + if (!datum.hasOwnProperty(property)) { + allFieldsMatch = false; + break; + } + //console.log("property: " + property) + if (datum[property] !== propertiesToValues[property]) { + //console.log(resList[j][property] + " != " + propertiesToValues[property]) + allFieldsMatch = false; + break; + } + } + if (allFieldsMatch) { + existsCbk(datum); + return; + } + } + elseCbk(propertiesToValues); + }).catch(errorCbk); + }; + }); + +'use strict'; + +/** + * Methods to create and retrieve versions in local storage. Articles are stored as json strings under the keys + * 'article.{timestamp}.{article id}'. When local storage is full, it will attempt to remove values older than + * yesterday. + */ +angular.module('bulbsCmsApp') + .factory('LocalStorageBackup', function ($q, $routeParams, $window, moment, _, + CurrentUserApi) { + + var keyPrefixArticle = 'article'; + var keyPrefix = keyPrefixArticle + '.' + $routeParams.id + '.'; + + return { + + /** + * Save content to local storage. + * + * @param articleData Content to save to local storage. + * @return New version data or null if no version was created. + */ + $create: function (articleData) { + + var createDefer = $q.defer(), + createPromise = createDefer.promise; + + // check if we have local storage + if ($window.localStorage) { + CurrentUserApi.getCurrentUserWithCache().then(function (user) { + + var simplifiedUser = _.pick(user, [ + 'id', + 'displayName' + ]); + + // create new version object + var version = { + timestamp: moment().valueOf(), + user: simplifiedUser, + content: articleData + }; + + try { + + // create new local storage item with version content + $window.localStorage.setItem(keyPrefix + moment().valueOf(), JSON.stringify(version)); + createDefer.resolve(version); + + } catch (error) { + + // some error occurred, prune entries older than yesterday + console.log('Caught localStorage error: ' + error); + console.log('Pruning old entries...'); + + // loop through local storage keys and see if they're old + _.chain($window.localStorage) + // pick keys that are articles and that are older than yesterday + .pick(function (value, key) { + var keySplit = key.split('.'), + pickForRemoval = false; + // check that this is an article in storage + if (keySplit.length === 3 && keySplit[0] === keyPrefixArticle) { + var yesterday = moment().subtract({days: 1}).valueOf(), + keyTime = Number(keySplit[2]); + // if older than yesterday, pick the key for removal + pickForRemoval = keyTime < yesterday; + } + // return our result + return pickForRemoval; + }) + // these keys should be removed from local storage + .each(function (value, key) { + $window.localStorage.removeItem(key); + }); + + // now try to add entry again + try { + $window.localStorage.setItem(version.timestamp, JSON.stringify(version.content)); + createDefer.resolve(version); + } catch (error) { + // total failure, reject with an error. + console.log('Maybe you\'ve been saving too much? Failed again at adding entry, no more retries: ' + error); + createDefer.reject('Maybe you\'ve been saving too much? Failed again at adding entry, no more retries: ' + error); + } + } + }); + + } else { + // no local storage, why are we here? + createDefer.reject('You don\'t have local storage capabilities in your browser. Use a better browser.'); + } + + return createPromise; + + }, + /** + * Get all versions for this article in local storage. No guarantee of order. + * + * @return objects returned contain a timestamp and a content variable which holds the version's content. + */ + $versions: function () { + + // note: using a promise here to better match the version api functionality + var retrieveDefer = $q.defer(), + retrievePromise = retrieveDefer.promise, + versions = + // loop through entries of local storage + _.chain($window.localStorage) + // pick only entries that are for this particular article + .pick(function (stored, key) { + var keySplit = key.split('.'); + return keySplit.length === 3 && keySplit[0] === keyPrefixArticle && keySplit[1] === $routeParams.id; + }) + // parse and map these entries into an array + .map(function (stored) { + return JSON.parse(stored); + }) + // return the array of version objects + .value(); + + retrieveDefer.resolve(versions); + + return retrievePromise; + + } + + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .service('Login', function Login($rootScope, $http, $cookies, $window, $) { + + $rootScope.$watch(function () { + return $cookies.csrftoken; + }, function (newCsrf, oldCsrf) { + $http.defaults.headers.common['X-CSRFToken'] = newCsrf; + if ($window.jqueryCsrfSetup) { + $window.jqueryCsrfSetup(); + } + }); + + return { + login: function (username, password) { + var data = $.param({username: username, password: password}); + return $http({ + method: 'POST', + url: '/login/', + data: data, + headers: {'Content-Type': 'application/x-www-form-urlencoded'} + }); + } + }; + }); + +'use strict'; + /* helpful SO question on injecting $modal into interceptor and doing intercept pass-through + http://stackoverflow.com/questions/14681654/i-need-two-instances-of-angularjs-http-service-or-what + */ +angular.module('bulbsCmsApp').factory('PermissionsInterceptor', function ($q, $injector) { + return { + responseError: function (rejection) { + if (rejection.config && rejection.config.noPermissionIntercept) { + return $q.when(rejection); + } else { + $injector.invoke(function ($modal) { + if (rejection.status === 403) { + if (rejection.data && rejection.data.detail && rejection.data.detail.indexOf('credentials') > 0) { + $modal.open({ + templateUrl: '/views/modals/login-modal.html', + controller: 'LoginmodalCtrl' + }); + } else { + var detail = rejection.data && rejection.data.detail || 'Forbidden'; + $modal.open({ + templateUrl: '/views/modals/403-modal.html', + controller: 'ForbiddenmodalCtrl', + resolve: { + detail: function () { return detail; } + } + }); + } + } + }); + return $q.reject(rejection); + } + } + }; +}); + +'use strict'; + +angular.module('bulbsCmsApp') + .factory('TemporaryUrlModalOpener', function ($modal) { + + var modal = null; + + return { + open: function ($scope, article) { + // ensure only one version browser is open at a time + if (modal) { + modal.close(); + } + + modal = $modal.open({ + templateUrl: '/views/modals/temporary-url-modal.html', + controller: 'TemporaryUrlModalCtrl', + scope: $scope, + resolve: { + article: function () { + return article; + } + } + }); + + return modal; + } + }; + + }); + +'use strict'; + +/** + * Api for saving article versions. Will automatically detect and attempt to use firebase, otherwise local storage + * will be used for versions. + * + * This API expects all version objects to be in at least the following form: + * + * { + * timestamp: Number - timestamp in ms for this version + * content: Object - content this version holds, which in this case is an article object + * } + */ +angular.module('bulbsCmsApp') + .factory('VersionStorageApi', function ($q, FirebaseApi, FirebaseArticleFactory, LocalStorageBackup, _) { + + // set up a promise for checking if we can authorize with firebase + var firebaseAvailableDefer = $q.defer(), + $firebaseAvailable = firebaseAvailableDefer.promise; + FirebaseApi.$authorize() + .then(function () { + + // we have a firebase connection, use firebase for versioning + firebaseAvailableDefer.resolve(FirebaseArticleFactory.$retrieveCurrentArticle()); + + }) + .catch(function () { + + // we don't have a firebase connection, use local storage for versioning + firebaseAvailableDefer.reject(); + + }); + + /** + * Memoized omitting function for deep scrubbing. + */ + var _omitter = _.memoize( + function (value, key) { + return _.isFunction(value) || + _.find(key, function (c) { + return c === '.' || c === '#' || c === '$' || c === '/' || c === '[' || c === ']'; + }); + }, + function (value, key) { + return [key, value]; + }); + + /** + * Recursively scrub object of functions and turn undefines into null, makes object valid for saving in firebase. + * + * @param obj object to recurse through + */ + var _deepScrub = function (obj) { + + var clone, transValue; + + if (_.isUndefined(obj)) { + // turn undefineds into nulls, this allows deletion of property values + clone = null; + } else if (_.isPlainObject(obj)) { + // this is an object, use omit to recurse through its members + clone = {}; + _.forOwn(obj, function (value, key) { + // run value through recursive omit call + transValue = _deepScrub(value); + // check if this should be omitted, if not clone it over + if (!_omitter(transValue, key)) { + clone[key] = transValue; + } + }); + } else if (_.isArray(obj)) { + // this is an array, loop through items use omit to decide what to do with them + clone = []; + _.each(obj, function (value, key) { + // run value through recursive omit call + transValue = _deepScrub(value); + // check if this should be omitted, if not clone over + if (!_omitter(transValue, key)) { + clone.push(transValue); + } + }); + } else { + // not a special case, just return object + clone = obj; + } + + return clone; + + }; + + /** + * Keep only the data we want to persist for an article. Does a deep clone to scrub sub-objects. + * + * @param articleData data to scrub. + */ + var scrubArticle = function (articleData) { + + return _deepScrub(articleData); + + }; + + return { + + /** + * Create a new version either in firebase or in local storage. + * + * @param rawArticleData raw article data to copy and transform before saving. + * @param articleIsDirty true if the article has unsaved changes, false otherwise. + * @return a promise that resolves on creation with a version object that contains timestamp and content + * properties. + */ + $create: function (rawArticleData, articleIsDirty) { + + // get article data that we want to save + var articleData = scrubArticle(rawArticleData); + + // create deferred to return + var createDefer = $q.defer(), + createPromise = createDefer.promise; + + // article is dirty and should be saved, check if firebase is being used + $firebaseAvailable + .then(function ($currentArticle) { + + // if article is dirty or there are no versions, attempt to create one using firebase + if (articleIsDirty || $currentArticle.$versions().length < 1) { + + // we do have firebase, so use firebase + $currentArticle.$createVersion(articleData) + .then(function (versionData) { + // create occurred, resolve it with new version data + createDefer.resolve(versionData); + }) + .catch(function () { + // create didn't occur, reject promise + createDefer.reject(); + }); + + } else { + + // article is not dirty, reject create + createDefer.reject(); + + } + + }) + .catch(function () { + + LocalStorageBackup.$versions().then(function (versions) { + + // if article is dirty or there are no versions, attempt to create one using local storage + if (articleIsDirty || versions.length < 1) { + + // create version with local storage + LocalStorageBackup.$create(articleData) + .then(function (versionData) { + // version was created, resolve create defer with version data + createDefer.resolve(versionData); + }) + .catch(function (error) { + // version was not created, pass on error + createDefer.reject(error); + }); + + } else { + + // article is not dirty, reject create + createDefer.reject(); + + } + + }); + + }); + + // return create promise + return createPromise; + + }, + /** + * Retrieve all versions either from firebase or local storage. + * @return list of version objects sorted by timestamp descending. + */ + $all: function () { + + // set up deferred objects for all retrieval + var allDefer = $q.defer(), + allPromise = allDefer.promise; + + // check if we have firebase + $firebaseAvailable + .then(function ($currentArticle) { + + // we do have firebase, so use firebase + $currentArticle.$versions().$loaded(function (versions) { + allDefer.resolve(versions); + }); + + }) + .catch(function () { + + // we don't have firebase so use local storage + LocalStorageBackup.$versions().then(function (versions) { + allDefer.resolve(versions); + }).catch(function (error) { + allDefer.reject(error); + }); + + }); + + // ensure versions are ordered by timestamp desc when they return + return allPromise.then(function (versions) { + return _.sortBy(versions, function (version) { + return -version.timestamp; + }); + }); + + } + + }; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .factory('VersionBrowserModalOpener', function ($modal) { + + var modal = null; + + return { + open: function ($scope, article) { + // ensure only one version browser modal is open at a time + if (modal) { + modal.close(); + } + + modal = $modal.open({ + templateUrl: '/views/modals/version-browser-modal.html', + controller: 'VersionBrowserModalCtrl', + scope: $scope, + size: 'lg', + resolve: { + article: function () { + return article; + } + } + }); + + return modal; + } + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .service('Zencoder', function Zencoder($http, $q, $modal, $) { + var newVideoUrl = '/video/new'; + var fileInputId = '#bulbs-cms-hidden-video-file-input'; + var inputTemplate = ''; + + this.onVideoFileUpload = function () { + var clickDeferred = $q.defer(); + + angular.element(fileInputId).remove(); + var fileInput = angular.element(inputTemplate); + var file; + angular.element('body').append(fileInput); + fileInput.click(); + fileInput.unbind('change'); + fileInput.bind('change', function (elem) { + if (this.files.length !== 0) { + file = this.files[0]; + + // We have a file upload limit of 1024MB + if (file.size > (1024 * 1024 * 1024)) { + clickDeferred.reject('Upload file cannot be larger than 1024MB.'); + } + + if (file.type.indexOf('video/') !== 0) { + clickDeferred.reject('You must upload a video file.'); + } + } else { + clickDeferred.reject('Please select a file.'); + } + + getNewVideoUploadCredentials(file) + .then(uploadToS3) + .then(encode, angular.noop, function (uploadPercentComplete) { clickDeferred.notify(uploadPercentComplete); }) + .then( + function (videoObject) { + clickDeferred.resolve(videoObject); + }, + function (error) { + clickDeferred.reject(error); + } + ); + + }); + + return clickDeferred.promise; + }; + + function getNewVideoUploadCredentials(file) { + var data = {name: file.name}; + data = $.param(data); + + var newVideoDeferred = $q.defer(); + + $http({ + method: 'POST', + url: newVideoUrl, + data: data, + headers: {'Content-Type': 'application/x-www-form-urlencoded'} + }).success(function (data) { + newVideoDeferred.resolve({ + file: file, + attrs: data + }); + }).error(function (data) { + newVideoDeferred.reject(data); + }); + + return newVideoDeferred.promise; + } + + function uploadToS3(videoObject) { + var s3deferred = $q.defer(); + + var formData = new FormData(); + + formData.append('key', videoObject.attrs.key); + formData.append('AWSAccessKeyId', videoObject.attrs.AWSAccessKeyId); + formData.append('acl', videoObject.attrs.acl); + formData.append('success_action_status', videoObject.attrs.success_action_status); + formData.append('policy', videoObject.attrs.policy); + formData.append('signature', videoObject.attrs.signature); + formData.append('file', videoObject.file); + + //todo: use a vanilla XMLHttpRequest in heyea + $.ajax(videoObject.attrs.upload_endpoint, { + processData: false, + contentType: false, + data: formData, + type: 'POST', + xhr: function () { + var req = $.ajaxSettings.xhr(); + if (req) { + req.upload.addEventListener('progress', function (e) { + var percent = (e.loaded / e.total) * 100; + s3deferred.notify(percent); + }, false); + } + return req; + }, + success: function (data) { + s3deferred.resolve(videoObject); + }, + error: function (data) { + s3deferred.reject(data); + } + }); + + return s3deferred.promise; + + } + + function encode(videoObject) { + var encodeDeferred = $q.defer(); + + $http({ + method: 'POST', + url: '/video/' + videoObject.attrs.id + '/encode' + }).success(function (data) { + videoObject.attrs['encode_status_endpoints'] = data; + _encodingVideos[videoObject.attrs.id] = videoObject.attrs; + + encodeDeferred.resolve(videoObject); + }).error(function (data) { + encodeDeferred.reject(data); + }); + + return encodeDeferred.promise; + } + this.encode = function (videoId) { + encode({attrs: {id: videoId}}); + }; + + this.openVideoThumbnailModal = function (videoId) { + return $modal.open({ + templateUrl: '/views/modals/video-thumbnail-modal.html', + controller: 'VideothumbnailmodalCtrl', + resolve: { + videoId: function () { return videoId; } + } + }); + }; + + this.getVideo = function (videoId) { + var url = '/video/' + videoId; + return $http({ + method: 'GET', + url: url + }); + }; + + this.setVideo = function (video) { + var url = '/video/' + video.id; + var data = $.param(video); + return $http({ + method: 'POST', + url: url, + data: data, + headers: {'Content-Type': 'application/x-www-form-urlencoded'} + }); + }; + + var _encodingVideos = {}; + this.encodingVideos = _encodingVideos; + + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .filter('truncateByCharacters', function () { + return function (input, chars, breakOnWord) { + if (isNaN(chars)) { return input; } + if (chars <= 0) { return ''; } + if (input && input.length >= chars) { + input = input.substring(0, chars); + if (!breakOnWord) { + var lastspace = input.lastIndexOf(' '); + //get last space + if (lastspace !== -1) { + input = input.substr(0, lastspace); + } + } else { + while (input.charAt(input.length - 1) === ' ') { + input = input.substr(0, input.length - 1); + } + } + if (chars === 1) { + return input + '.'; + } else { + return input + '...'; + } + } + return input; + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .filter('truncateByWords', function () { + return function (input, words) { + if (isNaN(words)) { return input; } + if (words <= 0) { return ''; } + if (input) { + var inputWords = input.split(/\s+/); + if (inputWords.length > words) { + input = inputWords.slice(0, words).join(' ') + '...'; + } + } + return input; + }; + }); + +'use strict'; + +angular.module('bulbsCmsApp') + .filter('tzDate', function (dateFilter, moment, CmsConfig) { + return function (input, format) { + if (!input) { + return ''; + } + var inDate = moment.tz(input, CmsConfig.getTimezoneName()); + var newdate = inDate.format('YYYY-MM-DDTHH:mm'); + var formattedDate = dateFilter(newdate, format); + if (format.toLowerCase().indexOf('h') > -1) { + formattedDate += ' ' + inDate.format('z'); + } + return formattedDate; + }; + }); diff --git a/dist/scripts/scripts.min.js b/dist/scripts/scripts.min.js new file mode 100644 index 000000000..8cff8b79c --- /dev/null +++ b/dist/scripts/scripts.min.js @@ -0,0 +1,9 @@ +!function(w){function computeStyle(element,image,selection){var scale,selector='.image[data-image-id="'+image.id+'"]>div',elementWidth=$(selector).width(),elementHeight=image.height/image.width*elementWidth,s_width=selection.x1-selection.x0,s_height=selection.y1-selection.y0,tmp_selection=selection;s_width&&s_height||(s_width=elementWidth,s_height=elementHeight,tmp_selection={x0:0,y0:0,x1:s_width,y1:s_height});var imageUrl=w.BC_ADMIN_URL+"/"+image.id+"/original/1200.jpg";scale=elementWidth/s_width;var rules={"background-image":"url("+imageUrl+")","background-size":scaleNumber(image.width,scale)+"px","background-position":"-"+scaleNumber(tmp_selection.x0,scale)+"px -"+scaleNumber(tmp_selection.y0,scale)+"px","background-repeat":"no-repeat"};createStyle(selector,rules,"image-css-"+image.id)}function createStyle(selector,rules,classname){var styleNode=document.createElement("style");styleNode.type="text/css",styleNode.className=classname;var css="",temp=""+selector+"{";for(var rule in rules)temp+=rule+":"+rules[rule]+";";temp+="}",css+=temp,styleNode.styleSheet?styleNode.styleSheet.cssText=css:styleNode.appendChild(document.createTextNode(css)),$(document).find("head").append(styleNode)}function scaleNumber(num,by_scale){return Math.floor(num*by_scale)}function computeAspectRatio(_w,_h){if(0!==_w&&0!==_h){var aspectRatio=Math.ceil(_w/_h*10);switch(aspectRatio){case 30:case 31:crop="3x1";break;case 20:crop="2x1";break;case 14:crop="4x3";break;case 18:crop="16x9";break;case 8:crop="3x4";break;case 10:crop="1x1";break;default:crop="original"}return crop}return"16x9"}w.picturefill=function(element){var ps;element&&"image"===element.getAttribute("data-type")?ps=[element]:("undefined"==typeof element&&(element=w.document),ps=element.getElementsByTagName("div"));for(var i=0,il=ps.length;idiv',{"padding-bottom":res.height/res.width*100+"%"},"image-css-"+this.id);var cropDetails={x0:0,x1:res.width,y0:0,y1:res.height}}else var cropDetails=imageData.selections[this.crop];computeStyle(this.element,imageData,cropDetails)},{element:element,id:id,crop:crop}),error:$.proxy(function(){"original"===this.crop&&createStyle('.image[data-image-id="'+this.id+'"]>div',{"padding-bottom":"56.25%","background-color":"rgba(200, 0,0, .5)"},"image-css-"+this.id)},{id:id,crop:crop})}))}}}}}(this),angular.module("lodash",[]).constant("_",window._),angular.module("URLify",[]).constant("URLify",window.URLify),angular.module("jquery",[]).constant("$",window.$),angular.module("moment",[]).constant("moment",window.moment),angular.module("PNotify",[]).constant("PNotify",window.PNotify),angular.module("keypress",[]).constant("keypress",window.keypress),angular.module("Raven",[]).constant("Raven",window.Raven),angular.module("OnionEditor",[]).constant("OnionEditor",window.OnionEditor),angular.module("bulbsCmsApp",["bulbs.cms.site.config","bulbs.cms.superFeatures","bulbs.cms.currentUser","bulbs.cms.dateTimeModal","bulbs.cms.dateTimeFilter","bulbs.cms.editor","bulbs.cms.imageCropModal","bulbs.cms.liveBlog","bulbs.cms.sendToEditorModal","bulbs.cms.staticImage","ngCookies","ngResource","ngRoute","ui.bootstrap","ui.bootstrap.datetimepicker","restangular","BettyCropper","jquery","lodash","URLify","moment","PNotify","keypress","Raven","firebase","ipCookie","bulbs.api","contentServices","cms.tunic","bettyEditable","bugReporter","campaigns","evergreenField","hideFromRssField","filterWidget","filterListWidget","notifications","polls","promotedContent","statusFilter","templateTypeField","specialCoverage","sections","reports"]).config(function($locationProvider,$routeProvider,$sceProvider){$locationProvider.html5Mode(!0),$routeProvider.when("/cms/app/list/",{templateUrl:"/views/contentlist.html",controller:"ContentlistCtrl",reloadOnSearch:!1}).when("/cms/app/edit/:id/contributions/",{templateUrl:"/views/contributions.html",controller:"ContributionsCtrl"}).when("/cms/app/edit/:id/:contentType?",{templateUrl:"/views/contentedit.html",controller:"ContenteditCtrl",reloadOnSearch:!1}).when("/cms/app/targeting/",{templateUrl:"/views/targeting-editor.html",controller:"TargetingCtrl"}).when("/cms/app/cms-notifications/",{templateUrl:"/views/cms-notifications.html",controller:"CmsNotificationsCtrl"}).when("/cms/app/reporting/",{templateUrl:"/views/reporting.html",controller:"ReportingCtrl"}).when("/cms/app/pzones/",{templateUrl:"/views/pzones.html",controller:"PzoneCtrl"}).otherwise({redirectTo:"/cms/app/list/"}),$sceProvider.enabled(!1)}).config(function($provide,$httpProvider){$provide.decorator("$exceptionHandler",function($delegate){return function(exception,cause){$delegate(exception,cause),window.Raven.captureException(exception)}}),$httpProvider.interceptors.push("BugReportInterceptor"),$httpProvider.interceptors.push("PermissionsInterceptor"),$httpProvider.interceptors.push("BadRequestInterceptor"),$httpProvider.interceptors.push("TunicInterceptor")}).run(function($rootScope,$http,$cookies){$http.defaults.headers.common["X-CSRFToken"]=$cookies.csrftoken;var deleteHeaders=$http.defaults.headers.delete||{};deleteHeaders["X-CSRFToken"]=$cookies.csrftoken,$http.defaults.headers.delete=deleteHeaders}),angular.module("bulbs.api",["restangular","moment"]),angular.module("bulbs.api").factory("AuthorService",function(Restangular){return Restangular.setBaseUrl("/cms/api/v1/"),Restangular.setRequestSuffix("/"),Restangular.extendModel("author",function(obj){return angular.extend(obj,{getFullName:function(){return obj.first_name+" "+obj.last_name}})}),Restangular.all("author")}),angular.module("bulbs.api").factory("ContentService",function(Restangular){return Restangular.setBaseUrl("/cms/api/v1/"),Restangular.setRequestSuffix("/"),Restangular.extendModel("content",function(obj){var extendAuthor=function(author){return angular.extend(author,{getFullName:function(){return obj.contributor.first_name+" "+obj.contributor.last_name}})};for(var i in obj.authors)obj.authors[i]=extendAuthor(obj.authors[i]);return obj}),Restangular.extendModel("contributions",function(obj){return obj&&obj.contributor&&(obj.contributor=angular.extend(obj.contributor,{getFullName:function(){return obj.contributor.first_name+" "+obj.contributor.last_name}})),obj}),Restangular.extendCollection("contributions",function(collection){return collection.save=function(data){return collection.post(data).then(function(contributions){return Restangular.restangularizeCollection("contributions",contributions)})},collection}),Restangular.service("content")}),angular.module("bulbs.api").factory("ContributionRoleService",function(Restangular){return Restangular.withConfig(function(RestangularConfigurer){RestangularConfigurer.setBaseUrl("/cms/api/v1/contributions/"),RestangularConfigurer.setRequestSuffix("/")}).service("role")}).factory("ContentReportingService",function(Restangular){return Restangular.withConfig(function(RestangularConfigurer){RestangularConfigurer.setBaseUrl("/cms/api/v1/contributions/"),RestangularConfigurer.setRequestSuffix("/")}).service("contentreporting")}).factory("FreelancePayReportingService",function(Restangular,moment){return Restangular.withConfig(function(RestangularConfigurer){RestangularConfigurer.setBaseUrl("/cms/api/v1/contributions/"),RestangularConfigurer.setRequestSuffix("/")}).service("freelancereporting")}).factory("ContributionReportingService",function(Restangular,moment){return Restangular.extendModel("reporting",function(obj){return obj.user=angular.extend(obj.user,{toString:function(){return obj.user.full_name||obj.user.username}}),obj.content=angular.extend(obj.content,{toString:function(){return obj.content.title+" ("+moment(obj.content.published).format("MM/DD/YYYY h:mm a")+")"}}),obj}),Restangular.withConfig(function(RestangularConfigurer){RestangularConfigurer.setBaseUrl("/cms/api/v1/contributions/"),RestangularConfigurer.setRequestSuffix("/")}).service("reporting")}),function(){function BettyCropperService($http,$interpolate,$q,CmsConfig,BettyImage,$){function upload(){var uploadImageDeferred=$q.defer();angular.element(fileInputId).remove();var fileInput=angular.element(inputTemplate);return angular.element("body").append(fileInput),fileInput.click(),fileInput.unbind("change"),fileInput.bind("change",function(e){1!==e.target.files.length&&uploadImageDeferred.reject("We need exactly one image!");var file=e.target.files[0];0!==file.type.indexOf("image/")&&uploadImageDeferred.reject("Not an image!"),file.size>10485760&&uploadImageDeferred.reject("The file is too large!");var imageData=new FormData;imageData.append("image",file),$http({method:"POST",url:CmsConfig.buildImageApiUrl("api/new"),headers:{"X-Betty-Api-Key":CmsConfig.getImageApiKey(),"Content-Type":void 0,"X-CSRFToken":void 0},data:imageData,transformRequest:angular.identity,transformResponse:function(data,headersGetter){"string"==typeof data&&(data=$.parseJSON(data));var image=new BettyImage(data);return image}}).success(function(success){uploadImageDeferred.resolve(success)}).error(function(error){uploadImageDeferred.reject(error)})}),uploadImageDeferred.promise}function get(id){return $http({method:"GET",url:CmsConfig.buildImageApiUrl("api/"+id),headers:{"X-Betty-Api-Key":CmsConfig.getImageApiKey(),"Content-Type":void 0,"X-CSRFToken":void 0},transformRequest:angular.identity,transformResponse:function(data,headersGetter){return"string"==typeof data&&(data=$.parseJSON(data)),new BettyImage(data)}})}function detailPatch(id,name,credit,selections){return $http({method:"PATCH",url:CmsConfig.buildImageApiUrl("api/"+id),headers:{"X-Betty-Api-Key":CmsConfig.getImageApiKey(),"Content-Type":void 0,"X-CSRFToken":void 0},data:{name:name,credit:credit,selections:selections},transformRequest:angular.identity,transformResponse:function(data,headersGetter){return"string"==typeof data&&(data=$.parseJSON(data)),new BettyImage(data)}})}function updateSelection(id,ratio,selections){return $http({method:"POST",url:CmsConfig.buildImageApiUrl("api/"+id+"/"+ratio),headers:{"X-Betty-Api-Key":CmsConfig.getImageApiKey(),"Content-Type":void 0,"X-CSRFToken":void 0},data:selections})}var fileInputId="#bulbs-cms-hidden-image-file-input",inputTemplate='';this.upload=upload,this.get=get,this.detail=get,this.detailPatch=detailPatch,this.updateSelection=updateSelection}function BettyImageFactory($interpolate,$http,CmsConfig,Selection,$){function BettyImage(data){this.id=data.id,this.name=data.name,this.width=data.width,this.height=data.height,this.selections={};for(var ratio in data.selections)this.selections[ratio]=new Selection(data.selections[ratio])}return BettyImage.prototype.scaleToFit=function(width,height){var scale;if(width&&height){var fitRatio=width/height,thisRatio=this.width/this.height;scale=fitRatio>thisRatio?height/this.height:width/this.width}else width&&(scale=width/this.width),height&&(scale=height/this.height);var scaled={width:Math.round(this.width*scale),height:Math.round(this.height*scale),scale:scale};return scaled},BettyImage.prototype.getStyles=function(width,height,ratio){0===height&&(height=null);var selection=this.selections[ratio],scaledSelection=selection.scaleToFit(width,height);return{"background-image":"url("+this.url("original",1200,"jpg")+")","background-size":Math.floor(scaledSelection.width()/selection.width()*this.width)+"px","background-position":"-"+scaledSelection.x0+"px -"+scaledSelection.y0+"px",height:scaledSelection.height()+"px",width:scaledSelection.width()+"px","background-repeat":"no-repeat",position:"relative"}},BettyImage.prototype.url=function(ratio,width,format){for(var exp=$interpolate("{{ base_url }}/{{ id }}/{{ ratio }}/{{ width }}.{{ format }}"),idStr=this.id.toString(),segmentedId="",i=0;ithisRatio?height/this.height():width/this.width()}else width&&(scale=width/this.width()),height&&(scale=height/this.height());return this.scaleBy(scale)},Selection}angular.module("BettyCropper",["bulbs.cms.site.config","restangular","jquery"]).factory("Selection",SelectionFactory).factory("BettyImage",BettyImageFactory).service("BettyCropper",BettyCropperService)}(),angular.module("autocompleteBasic",["bulbs.cms.site.config","BulbsAutocomplete","BulbsAutocomplete.suggest","lodash"]).value("AUTOCOMPLETE_BASIC_DEBOUNCE",200).directive("autocompleteBasic",["_","CmsConfig",function(_,CmsConfig){return{controller:["$scope","BULBS_AUTOCOMPLETE_EVENT_KEYPRESS","AUTOCOMPLETE_BASIC_DEBOUNCE",function($scope,BULBS_AUTOCOMPLETE_EVENT_KEYPRESS,AUTOCOMPLETE_BASIC_DEBOUNCE){$scope.writables={searchTerm:""},$scope.autocompleteItems=[];var $getItems=function(){return $scope.searchFunction($scope.writables.searchTerm).then(function(data){return _.map(data,function(item){return{name:$scope.displayFormatter(item),value:item}})})};$scope.updateAutocomplete=_.debounce(function(){$scope.writables.searchTerm&&$getItems().then(function(results){$scope.autocompleteItems=results})},AUTOCOMPLETE_BASIC_DEBOUNCE),$scope.delayClearAutocomplete=function(){_.delay(function(){$scope.clearAutocomplete(),$scope.$digest()},200)},$scope.clearAutocomplete=function(){$scope.writables.searchTerm="",$scope.autocompleteItems=[]},$scope.clearSelectionOverlay=function(){$scope.clearAutocomplete(),$scope.showSelectionOverlay=!1,$scope.updateNgModel(),$scope.onSelect({})},$scope.handleKeypress=function($event){27===$event.keyCode?$scope.clearAutocomplete():40===$event.keyCode&&_.isEmpty($scope.autocompleteItems)?$scope.updateAutocomplete():$scope.$broadcast(BULBS_AUTOCOMPLETE_EVENT_KEYPRESS,$event)},$scope.handleSelect=function(selection){selection&&$scope.updateNgModel&&($scope.updateNgModel(selection),$scope.showSelectionOverlay=!0),$scope.clearAutocomplete(),$scope.onSelect({selection:selection})}}],link:function(scope,iElement,iAttrs,ngModelCtrl){var defaultFormatter=function(context){return context.item};if(scope.valueFormatter=function(viewValue){return(scope.itemValueFormatter||defaultFormatter)({item:viewValue})},scope.displayFormatter=function(modelValue){return(scope.itemDisplayFormatter||defaultFormatter)({item:modelValue})},ngModelCtrl){ngModelCtrl.$formatters.push(function(modelValue){return scope.displayFormatter(modelValue)}),ngModelCtrl.$render=function(){scope.selectedValue=ngModelCtrl.$viewValue},ngModelCtrl.$parsers.push(function(viewValue){return scope.valueFormatter(viewValue)});var unbindInitialValue=scope.$watch("initialValue",function(){scope.selectedValue=scope.initialValue});scope.updateNgModel=function(selection){unbindInitialValue();var newValue=_.isUndefined(selection)?null:angular.copy(selection.value);ngModelCtrl.$setViewValue(angular.copy(newValue)),scope.selectedValue=scope.displayFormatter(newValue)}}},require:"?ngModel",restrict:"E",scope:{hideSearchIcon:"&",inputId:"@",inputPlaceholder:"@",initialValue:"=",itemDisplayFormatter:"&",itemValueFormatter:"&",onSelect:"&",searchFunction:"=",ngDisabled:"&"},templateUrl:CmsConfig.buildComponentPath("autocomplete-basic/autocomplete-basic.html")}}]),angular.module("bettyEditable",["BettyCropper","bulbs.cms.imageCropModal","bulbs.cms.site.config","ui.bootstrap.modal"]).directive("bettyEditable",["$http","CmsConfig","BettyCropper","openImageCropModal",function($http,CmsConfig,BettyCropper,openImageCropModal){return{restrict:"E",templateUrl:CmsConfig.buildComponentPath("betty-editable/betty-editable.html"),scope:{addStyles:"@",editable:"=?",hideMetas:"=",image:"=",placeholderText:"@",ratio:"@",onChange:"&"},controller:function($scope,$element){$scope.editable=!angular.isDefined($scope.editable)||$scope.editable,$scope.upload=function(e){BettyCropper.upload().then(function(success){$scope.image={id:success.id,caption:null,alt:null},$scope.bettyImage=success},function(error){console.log(error)},function(progress){console.log(progress)})},$scope.edit=function(e){openImageCropModal($scope.image).then(function(image){null===image.id?$scope.image=null:($scope.image=image,BettyCropper.get($scope.image.id).then(function(response){$scope.bettyImage=response.data,$scope.setStyles()}))})}},link:function(scope,element,attrs){void 0===scope.bettyImage&&(scope.bettyImage=null),scope.setStyles=function(){if(scope.bettyImage)scope.imageStyling=scope.bettyImage.getStyles(element.parent().width(),null,scope.ratio);else{var ratioWidth=parseInt(scope.ratio.split("x")[0],10),ratioHeight=parseInt(scope.ratio.split("x")[1],10);scope.imageStyling={"background-color":"#333",position:"relative",width:element.parent().width(),height:Math.floor(element.parent().width()*ratioHeight/ratioWidth)+"px"}}},scope.$watch("image",function(newImage,oldImage){newImage&&newImage.id?BettyCropper.get(newImage.id).then(function(response){scope.bettyImage=response.data,oldImage&&parseInt(response.data.id,10)===oldImage.id||scope.onChange()}):angular.equals(newImage,oldImage)||scope.onChange()}),scope.$watch("bettyImage",function(newImage,oldImage){scope.setStyles()},!0),element.resize(scope.setStyles),scope.removeImage=function(){scope.image=null},scope.editImage=function(){openImageCropModal(scope.image).then(function(success){console.log(success)})}}}}]),angular.module("bulbs.cms.breadcrumb",["bulbs.cms.site.config","bulbs.cms.superFeatures.api"]).directive("breadcrumb",["$sce","CmsConfig",function($sce,CmsConfig){return{scope:{linksList:"="},link:function(scope,element,attrs){scope.renderValue=function(value){return $sce.trustAsHtml(angular.isFunction(value)?value():value)}},templateUrl:CmsConfig.buildComponentPath("breadcrumb","breadcrumb.html")}}]),angular.module("bugReporter",["bulbs.cms.config"]).directive("bugReporter",["$http","$window","CmsConfig",function($http,$window,CmsConfig){return{restrict:"E",templateUrl:CmsConfig.buildComponentPath("bug-reporter/bug-reporter-button.html"),scope:{},controller:function($scope,$element,$timeout){$scope.report={},$scope.reportButton={idle:"Submit",busy:"Sending",finished:"Sent!",error:"Error!"},$scope.modalVisible=!1,$scope.showThankYou=!1,$scope.showModal=function(){$scope.modalVisible=!0},$scope.dismissModal=function(){$scope.modalVisible=!1,$scope.showThankYou=!1},$scope.sendToWebtech=function(){var report="When I tried to:\n\n"+$scope.report.firstRes+"\n\nI thought this would happen:\n\n"+$scope.report.secondRes+"\n\n...but this happened instead:\n\n"+$scope.report.thirdRes,data={report:report,url:$window.location.href,user_agent:$window.navigator.userAgent};return $http.post("/cms/api/v1/report-bug/",data)},$scope.sendToWebtechCbk=function(promise){promise.then(function(){$scope.showThankYou=!0,$timeout(function(){$scope.dismissModal();for(var entry in $scope.report)$scope.report[entry]=""},5e3)})},$window.showBugReportModal=function(){$scope.$apply($scope.showModal())}},link:function(scope,element){}}}]),angular.module("campaignAutocomplete",["autocompleteBasic","bulbs.cms.site.config","cms.tunic.config","lodash","uuid4"]).directive("campaignAutocomplete",["$http","CmsConfig","TunicConfig","uuid4","_",function($http,CmsConfig,TunicConfig,uuid4,_){return{controller:["$scope",function($scope){$scope.itemDisplayFormatter=function(campaign){if(_.isObject(campaign))return campaign.name+" - "+campaign.id},$scope.itemValueFormatter=function(campaign){return _.isObject(campaign)?campaign.id:null},$scope.searchCampaigns=function(searchTerm){return $http.get(TunicConfig.buildBackendApiUrl("campaign/"),{params:{search:searchTerm}}).then(function(response){return response.data.results})}}],link:function(scope,iElement,iAttrs,ngModelCtrl){scope.uuid=uuid4.generate(),ngModelCtrl&&(scope.ngModel=ngModelCtrl,ngModelCtrl.$render=function(){_.isNumber(ngModelCtrl.$modelValue)&&!scope.initialValue&&$http.get(TunicConfig.buildBackendApiUrl("campaign/"+ngModelCtrl.$modelValue+"/")).then(function(result){scope.initialValue=scope.itemDisplayFormatter(result.data)})},scope.onSelect=function(selection){ngModelCtrl.$commitViewValue()})},restrict:"E",templateUrl:CmsConfig.buildComponentPath("campaign-autocomplete/campaign-autocomplete.html"),require:"ngModel",scope:{label:"@campaignAutocompleteLabel",onSelect:"&campaignAutocompleteOnSelect"}}}]),angular.module("campaigns.edit.directive",["apiServices.campaign.factory","BettyCropper","bulbs.cms.site.config","campaigns.edit.sponsorPixel","lodash","saveButton.directive","topBar"]).directive("campaignsEdit",function(CmsConfig){return{controller:function(_,$location,$q,$routeParams,$scope,Campaign){"new"===$routeParams.id?($scope.model=Campaign.$build(),$scope.isNew=!0):$scope.model=Campaign.$find($routeParams.id),window.onbeforeunload=function(e){if(!_.isEmpty($scope.model.$dirty())||$scope.isNew||$scope.needsSave)return"You have unsaved changes."},$scope.$on("$destroy",function(){delete window.onbeforeunload}),$scope.addPixel=function(){var pixel={url:"",campaign_type:""};$scope.model.pixels.push(pixel)},$scope.deletePixel=function(pixel){$scope.model.pixels=_.without($scope.model.pixels,pixel)},$scope.saveModel=function(){var promise;if($scope.model)promise=$scope.model.$save().$asPromise().then(function(data){$location.path("/cms/app/campaigns/edit/"+data.id+"/")});else{var deferred=$q.defer();deferred.reject(),promise=deferred.promise}return promise}},restrict:"E",scope:{getModelId:"&modelId"},templateUrl:CmsConfig.buildComponentPath("campaigns/campaigns-edit/campaigns-edit.html")}}),angular.module("campaigns.edit.sponsorPixel.directive",["bulbs.cms.config"]).constant("PIXEL_TYPES",[{name:"Listing",value:"Listing"},{name:"Detail",value:"Detail"}]).directive("campaignsEditSponsorPixel",function(CmsConfig){return{controller:function($scope,PIXEL_TYPES){$scope.PIXEL_TYPES=PIXEL_TYPES},restrict:"E",scope:{model:"="},templateUrl:CmsConfig.buildComponentPath("campaigns/campaigns-edit/campaigns-edit-sponsor-pixel/campaigns-edit-sponsor-pixel.html")}}),angular.module("campaigns.edit.sponsorPixel",["campaigns.edit.sponsorPixel.directive"]),angular.module("campaigns.edit",["bulbs.cms.site.config","campaigns.edit.directive"]).config(function($routeProvider){$routeProvider.when("/cms/app/campaigns/edit/:id/",{controller:function($routeParams,$scope,$window,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Edit Campaign",$scope.routeId=$routeParams.id},template:'',reloadOnSearch:!1})}),angular.module("campaigns.list",["apiServices.campaign.factory","bulbs.cms.site.config","listPage","moment"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/campaigns/",{controller:function($scope,$window,Campaign){$window.document.title=CmsConfig.getCmsName()+" | Campaign",$scope.modelFactory=Campaign},templateUrl:CmsConfig.buildComponentPath("campaigns/campaigns-list/campaigns-list-page.html")})}),angular.module("campaigns",["campaigns.edit","campaigns.list"]),angular.module("confirmationModal",["confirmationModal.factory","ui.bootstrap"]).directive("confirmationModalOpener",function(ConfirmationModal){return{restrict:"A",scope:{modalBody:"@",modalCancelText:"@",modalOkText:"@",modalOnCancel:"&",modalOnOk:"&",modalTitle:"@"},link:function(scope,element){var modalInstance=null;element.addClass("confirmation-modal-opener"),element.on("click",function(){modalInstance=new ConfirmationModal(scope)})}}}),angular.module("confirmationModal.factory",["bulbs.cms.site.config","ui.bootstrap.modal"]).factory("ConfirmationModal",function($modal,CmsConfig){var ConfirmationModal=function(scope){return function(scope){$modal.open({controller:function($scope,$modalInstance){$scope.confirm=function(){$scope.$close(),$scope.modalOnOk()},$scope.cancel=function(){$scope.$dismiss(),$scope.modalOnCancel()}},scope:scope,templateUrl:CmsConfig.buildComponentPath("confirmation-modal/confirmation-modal.html")})}(scope)};return ConfirmationModal}),angular.module("bulbs.cms.contentSearch",["autocompleteBasic","bulbs.cms.site.config","contentServices.factory","lodash"]).directive("contentSearch",["_","CmsConfig","ContentFactory",function(_,CmsConfig,ContentFactory){return{link:function(scope,element,attrs,ngModelCtrl){scope.itemDisplayFormatter=function(content){if(_.isObject(content))return content.title+" - "+content.id},scope.itemValueFormatter=function(content){return _.isObject(content)?content.id:null},scope.searchContent=function(searchTerm){return ContentFactory.all("content").getList({search:searchTerm})},ngModelCtrl?(scope.ngModel=ngModelCtrl,ngModelCtrl.$render=function(){_.isNumber(ngModelCtrl.$modelValue)&&!scope.initialValue&&ContentFactory.one("content",ngModelCtrl.$modelValue).get().then(function(content){scope.initialValue=scope.itemDisplayFormatter(content)})},scope.autocompleteOnSelect=function(){ngModelCtrl.$commitViewValue()}):scope.autocompleteOnSelect=function(selection){scope.onSelect({selection:scope.itemValueFormatter(selection.value)})}},require:"?ngModel",restrict:"E",scope:{inputId:"@",onSelect:"&",ngDisabled:"&"},templateUrl:CmsConfig.buildComponentPath("content-search","content-search.html")}}]),angular.module("bulbs.cms.currentUser.api",["bulbs.cms.site.config","lodash"]).service("CurrentUserApi",["_","$http","$q","CmsConfig",function(_,$http,$q,CmsConfig){var currentUser,pendingGetCurrentUserRequest,parsePayload=function(payload){var data=_.cloneDeep(payload);return data.displayName=data.first_name&&data.last_name?data.first_name+" "+data.last_name:data.email||data.username,data},getCurrentUser=function(){return pendingGetCurrentUserRequest||(pendingGetCurrentUserRequest=$http.get(CmsConfig.buildApiUrlRoot("me/")).then(function(response){return currentUser=parsePayload(response.data)}).finally(function(){pendingGetCurrentUserRequest=null})),pendingGetCurrentUserRequest};return{getCurrentUserWithCache:function(){return currentUser?$q.when(currentUser):getCurrentUser()},logout:function(){return $http.get(CmsConfig.buildApiUrlRoot("me","logout/")).then(function(){currentUser=null})}}}]),angular.module("bulbs.cms.currentUser",["bulbs.cms.currentUser.api","bulbs.cms.user.nameDisplayFilter"]),angular.module("bulbs.cms.user.nameDisplayFilter",[]).filter("userNameDisplay",[function(){return function(user){return user?user.full_name?user.full_name:user.first_name&&user.last_name?user.first_name+" "+user.last_name:user.username:""}}]),angular.module("bulbs.cms.customSearch.config",["lodash"]).provider("CustomSearchConfig",["_",function(_){var CustomSearchConfigError=BulbsCmsConfigError.build("CustomSearchConfig"),checkOrError=function(value,test,errorMsg){if(test(value))return value;throw new CustomSearchConfigError(errorMsg)},conditionFieldMappings=[],conditionTypes=[],timePeriodMappings=[],requestCapMs=150;this.addConditionField=function(name,endpoint,valueStructureName,valueStructureValue){return conditionFieldMappings.push({name:checkOrError(name,_.isString,"condition field name must be a string!"),endpoint:checkOrError(endpoint,_.isString,"condition field endpoint must be a string!"),value_structure:{name:checkOrError(valueStructureName,_.isString,"condition field value structure name must be a string!"),value:checkOrError(valueStructureValue,_.isString,"condition field value structure value must be a string!")}}),this},this.addConditionType=function(name,value){return conditionTypes.push({name:checkOrError(name,_.isString,"condition type name must be a string!"),value:checkOrError(value,_.isString,"condition type value must be a string!")}),this},this.addTimePeriod=function(name,value){return timePeriodMappings.push({name:checkOrError(name,_.isString,"time period name must be a string!"),value:checkOrError(value,_.isString,"time period value must be a string!")}),this},this.setRequestCapMs=function(value){return requestCapMs=checkOrError(value,_.isNumber,"request cap milliseconds must be a number!"),this},this.$get=[function(){return{getConditionFields:_.constant(conditionFieldMappings),getConditionTypes:_.constant(conditionTypes),getRequestCapMs:_.constant(requestCapMs),getTimePeriods:_.constant(timePeriodMappings)}}]}]),angular.module("customSearch.contentItem.directive",["bulbs.cms.site.config"]).directive("customSearchContentItem",function(CmsConfig){return{restrict:"E",scope:{model:"=",controllerService:"=",onUpdate:"&"},templateUrl:CmsConfig.buildComponentPath("custom-search/custom-search-content-item/custom-search-content-item.html")}}),angular.module("customSearch.contentItem",["customSearch.contentItem.directive"]),angular.module("customSearch.directive",["bulbs.cms.contentSearch","bulbs.cms.site.config","customSearch.contentItem","customSearch.group","customSearch.service"]).directive("customSearch",function(CmsConfig){return{controller:function(_,$scope,CustomSearchService){$scope.customSearchService=new CustomSearchService,$scope.resetFilters=function(){$scope.customSearchService.setPage(1),$scope.customSearchService.setQuery(""),$scope.addedFilterOn=!1,$scope.removedFilterOn=!1},$scope.$conditionalContentRetrieve=function(){$scope.addedFilterOn?$scope.customSearchService.$filterContentByIncluded():$scope.removedFilterOn?$scope.customSearchService.$filterContentByExcluded():$scope.customSearchService.$retrieveContent()},$scope.$contentRetrieve=function(){$scope.customSearchService.$retrieveContent(),$scope.onUpdate()}},link:function(scope,iElement,iAttrs,ngModelCtrl){ngModelCtrl.$formatters.push(function(modelValue){scope.customSearchService.data(modelValue),scope.customSearchService.$retrieveContent()})},require:"ngModel",restrict:"E",scope:{onUpdate:"&"},templateUrl:CmsConfig.buildComponentPath("custom-search/custom-search.html")}}),angular.module("customSearch.group.condition.directive",["bulbs.cms.site.config","BulbsAutocomplete.suggest","BulbsAutocomplete","contentServices.factory"]).directive("customSearchGroupCondition",function(CmsConfig){return{controller:function(_,$q,$scope,BULBS_AUTOCOMPLETE_EVENT_KEYPRESS,ContentFactory,CustomSearchConfig){$scope.conditionTypes=CustomSearchConfig.getConditionTypes(),$scope.fieldTypes=CustomSearchConfig.getConditionFields(), +$scope.writables={searchTerm:""},$scope.autocompleteItems=[],$scope.data=$scope.controllerService.groupsConditionsGet($scope.groupIndex,$scope.conditionIndex);var $getItems=function(){return ContentFactory.all($scope.data.field).getList({search:$scope.writables.searchTerm}).then(function(items){var field=_.find($scope.fieldTypes,function(type){return type.endpoint===$scope.data.field});return _.map(items,function(item){return{name:item[field.value_structure.name],value:item[field.value_structure.value]}})})};$scope.updateAutocomplete=function(){$scope.writables.searchTerm&&$getItems().then(function(results){$scope.autocompleteItems=results})},$scope.delayClearAutocomplete=function(){_.delay(function(){$scope.clearAutocomplete(),$scope.$digest()},200)},$scope.clearAutocomplete=function(){$scope.writables.searchTerm="",$scope.autocompleteItems=[]},$scope.handleKeypress=function($event){27===$event.keyCode?$scope.clearAutocomplete():$scope.$broadcast(BULBS_AUTOCOMPLETE_EVENT_KEYPRESS,$event)}},restrict:"E",scope:{controllerService:"=",groupIndex:"=",conditionIndex:"=",onUpdate:"&",remove:"&"},templateUrl:CmsConfig.buildComponentPath("custom-search/custom-search-group/custom-search-group-condition/custom-search-group-condition.html")}}),angular.module("customSearch.group.condition",["customSearch.group.condition.directive"]),angular.module("customSearch.group.directive",["bulbs.cms.site.config","uuid4"]).directive("customSearchGroup",function(CmsConfig){return{controller:function($scope,CustomSearchConfig,uuid4){$scope.data=$scope.controllerService.groupsGet($scope.groupIndex),$scope.timePeriods=CustomSearchConfig.getTimePeriods(),$scope.uuid=uuid4.generate(),$scope.$update=function(){$scope.controllerService.$groupsUpdateResultCountFor($scope.groupIndex).then(function(){$scope.onUpdate()})},$scope.controllerService.$groupsUpdateResultCountFor($scope.groupIndex)},restrict:"E",scope:{controllerService:"=",groupIndex:"=",remove:"&",onUpdate:"&"},templateUrl:CmsConfig.buildComponentPath("custom-search/custom-search-group/custom-search-group.html")}}),angular.module("customSearch.group",["customSearch.group.directive","customSearch.group.condition"]),angular.module("customSearch.service",["bulbs.cms.site.config","apiServices.customSearch.factory"]).factory("CustomSearchService",function(_,CustomSearch,CustomSearchConfig){var defaultData={groups:[],includedIds:[],excludedIds:[],pinnedIds:[]},CustomSearchService=function(data){this.data(data),this.$page=1,this.$query="",this.content={}};return CustomSearchService.prototype.data=function(data){return _.isUndefined(data)?this._data=defaultData:this._data=_.defaults(data,defaultData),this._data},CustomSearchService.prototype._$getContent=_.debounce(function(queryData){var self=this;return CustomSearch.$retrieveContent(queryData).then(function(data){self.content=data})},CustomSearchConfig.getRequestCapMs()),CustomSearchService.prototype.$filterContentByIncluded=function(){var contentQuery={includedIds:this._data.includedIds,page:this.$page,query:this.$query};return this._$getContent(contentQuery)},CustomSearchService.prototype.$filterContentByExcluded=function(){var contentQuery={includedIds:this._data.excludedIds,page:this.$page,query:this.$query};return this._$getContent(contentQuery)},CustomSearchService.prototype.$retrieveContent=function(){var contentQuery=_.assign({page:this.$page,query:this.$query,preview:!0},this._data);return this._$getContent(contentQuery)},CustomSearchService.prototype.$groupsUpdateResultCountFor=function(index){var self=this;return function(index){return CustomSearch.$retrieveGroupCount(self._data.groups[index]).then(function(count){self._data.groups[index].$result_count=count})}(index)},CustomSearchService.prototype.groupsResultCountGet=function(index){return this._data.groups[index].$result_count||0},CustomSearchService.prototype.groupsList=function(){return this._data.groups},CustomSearchService.prototype.groupsAdd=function(data){return _.isUndefined(data)&&(data={}),data=_.defaults(data,{conditions:[],time:null,$result_count:0}),this._data.groups.push(data),data},CustomSearchService.prototype.groupsGet=function(index){return this._data.groups[index]},CustomSearchService.prototype.groupsRemove=function(index){return this._data.groups.splice(index,1).length>0},CustomSearchService.prototype.groupsClear=function(){this._data.groups=[]},CustomSearchService.prototype.groupsConditionsAdd=function(groupIndex,data){return _.isUndefined(data)&&(data={}),data=_.defaults(data,{field:CustomSearchConfig.getConditionFields()[0].endpoint,type:CustomSearchConfig.getConditionTypes()[0].value,values:[]}),this._data.groups[groupIndex].conditions.push(data),data},CustomSearchService.prototype.groupsConditionsGet=function(groupIndex,conditionIndex){return this._data.groups[groupIndex].conditions[conditionIndex]},CustomSearchService.prototype.groupsConditionsList=function(groupIndex){return this._data.groups[groupIndex].conditions},CustomSearchService.prototype.groupsConditionsRemove=function(groupIndex,conditionIndex){return this._data.groups[groupIndex].conditions.splice(conditionIndex,1).length>0},CustomSearchService.prototype.groupsTimePeriodSet=function(groupIndex){var value=CustomSearchConfig.getTimePeriods()[0].value;return this._data.groups[groupIndex].time=value,value},CustomSearchService.prototype.groupsTimePeriodGet=function(groupIndex){return this._data.groups[groupIndex].time||null},CustomSearchService.prototype.groupsTimePeriodRemove=function(groupIndex){this._data.groups[groupIndex].time=null},CustomSearchService.prototype.groupsConditionsValuesAdd=function(groupIndex,conditionIndex,value){var values=this._data.groups[groupIndex].conditions[conditionIndex].values,matches=_.find(values,function(existingValue){return existingValue.name===value.name&&existingValue.value===value.value});matches||values.push(value)},CustomSearchService.prototype.groupsConditionsValuesClear=function(groupIndex,conditionIndex){this._data.groups[groupIndex].conditions[conditionIndex].values=[]},CustomSearchService.prototype.groupsConditionsValuesList=function(groupIndex,conditionIndex){return this._data.groups[groupIndex].conditions[conditionIndex].values},CustomSearchService.prototype.groupsConditionsValuesRemove=function(groupIndex,conditionIndex,valueIndex){return this._data.groups[groupIndex].conditions[conditionIndex].values.splice(valueIndex,1).length>0},CustomSearchService.prototype.includesList=function(){return this._data.includedIds},CustomSearchService.prototype.includesAdd=function(id){this._data.includedIds.push(id),this._data.includedIds=_.uniq(this._data.includedIds),this.excludesRemove(id)},CustomSearchService.prototype.includesRemove=function(id){this._data.includedIds=_.without(this._data.includedIds,id)},CustomSearchService.prototype.includesHas=function(id){return _.includes(this._data.includedIds,id)},CustomSearchService.prototype.excludesList=function(){return this._data.excludedIds},CustomSearchService.prototype.excludesAdd=function(id){this._data.excludedIds.push(id),this._data.excludedIds=_.uniq(this._data.excludedIds),this.includesRemove(id),this.pinsRemove(id)},CustomSearchService.prototype.excludesRemove=function(id){this._data.excludedIds=_.without(this._data.excludedIds,id)},CustomSearchService.prototype.excludesHas=function(id){return _.includes(this._data.excludedIds,id)},CustomSearchService.prototype.pinsList=function(){return this._data.pinnedIds},CustomSearchService.prototype.pinsAdd=function(id){this._data.pinnedIds.push(id),this._data.pinnedIds=_.uniq(this._data.pinnedIds),this.excludesRemove(id)},CustomSearchService.prototype.pinsRemove=function(id){this._data.pinnedIds=_.without(this._data.pinnedIds,id)},CustomSearchService.prototype.pinsHas=function(id){return _.includes(this._data.pinnedIds,id)},CustomSearchService.prototype.getPage=function(){return this.$page},CustomSearchService.prototype.setPage=function(page){this.$page=page},CustomSearchService.prototype.getQuery=function(){return this.$query},CustomSearchService.prototype.setQuery=function(query){this.$query=query},CustomSearchService}),angular.module("customSearch",["customSearch.directive"]),angular.module("bulbs.cms.dateTimeFilter",["bulbs.cms.site.config","lodash","moment"]).filter("dateTimeFormat",["_","moment","CmsConfig",function(_,moment,CmsConfig){function isInvalidDateValue(dateValue){return!_.isString(dateValue)&&!_.isDate(dateValue)&&!moment.isMoment(dateValue)}return function(date,format){return isInvalidDateValue(date)?"":(_.isString(format)||(date=moment(date),format=CmsConfig.getDateTimeFormatHumanReadable()),moment.tz(date,CmsConfig.getTimezoneName()).format(format))}}]),angular.module("bulbs.cms.dateTimeModal.controller",["bulbs.cms.site.config","moment","ui.bootstrap.modal"]).controller("DatetimeSelectionModalCtrl",["$scope","$modalInstance","CmsConfig","moment",function($scope,$modalInstance,CmsConfig,moment){$scope.nowInTimezone=function(){return $scope.dateInTimezone(moment())},$scope.dateInTimezone=function(date){return date.clone().tz(CmsConfig.getTimezoneName())},$scope.TIMEZONE_LABEL=moment.tz(CmsConfig.getTimezoneName()).format("z"),$scope.dateTime=$scope.modDatetime?$scope.dateInTimezone($scope.modDatetime):$scope.nowInTimezone(),$scope.time=$scope.dateTime.toDate(),$scope.date=$scope.dateTime.clone(),$scope.$watch("time",function(){var time=moment($scope.time),newTime=moment().year($scope.date.year()).dayOfYear($scope.date.dayOfYear()).hours(time.hours()).minutes(time.minutes());$scope.dateTime=$scope.dateInTimezone(newTime)}),$scope.$watch("date",function(){var date=$scope.date.clone(),time=moment($scope.time),newDate=moment().year($scope.date.year()).dayOfYear(date.dayOfYear()).hours(time.hours()).minutes(time.minutes());$scope.dateTime=$scope.dateInTimezone(newDate)}),$scope.setDate=function(selectedDate){$scope.date=$scope.dateInTimezone(moment(selectedDate))},$scope.isDateTimeInvalid=function(){return!$scope.dateTime.isValid()},$scope.setDateToday=function(){var today=$scope.nowInTimezone();$scope.date=$scope.dateTime.clone().dayOfYear(today.dayOfYear())},$scope.setDateTomorrow=function(){var tomorrow=moment().add(1,"day");$scope.date=$scope.dateInTimezone(tomorrow.clone())},$scope.setTimeNow=function(){var now=$scope.nowInTimezone();$scope.date=now.clone(),$scope.time=now.clone().toDate()},$scope.setTimeMidnight=function(){var midnightTonight=$scope.nowInTimezone().startOf("day");$scope.date=midnightTonight.clone(),$scope.time=midnightTonight.clone().toDate()},$scope.clearDatetime=function(){$modalInstance.close(null)},$scope.chooseDatetime=function(){$scope.dateTime.isValid()&&$modalInstance.close($scope.dateTime)}}]),angular.module("bulbs.cms.dateTimeModal.opener",["bulbs.cms.dateTimeModal.controller","bulbs.cms.site.config","ui.bootstrap.modal"]).directive("datetimeSelectionModalOpener",["$modal","$q","CmsConfig",function($modal,$q,CmsConfig){return{restrict:"A",scope:{modalClearText:"@",modalOkText:"@",modDatetime:"=?ngModel",modalTitle:"@",modalOnBeforeClose:"&",modalOnClear:"&",modalOnClose:"&"},link:function(scope,element){var modalInstance=null;element.addClass("datetime-selection-modal-opener"),element.on("click",function(){modalInstance=$modal.open({templateUrl:CmsConfig.buildComponentPath("date-time-modal","date-time-modal.html"),controller:"DatetimeSelectionModalCtrl",scope:scope}),modalInstance.result.then(function(newDate){return $q.when(scope.modalOnBeforeClose({newDate:newDate})).then(function(result){result!==!1&&(scope.modDatetime=newDate,newDate?scope.modalOnClose({newDate:newDate}):scope.modalOnClear())})})})}}}]),angular.module("bulbs.cms.dateTimeModal",["bulbs.cms.dateTimeModal.opener"]),angular.module("bulbs.cms.dynamicContent.api",["bulbs.cms.site.config"]).service("DynamicContentApi",["$http","CmsConfig",function($http,CmsConfig){return{retrieveSchema:function(url){return $http({method:"OPTIONS",url:url})}}}]),angular.module("bulbs.cms.dynamicContent.form.field.boolean",["bulbs.cms.site.config","bulbs.cms.dynamicContent.form.input.label","bulbs.cms.dynamicContent.form.input.errors"]).directive("dynamicContentFormFieldBoolean",["CmsConfig",function(CmsConfig){return{require:["ngModel","^^form"],restrict:"E",scope:{uuid:"@",name:"@",ngModel:"=",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-boolean","dynamic-content-form-field-boolean.html")}}]),angular.module("bulbs.cms.dynamicContent.form.field.color",["bulbs.cms.site.config","bulbs.cms.dynamicContent.form.input.label","bulbs.cms.dynamicContent.form.input.errors"]).directive("dynamicContentFormFieldColor",["CmsConfig",function(CmsConfig){return{require:["ngModel","^^form"],restrict:"E",scope:{uuid:"@",name:"@",ngModel:"=",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-color","dynamic-content-form-field-color.html"),link:function(scope,element,attr,ctrls){var formField=ctrls[1][scope.name];formField.$validators.rgbhex=function(modelValue){return!modelValue||/^#[0-9a-fA-F]{6}$/.test(modelValue)}}}}]),angular.module("bulbs.cms.dynamicContent.form.field.dateTime",["bulbs.cms.dateTimeFilter","bulbs.cms.dateTimeModal","bulbs.cms.dynamicContent.form.input.label","bulbs.cms.dynamicContent.form.input.errors","bulbs.cms.site.config"]).directive("dynamicContentFormFieldDateTime",["CmsConfig",function(CmsConfig){return{controller:["$scope",function($scope){$scope.setDate=function(newDate){$scope.ngModel[$scope.name]=newDate.format()},$scope.clearDate=function(){$scope.ngModel[$scope.name]=null}}],require:["ngModel","^^form"],restrict:"E",scope:{uuid:"@",name:"@",ngModel:"=",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-date-time","dynamic-content-form-field-date-time.html")}}]),angular.module("bulbs.cms.dynamicContent.form.field.image",["bettyEditable","bulbs.cms.site.config","bulbs.cms.staticImage","bulbs.cms.dynamicContent.form.input.errors","bulbs.cms.dynamicContent.form.input.label"]).directive("dynamicContentFormFieldImage",["CmsConfig",function(CmsConfig){return{require:["ngModel","^^form"],restrict:"E",scope:{name:"@",ngModel:"=",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-image","dynamic-content-form-field-image.html")}}]),angular.module("bulbs.cms.dynamicContent.form.input.errors",["bulbs.cms.site.config"]).directive("dynamicContentFormFieldInputErrors",["CmsConfig",function(CmsConfig){return{link:function(scope,element,attrs,formCtrl){scope.form=formCtrl},require:"^^form",restrict:"E",scope:{name:"@",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-input-errors","dynamic-content-form-field-input-errors.html")}}]),angular.module("bulbs.cms.dynamicContent.form.input.label",["bulbs.cms.site.config","lodash"]).directive("dynamicContentFormFieldInputLabel",["_","CmsConfig",function(_,CmsConfig){return{link:function(scope,element,attrs,formCtrl){scope.form=formCtrl,scope.isEmpty=_.isEmpty},require:"^^form",restrict:"E",scope:{inputId:"@",name:"@",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-input-label","dynamic-content-form-field-input-label.html")}}]),angular.module("bulbs.cms.dynamicContent.form.field.integer",["bulbs.cms.site.config","bulbs.cms.dynamicContent.form.input.label","bulbs.cms.dynamicContent.form.input.errors"]).directive("dynamicContentFormFieldInteger",["CmsConfig",function(CmsConfig){return{require:["ngModel","^^form"],restrict:"E",scope:{uuid:"@",name:"@",ngModel:"=",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-integer","dynamic-content-form-field-integer.html"),link:function(scope,element,attr,ctrls){var formField=ctrls[1][scope.name];formField.$validators.integer=function(modelValue){return!modelValue||parseInt(modelValue,10)===modelValue}}}}]),angular.module("bulbs.cms.dynamicContent.form.field.invalid",["bulbs.cms.dynamicContent.form.input.label","bulbs.cms.site.config"]).directive("dynamicContentFormFieldInvalid",["CmsConfig",function(CmsConfig){return{require:"^^form",restrict:"E",scope:{name:"@",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-invalid","dynamic-content-form-field-invalid.html")}}]),angular.module("bulbs.cms.dynamicContent.form.field.list",["bulbs.cms.dynamicContent.form.field.object","bulbs.cms.dynamicContent.form.input.label","bulbs.cms.dynamicContent.form.types","bulbs.cms.site.config","bulbs.cms.utils","lodash"]).directive("dynamicContentFormFieldList",["CmsConfig","FIELD_TYPES_META","Utils",function(CmsConfig,FIELD_TYPES_META,Utils){return{controller:["_","$scope",function(_,$scope){_.isUndefined($scope.ngModel[$scope.name])&&($scope.ngModel[$scope.name]=[]),$scope.itemOrderingMemory=[],$scope.redoOrdering=function(){$scope.itemOrderingMemory=$scope.ngModel[$scope.name].map(function(v,i){return i+1})},$scope.redoOrdering(),$scope.newItem=function(){$scope.readOnly||($scope.ngModel[$scope.name].push({}),$scope.redoOrdering())},$scope.moveItem=function(fromIndex,toIndex){Utils.moveTo($scope.ngModel[$scope.name],fromIndex,toIndex,!0),$scope.redoOrdering()},$scope.removeItem=function(index){Utils.removeFrom($scope.ngModel[$scope.name],index),$scope.redoOrdering()}}],link:function(scope,elements,attrs,ctrls){scope.form=ctrls[1],0===scope.ngModel[scope.name].length&&scope.newItem()},require:["ngModel","^^form"],restrict:"E",scope:{name:"@",schema:"=",ngModel:"=",readOnly:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-list","dynamic-content-form-field-list.html")}}]),angular.module("bulbs.cms.dynamicContent.form.field.object",["bulbs.cms.dynamicContent.form.field.boolean","bulbs.cms.dynamicContent.form.field.color","bulbs.cms.dynamicContent.form.field.dateTime","bulbs.cms.dynamicContent.form.field.image","bulbs.cms.dynamicContent.form.field.list","bulbs.cms.dynamicContent.form.field.integer","bulbs.cms.dynamicContent.form.field.invalid","bulbs.cms.dynamicContent.form.field.richtext","bulbs.cms.dynamicContent.form.field.text","bulbs.cms.dynamicContent.form.input.label","bulbs.cms.dynamicContent.form.types","bulbs.cms.site.config","lodash","uuid4"]).directive("dynamicContentFormFieldObject",["_","$compile","CmsConfig","FIELD_TYPES_META","uuid4",function(_,$compile,CmsConfig,FIELD_TYPES_META,uuid4){return{link:function(scope,element,attrs){var $form=element.find("ng-form");scope.hideLabel="hideLabel"in attrs,scope.$watch("form.$valid",function(isValid){scope.onValidityChange({isValid:isValid,internalForm:scope.form})}),scope.$watch("schema",function(){if(_.has(scope.schema,"fields")){var fieldKeys=Object.keys(scope.schema.fields),includeOnlyProvided=_.isArray(scope.includeOnly),hideOnlyChildLabel=!1;includeOnlyProvided&&(fieldKeys=_.intersection(fieldKeys,scope.includeOnly),1===scope.includeOnly.length&&(hideOnlyChildLabel=!0)),fieldKeys.forEach(function(id){var fieldSchema=scope.schema.fields[id],fieldMeta=FIELD_TYPES_META[fieldSchema.type];_.isUndefined(fieldMeta)&&(fieldMeta=_.has(fieldSchema,"fields")?FIELD_TYPES_META.object:FIELD_TYPES_META.invalid);var tagName=fieldMeta.tagName,html=angular.element("<"+tagName+">");html.attr("uuid",uuid4.generate()),html.attr("name",id),html.attr("schema","schema.fields."+id),html.attr("class","dynamic-content-form-field"),hideOnlyChildLabel&&html.attr("hide-label","true"),_.isString(scope.name)?html.attr("ng-model","ngModel[name]"):html.attr("ng-model","ngModel"),$form.append(html),$compile(html)(scope)})}},!0)},require:"ngModel",restrict:"E",scope:{name:"@",schema:"=",ngModel:"=",onValidityChange:"&",includeOnly:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-object","dynamic-content-form-field-object.html")}}]),angular.module("bulbs.cms.dynamicContent.form.field.richtext",["bulbs.cms.site.config","bulbs.cms.dynamicContent.form.input.label","bulbs.cms.dynamicContent.form.input.errors","OnionEditor"]).directive("dynamicContentFormFieldRichtext",["CmsConfig",function(CmsConfig){return{link:function(scope,element,attr,ctrls){scope.formField=ctrls[1][scope.name]},require:["ngModel","^^form"],restrict:"E",scope:{name:"@",ngModel:"=",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-richtext","dynamic-content-form-field-richtext.html")}}]),angular.module("bulbs.cms.dynamicContent.form.field.text",["bulbs.cms.site.config","bulbs.cms.dynamicContent.form.input.label","bulbs.cms.dynamicContent.form.input.errors","jquery"]).directive("dynamicContentFormFieldText",["CmsConfig",function(CmsConfig){return{link:function(scope,element,attr,ctrls){if(scope.formField=ctrls[1][scope.name],scope.schema.max_length>0){var input=element.find("input");input.css("width",scope.schema.max_length+"em"),element.width()<=input.width()&&input.css("width","")}},require:["ngModel","^^form"],restrict:"E",scope:{uuid:"@",name:"@",ngModel:"=",schema:"="},templateUrl:CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form","dynamic-content-form-field","dynamic-content-form-field-text","dynamic-content-form-field-text.html")}}]),angular.module("bulbs.cms.dynamicContent.form.types",[]).constant("FIELD_TYPES_META",{array:{tagName:"dynamic-content-form-field-list"},boolean:{tagName:"dynamic-content-form-field-boolean"},color:{tagName:"dynamic-content-form-field-color"},datetime:{tagName:"dynamic-content-form-field-date-time"},image:{tagName:"dynamic-content-form-field-image"},invalid:{tagName:"dynamic-content-form-field-invalid"},object:{tagName:"dynamic-content-form-field-object"},richtext:{tagName:"dynamic-content-form-field-richtext"},string:{tagName:"dynamic-content-form-field-text"},integer:{tagName:"dynamic-content-form-field-integer"}}),angular.module("bulbs.cms.dynamicContent.form",["bulbs.cms.dynamicContent.api","bulbs.cms.dynamicContent.form.field.object","bulbs.cms.site.config","lodash"]).directive("dynamicContentForm",["_","CmsConfig",function(_,CmsConfig){var DynamicContentFormError=BulbsCmsError.build(""),template=function(name){return CmsConfig.buildComponentPath("dynamic-content","dynamic-content-form",name)};return{controller:["$scope","DynamicContentApi",function($scope,DynamicContentApi){if(!_.isString($scope.schemaSrc))throw new DynamicContentFormError("must be provided a schema url!");if(!_.isObject($scope.ngModel))throw new DynamicContentFormError("must be provided an object for ng-model!");$scope.template=template("dynamic-content-form-loading.html"),$scope.schema={},$scope.validityCallback=function(isValid){$scope.onValidityChange({isValid:isValid})},DynamicContentApi.retrieveSchema($scope.schemaSrc).then(function(response){$scope.template=template("dynamic-content-form-loaded.html"),$scope.schema=response.data}).catch(function(){$scope.template=template("dynamic-content-form-error.html"),$scope.errorMessage="Unable to retrieve schema"})}],require:"ngModel",restrict:"E",scope:{schemaSrc:"@",ngModel:"=",onValidityChange:"&",includeOnly:"="},template:''}}]),angular.module("bulbs.cms.dynamicContent",["bulbs.cms.dynamicContent.form"]),angular.module("bulbs.cms.editor.wrapper",["BettyCropper","bulbs.cms.imageCropModal","bulbs.cms.site.config","jquery","OnionEditor"]).run(["$",function($){$(document).unbind("keydown").bind("keydown",function(event){var doPrevent=!1;if(8===event.keyCode){var d=event.srcElement||event.target;doPrevent=["TEXTAREA","INPUT"].indexOf(d.tagName.toUpperCase()!==-1)?d.readOnly||d.disabled:!d.isContentEditable}doPrevent&&event.preventDefault()})}]).directive("onionEditor",["$","BettyCropper","CmsConfig","OnionEditor","openImageCropModal","Zencoder",function($,BettyCropper,CmsConfig,OnionEditor,openImageCropModal,Zencoder){var safeApply=function(scope,fn){scope.$$phase||scope.$root.$$phase?fn():scope.$apply(function(){fn()})};return{controller:["$scope",function($scope){$scope.editor=null,this.getEditor=function(){return $scope.editor}}],require:"ngModel",replace:!0,restrict:"E",templateUrl:CmsConfig.buildComponentPath("editor","editor-wrapper","editor-wrapper.html"),scope:{ngModel:"="},link:function(scope,element,attrs,ngModel){function read(){safeApply(scope,function(){var html=scope.editor.getContent();html===defaultValue&&(html=""),ngModel.$setViewValue(html)})}if(ngModel){var formatting;attrs.formatting&&(formatting=attrs.formatting.split(","));var options={},defaultValue="";"multiline"===attrs.role?(defaultValue="


    ",options={multiline:!0,formatting:formatting||["link","bold","italic","blockquote","heading","list","strike","underline"],placeholder:{text:attrs.placeholder||"

    Write here

    ",container:$(".editorPlaceholder",element[0])[0]},link:{domain:attrs.linkDomain||!1,searchHandler:window[attrs.linkSearchHandler]||!1},statsContainer:".wordcount",inlineObjects:attrs.inlineObjects||CmsConfig.getInlineObjecsPath(),image:{insertDialog:BettyCropper.upload,editDialog:openImageCropModal},video:{insertDialog:Zencoder.onVideoFileUpload,editDialog:Zencoder.openVideoThumbnailModal,videoEmbedUrl:CmsConfig.buildVideoUrl()}}):($(".document-tools, .embed-tools",element).hide(),defaultValue="",options={multiline:!1,placeholder:{text:attrs.placeholder||"Write here",container:$(".editorPlaceholder",element[0])[0]},formatting:formatting||[]}),scope.editor=new OnionEditor($(".editor",element[0])[0],options),ngModel.$render=function(){scope.editor.setContent(ngModel.$viewValue||defaultValue),setTimeout(function(){scope.editor.setChangeHandler(read)})},ngModel.$isEmpty=function(value){return!value||scope.editor.scribe.allowsBlockElements()&&value===defaultValue},scope.$watch(ngModel,function(){scope.editor.setContent(ngModel.$viewValue||defaultValue),window.picturefill&&window.picturefill(element[0])})}}}}]),angular.module("bulbs.cms.editor",["bulbs.cms.editor.wrapper"]),angular.module("EditorsPick",["bulbs.cms.site.config","customSearch"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/sod/",{controller:function($scope,$window){$window.document.title=CmsConfig.getCmsName()+" | SoD",$scope.$watch("queryData",function(){console.log(arguments)}),$scope.queryData={},$scope.updateQueryData=function(){$scope.queryData={groups:[{conditions:[{field:"content-type",type:"all",values:[{name:"for display",value:"actually-use-this-value-123"}]}],time:"1 day"}],included_ids:[1],excluded_ids:[2],pinned_ids:[3],page:1,query:"query balh blah blahb"}},$scope.updateConditionData=function(){$scope.queryData.groups[0].conditions=[{field:"content-type",type:"all",values:[{name:"ANOTHER THIGN",value:"actually-use-this-value-123"}]}]}},templateUrl:CmsConfig.buildComponentPath("editors-pick/editors-pick.html"),reloadOnSearch:!1})}),angular.module("evergreenField.directive",["lodash","saveButton.directive"]).directive("evergreenField",["CmsConfig",function(CmsConfig){return{restrict:"E",scope:{article:"="},templateUrl:CmsConfig.buildComponentPath("evergreen-field/evergreen-field.html")}}]),angular.module("evergreenField",["evergreenField.directive"]),angular.module("filterListWidget.directive",["bulbs.cms.site.config"]).directive("filterListWidget",function(_,$http,$location,$timeout,$,CmsConfig){return{restrict:"E",scope:{filters:"="},templateUrl:CmsConfig.buildComponentPath("filter-list-widget/filter-list-widget.html"),link:function(scope,element,attrs){function getAutocompletes(search){return $timeout.cancel(filterInputTimeout),filterInputCounter=0,search.length<1?(scope.autocompleteArray=[],void scope.$apply()):void $http({url:"/cms/api/v1/things/?type=tag&type=feature_type&type=author",method:"GET",params:{q:search}}).success(function(data){scope.autocompleteArray=data})}function arrowSelect(direction){var $toSelect,$entries=$element.find(".entry"),$selected=$element.find(".entry.selected");$selected.length>0?("up"===direction&&($toSelect=$selected.first().prev()),"down"===direction&&($toSelect=$selected.first().next())):("up"===direction&&($toSelect=$entries.last()),"down"===direction&&($toSelect=$entries.first())),scope.selectEntry($toSelect)}function applyFilterChange(filterObject){filterObject.page=1,$location.search(filterObject),scope.autocompleteArray=[],$input.trigger("blur")}function getFilterObjects(){var search=$location.search();if(scope.filters={},"undefined"==typeof search)return void console.log("undefined");var filterParamsToTypes={authors:"author",tags:"tag",feature_types:"feature_type"};for(var filterParam in filterParamsToTypes){var filterType=filterParamsToTypes[filterParam];"string"==typeof search[filterParam]&&(search[filterParam]=[search[filterParam]]);for(var i in search[filterParam]){var value=search[filterParam][i];scope.filters[filterType+value]={query:value,type:filterParam},getQueryToLabelMappings(filterType,value)}}search.search&&(scope.filterInputValue=search.search)}function getQueryToLabelMappings(type,query){scope.queryToLabelMappings=scope.queryToLabelMappings||{},query in scope.queryToLabelMappings||$http({url:"/cms/api/v1/things/?type="+type,method:"GET",params:{q:query}}).success(function(data){for(var i in data)scope.queryToLabelMappings[data[i].value]=data[i].name})}var $element=$(element),$input=$element.find("input");scope.autocompleteArray=[];var filterInputTimeout,filterInputCounter=0;$input.on("input",function(e){var search=$input.val();scope.searchTerm=search,$timeout.cancel(filterInputTimeout),filterInputTimeout=$timeout(function(){getAutocompletes(search)},200),filterInputCounter>2&&getAutocompletes(search)}),$input.on("keyup",function(e){38===e.keyCode&&arrowSelect("up"),40===e.keyCode&&arrowSelect("down"),13===e.keyCode&&($element.find(".selected").length>0?$timeout(function(){angular.element(".selected > a").triggerHandler("click")},0):scope.addFilter("search",$input.val()))}),scope.search=function(){scope.addFilter("search",scope.filterInputValue)},scope.clearSearch=function(){scope.filterInputValue=""},scope.clearFilters=function(){return scope.filters={},scope.filterInputValue="",applyFilterChange({})},$element.on("mouseover",".entry",function(){scope.selectEntry(this)}),scope.selectEntry=function(entry){$element.find(".selected").removeClass("selected"),$(entry).addClass("selected")},$input.on("blur",function(){$element.find(".dropdown-menu").fadeOut(200)}),$input.on("focus",function(){$element.find(".dropdown-menu").fadeIn(200)}),scope.addFilter=function(type,newFilterValue){var filterObject=$location.search();return"search"===type?filterObject.search=newFilterValue:(filterObject[type]||(filterObject[type]=[]),"string"==typeof filterObject[type]&&(filterObject[type]=[filterObject[type]]),_.contains(filterObject[type],newFilterValue)||filterObject[type].push(newFilterValue)),applyFilterChange(filterObject)},scope.deleteFilter=function(key){var filterObject=$location.search(),toDelete=scope.filters[key];"string"==typeof filterObject[toDelete.type]&&(filterObject[toDelete.type]=[filterObject[toDelete.type]]);var toSplice;for(var i in filterObject[toDelete.type])if(filterObject[toDelete.type][i]===toDelete.query){toSplice=i;break}return filterObject[toDelete.type].splice(i,1),filterObject.search=$input.val(),delete scope.filters[key],applyFilterChange(filterObject); +},scope.$on("$routeUpdate",function(){getFilterObjects()}),getFilterObjects()}}}),angular.module("filterListWidget",["filterListWidget.directive"]),angular.module("filterWidget.directive",["contentServices.listService"]).directive("filterWidget",function(_,$http,$location,$timeout,$,ContentListService,CmsConfig){return{restrict:"E",scope:{},templateUrl:CmsConfig.buildComponentPath("filter-widget/filter-widget.html"),link:function(scope,element,attrs){function getAutocompletes(search){return $timeout.cancel(filterInputTimeout),filterInputCounter=0,search.length<1?(scope.autocompleteArray=[],void scope.$apply()):void $http({url:"/cms/api/v1/things/?type=tag&type=feature_type&type=author",method:"GET",params:{q:search}}).success(function(data){scope.autocompleteArray=data})}function arrowSelect(direction){var $toSelect,$entries=$element.find(".entry"),$selected=$element.find(".entry.selected");$selected.length>0?("up"===direction&&($toSelect=$selected.first().prev()),"down"===direction&&($toSelect=$selected.first().next())):("up"===direction&&($toSelect=$entries.last()),"down"===direction&&($toSelect=$entries.first())),scope.selectEntry($toSelect)}function applyFilterChange(filterObject){return filterObject.page=1,$location.search(filterObject),scope.autocompleteArray=[],$input.trigger("blur"),ContentListService.$updateContent(filterObject)}function getFilterObjects(){var search=$location.search();if(scope.filterObjects={},"undefined"==typeof search)return void console.log("undefined");var filterParamsToTypes={authors:"author",tags:"tag",feature_types:"feature_type"};for(var filterParam in filterParamsToTypes){var filterType=filterParamsToTypes[filterParam];"string"==typeof search[filterParam]&&(search[filterParam]=[search[filterParam]]);for(var i in search[filterParam]){var value=search[filterParam][i];scope.filterObjects[filterType+value]={query:value,type:filterParam},getQueryToLabelMappings(filterType,value)}}search.search&&(scope.filterInputValue=search.search)}function getQueryToLabelMappings(type,query){scope.queryToLabelMappings=scope.queryToLabelMappings||{},query in scope.queryToLabelMappings||$http({url:"/cms/api/v1/things/?type="+type,method:"GET",params:{q:query}}).success(function(data){for(var i in data)scope.queryToLabelMappings[data[i].value]=data[i].name})}var $element=$(element),$input=$element.find("input");scope.autocompleteArray=[];var filterInputTimeout,filterInputCounter=0;$input.on("input",function(e){var search=$input.val();scope.searchTerm=search,$timeout.cancel(filterInputTimeout),filterInputTimeout=$timeout(function(){getAutocompletes(search)},200),filterInputCounter>2&&getAutocompletes(search)}),$input.on("keyup",function(e){38===e.keyCode&&arrowSelect("up"),40===e.keyCode&&arrowSelect("down"),13===e.keyCode&&($element.find(".selected").length>0?$timeout(function(){angular.element(".selected > a").triggerHandler("click")},0):scope.addFilter("search",$input.val()))}),scope.search=function(){scope.addFilter("search",scope.filterInputValue)},scope.clearSearch=function(){scope.filterInputValue=""},scope.clearFilters=function(){return scope.filterObjects={},scope.filterInputValue="",applyFilterChange({})},$element.on("mouseover",".entry",function(){scope.selectEntry(this)}),scope.selectEntry=function(entry){$element.find(".selected").removeClass("selected"),$(entry).addClass("selected")},$input.on("blur",function(){$element.find(".dropdown-menu").fadeOut(200)}),$input.on("focus",function(){$element.find(".dropdown-menu").fadeIn(200)}),scope.addFilter=function(type,newFilterValue){var filterObject=$location.search();return"search"===type?filterObject.search=newFilterValue:(filterObject[type]||(filterObject[type]=[]),"string"==typeof filterObject[type]&&(filterObject[type]=[filterObject[type]]),_.contains(filterObject[type],newFilterValue)||filterObject[type].push(newFilterValue)),applyFilterChange(filterObject)},scope.deleteFilter=function(key){var filterObject=$location.search(),toDelete=scope.filterObjects[key];"string"==typeof filterObject[toDelete.type]&&(filterObject[toDelete.type]=[filterObject[toDelete.type]]);var toSplice;for(var i in filterObject[toDelete.type])if(filterObject[toDelete.type][i]===toDelete.query){toSplice=i;break}return filterObject[toDelete.type].splice(i,1),filterObject.search=$input.val(),delete scope.filterObjects[key],applyFilterChange(filterObject)},scope.$on("$routeUpdate",function(){getFilterObjects()}),getFilterObjects()}}}),angular.module("filterWidget",["filterWidget.directive"]),angular.module("genericAjaxButton.controller",[]).controller("GenericAjaxButtonController",function($scope){$scope.STATES={DONE:"done",PROGRESS:"in-progress",ERROR:"error"},$scope.doClick=function(){$scope.state=$scope.STATES.PROGRESS,$scope.clickFunction().then(function(){$scope.state=$scope.STATES.DONE}).catch(function(){$scope.state=$scope.STATES.ERROR})}}),angular.module("genericAjaxButton.directive",["bulbs.cms.site.config","genericAjaxButton.controller"]).directive("genericAjaxButton",function(CmsConfig){return{controller:"GenericAjaxButtonController",restrict:"E",scope:{disableWhen:"&",clickFunction:"=",cssBtnClassComplete:"@",cssBtnClassError:"@",cssBtnClassProgress:"@",cssIconComplete:"@",textError:"@",textProgress:"@",textComplete:"@"},templateUrl:CmsConfig.buildComponentPath("generic-ajax-button/generic-ajax-button.html")}}),angular.module("genericAjaxButton",["genericAjaxButton.directive"]),angular.module("saveButton.directive",["bulbs.cms.site.config","genericAjaxButton"]).directive("saveButton",function(CmsConfig){return{controller:"GenericAjaxButtonController",link:{pre:function(scope){scope.cssIconComplete="glyphicon-floppy-disk",scope.textProgress="Saving...",scope.textComplete="Save"}},restrict:"E",scope:{disableWhen:"&",clickFunction:"="},templateUrl:CmsConfig.buildComponentPath("generic-ajax-button/generic-ajax-button.html")}}),angular.module("hideFromRssField.directive",["bulbs.cms.site.config"]).directive("hideFromRssField",["CmsConfig",function(CmsConfig){return{restrict:"E",scope:{article:"="},templateUrl:CmsConfig.buildComponentPath("hide-from-rss-field","hide-from-rss-field.html")}}]),angular.module("hideFromRssField",["hideFromRssField.directive"]),angular.module("bulbs.cms.imageCropModal.controller",["BettyCropper","jquery","ui.bootstrap.modal"]).controller("ImageCropModalCtrl",["$","$modalInstance","$scope","$timeout","BettyCropper","Selection","imageData","ratios",function($,$modalInstance,$scope,$timeout,BettyCropper,Selection,imageData,ratios){$scope.selectedCrop=null,$scope.cropMode=!1,$scope.ratios=ratios,$scope.finished=!1,$scope.thumb_container_styles={},$scope.imageData=imageData,$scope.image||($scope.image=null,BettyCropper.get(imageData.id).then(function(success){$scope.image=success.data})),$scope.$watch("image",function(image){if(image){var finished=!0;for(var ratio in image.selections)if("auto"===image.selections[ratio].source){finished=!1;break}$scope.finished=finished,$scope.scaleData=image.scaleToFit(550,400),angular.element(".crop-image-container img").one("load",function(){$(this).Jcrop({allowSelect:!1,allowMove:!0,allowResize:!0,keySupport:!1},function(){$scope.jcrop_api=this})}),$scope.image_url=image.url("original",1200,"jpg"),$scope.ratios||($scope.ratios=Object.keys(image.selections)),$scope.setThumbStyles()}}),$scope.$watch("selectedCrop",function(crop){if($scope.image){var finished=!0;for(var ratio in $scope.image.selections)if("auto"===$scope.image.selections[ratio].source&&ratio!==crop){finished=!1;break}$scope.finished=finished}}),$scope.selectCrop=function(ratio){if(!ratio){ratio=Object.keys($scope.image.selections)[0];for(var key in $scope.image.selections)if("auto"===$scope.image.selections[key].source){ratio=key;break}}var selection=$scope.image.selections[ratio].scaleBy($scope.scaleData.scale);$scope.jcrop_api&&($scope.jcrop_api.setOptions({aspectRatio:selection.width()/selection.height()}),$scope.jcrop_api.setSelect([selection.x0,selection.y0,selection.x1,selection.y1])),$scope.cropMode=!0,$scope.selectedCrop=ratio},$scope.setThumbStyles=function(){$scope.thumb_styles=$scope.thumb_styles||{};for(var ratio in $scope.image.selections){var scaledSelection=$scope.image.selections[ratio].scaleToFit(170,170);$scope.thumb_container_styles[ratio]={"padding-top":Math.round((180-scaledSelection.height())/2)+"px","padding-bottom":"5px","padding-left":Math.round((180-scaledSelection.width())/2)+"px","padding-right":"5px"},$scope.thumb_styles[ratio]=$scope.image.getStyles(170,170,ratio)}},$scope.save=function(ratio){var jcrop_selection=$scope.jcrop_api.tellSelect(),newSelection=new Selection({x0:jcrop_selection.x,x1:jcrop_selection.x2,y0:jcrop_selection.y,y1:jcrop_selection.y2,source:"user"});return newSelection=newSelection.scaleBy(1/$scope.scaleData.scale),newSelection.x1>$scope.image.width&&(newSelection.x1=$scope.image.width),newSelection.y1>$scope.image.height&&(newSelection.y1=$scope.image.height),this.image.updateSelection(ratio,newSelection)},$scope.saveAndQuit=function(){var ratio=$scope.selectedCrop;this.save(ratio).then(function(success){var ratio=success.data[0],selection=success.data[1];$scope.image.selections[ratio]=selection}),$scope.cropMode=!1,$modalInstance.close(imageData)},$scope.saveAndNext=function(){var ratio=$scope.selectedCrop;this.save(ratio).then(function(success){var ratio=success.data[0],selection=success.data[1];$scope.image.selections[ratio]=selection;var nextRatioIndex=($scope.ratios.indexOf(ratio)+1)%$scope.ratios.length;$scope.selectCrop($scope.ratios[nextRatioIndex])})}}]),angular.module("bulbs.cms.imageCropModal",["bulbs.cms.imageCropModal.controller","bulbs.cms.site.config","ui.bootstrap.modal"]).factory("openImageCropModal",["$modal","CmsConfig",function($modal,CmsConfig){return function(imageData,ratios){return $modal.open({templateUrl:CmsConfig.buildComponentPath("image-crop-modal","image-crop-modal.html"),controller:"ImageCropModalCtrl",resolve:{imageData:function(){return imageData},ratios:function(){return ratios||!1}},backdrop:"static"}).result}}]),angular.module("bulbs.cms.liveBlog.api",["bulbs.cms.site.config","bulbs.cms.utils","lodash","moment"]).service("LiveBlogApi",["_","$http","CmsConfig","moment","Utils",function(_,$http,CmsConfig,moment,Utils){var liveBlogEndpoint=CmsConfig.buildApiUrlRoot.bind(null,"liveblog"),liveBlogEntryEndpoint=liveBlogEndpoint.bind(null,"entry"),parsePayload=function(payload){var data=_.cloneDeep(payload);return payload.published&&(data.published=moment.tz(payload.published,CmsConfig.getTimezoneName())),payload.created&&(data.created=moment.tz(payload.created,CmsConfig.getTimezoneName())),payload.updated&&(data.updated=moment.tz(payload.updated,CmsConfig.getTimezoneName())),data},cleanData=function(data){var payload=_.chain(data).omit("published").cloneDeep().value();return data.published&&(payload.published=data.published.format()),data.created&&(payload.created=data.created.format()),data.updated&&(payload.updated=data.updated.format()),payload};return{createEntry:function(data){var payload=cleanData(data);return $http.post(liveBlogEntryEndpoint("/"),payload).then(function(response){return parsePayload(response.data)})},updateEntry:function(entry){var payload=cleanData(entry);return $http.put(liveBlogEntryEndpoint(payload.id,"/"),payload).then(function(response){return parsePayload(response.data)})},deleteEntry:function(entry){return $http.delete(liveBlogEntryEndpoint(entry.id,"/"))},getEntries:function(id){var params;return id&&(params=Utils.param({liveblog:id})),$http.get(liveBlogEntryEndpoint("/",params)).then(function(response){return{results:response.data.results.map(function(result){return parsePayload(result)})}})}}}]),angular.module("bulbs.cms.liveBlog.entries.authorBridge",["bulbs.cms.site.config"]).directive("liveBlogEntriesAuthorBridge",["$compile","CmsConfig",function($compile,CmsConfig){return{link:function(scope,element){var name=CmsConfig.getLiveBlogAuthorSelectorDirectiveName();if(scope.authors=scope.ngModel,name){var html=angular.element("<"+name+' ng-model="authors">");element.find("> div").html($compile(html)(scope))}scope.$watch("authors",function(newValue,oldValue){angular.equals(newValue,oldValue)||(scope.onUpdate({newValue:newValue}),scope.ngModel=newValue)},!0)},restrict:"E",scope:{ngModel:"=",onUpdate:"&"},template:'
    No live blog author selector has been configured!
    '}}]),angular.module("bulbs.cms.liveBlog.entries",["bulbs.cms.currentUser","bulbs.cms.dateTimeFilter","bulbs.cms.dateTimeModal","bulbs.cms.dateTimeModal","bulbs.cms.liveBlog.api","bulbs.cms.liveBlog.entries.authorBridge","bulbs.cms.site.config","bulbs.cms.utils","confirmationModal","OnionEditor","Raven"]).directive("liveBlogEntries",["$q","CmsConfig","CurrentUserApi","LiveBlogApi","Raven","Utils",function($q,CmsConfig,CurrentUserApi,LiveBlogApi,Raven,Utils){return{link:function(scope){var reportError=function(message,data){Raven.captureMessage(message,data),scope.errorMessage=message},titleDisplay=function(entry){return entry.headline?'"'+entry.headline+'"':"an entry"};scope.clearError=function(){scope.errorMessage=""},LiveBlogApi.getEntries(scope.article.id).then(function(response){scope.entries=response.results}).catch(function(response){var message="An error occurred retrieving entries!";reportError(message,{response:response})});var panelOpen={};scope.isPanelOpen=function(entry){return angular.isUndefined(panelOpen[entry.id])&&(panelOpen[entry.id]=!0),panelOpen[entry.id]},scope.togglePanel=function(entry){panelOpen[entry.id]=!panelOpen[entry.id]},scope.collapseAll=function(){scope.entries.forEach(function(entry){panelOpen[entry.id]=!1})},scope.expandAll=function(){scope.entries.forEach(function(entry){panelOpen[entry.id]=!0})};var entryForm="entryForm_";scope.wrapperForm={},scope.makeEntryFormName=function(entry){return entryForm+entry.id},scope.getEntryForm=function(entry){var name=scope.makeEntryFormName(entry);return scope.wrapperForm[name]?scope.wrapperForm[name]:(scope.wrapperForm[name]={},scope.wrapperForm[name])},scope.isEntryFormSaveDisabled=function(entry){return scope.transactionsLocked()||scope.getEntryForm(entry).$pristine};var lock=Utils.buildLock();scope.transactionsLocked=lock.isLocked,scope.addEntry=lock(function(){return CurrentUserApi.getCurrentUserWithCache().then(function(user){var now=moment();return LiveBlogApi.createEntry({liveblog:scope.article.id,created_by:user,created:now,updated_by:user,updated:now}).then(function(entry){scope.entries.unshift(entry)}).catch(function(response){var message="An error occurred attempting to add an entry!";reportError(message,{response:response})})})}),scope.saveEntry=lock(function(entry){return CurrentUserApi.getCurrentUserWithCache().then(function(user){var oldUpdateBy=entry.updated_by,oldUpdated=entry.updated;return entry.updated_by=user,entry.updated=moment(),LiveBlogApi.updateEntry(entry).then(function(){scope.getEntryForm(entry).$setPristine()}).catch(function(response){entry.updated_by=oldUpdateBy,entry.updated=oldUpdated;var message="An error occurred attempting to save "+titleDisplay(entry)+"!";return reportError(message,{response:response}),$q.reject()})})}),scope.publishAndSave=function(entry,newDate){var oldDate=entry.published;return entry.published=newDate,scope.saveEntry(entry).then(function(){scope.entries.sort(function(entry1,entry2){var entry1IsMoment=moment.isMoment(entry1.published),entry2IsMoment=moment.isMoment(entry2.published);return!entry1IsMoment||entry2IsMoment&&!entry1.published.isBefore(entry2.published)?!entry2IsMoment||entry1IsMoment&&!entry2.published.isBefore(entry1.published)?0:-1:1})}).catch(function(){return entry.published=oldDate,!1})},scope.deleteEntry=lock(function(entry){return LiveBlogApi.deleteEntry(entry).then(function(){var index=scope.entries.indexOf(entry);Utils.removeFrom(scope.entries,index)}).catch(function(response){var message="An error occurred attempting to delete "+titleDisplay(entry)+"!";reportError(message,{response:response})})})},restrict:"E",scope:{article:"="},templateUrl:CmsConfig.buildComponentPath("live-blog","live-blog-entries","live-blog-entries.html")}}]),angular.module("bulbs.cms.liveBlog",["bulbs.cms.liveBlog.entries"]),angular.module("notifications.edit.directive",["apiServices.notification.factory","BettyCropper","bulbs.cms.site.config","copyButton","customSearch","lodash","saveButton.directive","notifications.settings","topBar"]).directive("notificationsEdit",function(CmsConfig){return{controller:function(_,$location,$q,$scope,NOTIFICATIONS_LIST_REL_PATH,Notification){$scope.LIST_URL=CmsConfig.buildExternalUrl(NOTIFICATIONS_LIST_REL_PATH),$scope.needsSave=!1;var modelId=$scope.getModelId();"new"===modelId?($scope.model=Notification.$build(),$scope.isNew=!0):$scope.model=Notification.$find($scope.getModelId()),window.onbeforeunload=function(e){if(!_.isEmpty($scope.model.$dirty())||$scope.isNew||$scope.needsSave)return"You have unsaved changes."},$scope.$on("$destroy",function(){delete window.onbeforeunload}),$scope.saveModel=function(){var promise;if($scope.model)promise=$scope.model.$save().$asPromise().then(function(data){"new"===modelId&&$location.path("/cms/app/notification/edit/"+data.id+"/"),$scope.isNew=!1,$scope.needsSave=!1});else{var deferred=$q.defer();deferred.reject(),promise=deferred.promise}return promise}},restrict:"E",scope:{getModelId:"&modelId"},templateUrl:CmsConfig.buildComponentPath("notifications/notifications-edit/notifications-edit.html")}}),angular.module("notifications.edit",["bulbs.cms.site.config","notifications.edit.directive"]).config(function($routeProvider){$routeProvider.when("/cms/app/notifications/edit/:id/",{controller:function($routeParams,$scope,$window,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Edit Notification",$scope.routeId=$routeParams.id},template:'',reloadOnSearch:!1})}),angular.module("notifications.list",["apiServices.notification.factory","bulbs.cms.site.config","listPage","moment","notifications.settings"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/notifications/",{controller:function($scope,$window,NOTIFICATIONS_LIST_REL_PATH,Notification){$window.document.title=CmsConfig.getCmsName()+" | Notification",$scope.modelFactory=Notification,$scope.LIST_URL=CmsConfig.buildExternalUrl(NOTIFICATIONS_LIST_REL_PATH)},templateUrl:CmsConfig.buildComponentPath("notifications/notifications-list/notifications-list-page.html")})}),angular.module("notifications.settings",[]).value("NOTIFICATIONS_LIST_REL_PATH","/notification/"),angular.module("notifications",["notifications.list","notifications.edit"]),angular.module("polls.edit.directive",["apiServices.answer.factory","apiServices.poll.factory","BettyCropper","bulbs.cms.site.config","lodash","saveButton.directive","topBar"]).constant("RESPONSE_TYPES",[{name:"Text Only",value:"Text"},{name:"Image + Text",value:"Image"}]).directive("pollsEdit",function(CmsConfig){return{templateUrl:CmsConfig.buildComponentPath("polls/polls-edit/polls-edit.html"),controller:function(_,$http,$location,$q,$routeParams,$scope,$timeout,Answer,Poll){"new"===$routeParams.id?($scope.model={},$scope.isNew=!0):Poll.getPoll($routeParams.id).then(function(response){$scope.model=response,$scope.answers=_.cloneDeep(response.answers)}),window.onbeforeunload=function(e){if(!_.isEmpty($scope.model.$dirty())||$scope.isNew||$scope.needsSave)return"You have unsaved changes."},$scope.$on("$destroy",function(){delete window.onbeforeunload}),$scope.embedCode=function(){return''},$scope.validatePublication=function(){$timeout(function(){var published=$scope.model.published,endDate=$scope.model.end_date,publishedField=$scope.pollForm.published,endDateField=$scope.pollForm.endDate;publishedField.$setValidity("requiredWithEndDate",!(endDate&&!published));var comesAfterPublishedValid=!0;endDate&&published&&(comesAfterPublishedValid=published.isBefore(endDate)),endDateField.$setValidity("comesAfterPublished",comesAfterPublishedValid)})},$scope.saveModel=function(){return $scope.model?$scope.isNew?Poll.postPoll($scope.model).then(function(data){var answerPromise=_.map($scope.answers,function(answer){return Answer.postAnswer(answer,data.id)});return $q.all(answerPromise).then(function(){$location.path("/cms/app/polls/edit/"+data.id+"/")})}):(Answer.updatePollAnswers($scope),$scope.deletedAnswers=[],Poll.updatePoll($scope.model)):$q.reject("Save failed")},$scope.deletedAnswers=[];var newId=$scope.answers?$scope.answers.length:0;$scope.addAnswer=function(){$scope.answers.push({id:newId++,notOnSodahead:!0})},$scope.isNew&&($scope.answers=[],_.times(3,$scope.addAnswer)),$scope.removeAnswer=function(answerId){var deletedAnswer=_.remove($scope.answers,function(a){return a.id===answerId});deletedAnswer[0].notOnSodahead||$scope.deletedAnswers.push(deletedAnswer[0])}},restrict:"E",scope:{getModelId:"&modelId"}}}),angular.module("polls.edit",["bulbs.cms.site.config","polls.edit.directive"]).config(function($routeProvider){$routeProvider.when("/cms/app/polls/edit/:id/",{controller:function($routeParams,$scope,$window,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Edit Poll",$scope.routeId=$routeParams.id},template:'',reloadOnSearch:!1})}),angular.module("polls.list",["apiServices.poll.factory","bulbs.cms.site.config","bulbsCmsApp.nonRestmodListPage","moment"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/polls/",{controller:function($scope,$window,Poll){$window.document.title=CmsConfig.getCmsName()+" | Poll",$scope.modelFactory=Poll},templateUrl:CmsConfig.buildComponentPath("polls/polls-list/polls-list-page.html")})}),angular.module("polls",["polls.list","polls.edit"]),angular.module("promotedContentArticle.directive",["bulbs.cms.site.config"]).directive("promotedContentArticle",function(CmsConfig){return{restrict:"E",scope:{article:"="},templateUrl:CmsConfig.buildComponentPath("promoted-content/promoted-content-article/promoted-content-article.html")}}),angular.module("promotedContentArticle",["promotedContentArticle.directive"]),angular.module("promotedContentList.directive",["bulbs.cms.site.config","promotedContentArticle","promotedContentSave","ui.sortable"]).directive("promotedContentList",function($,CmsConfig){return{controller:function($scope,PromotedContentService){$scope.pzoneData=PromotedContentService.getData(),$scope.moveUp=function(index){PromotedContentService.moveContentUp(index)},$scope.moveDown=function(index){PromotedContentService.moveContentDn(index)},$scope.remove=function(article){PromotedContentService.$removeContentFromPZone(article.id)},$scope.completeAction=function(index){PromotedContentService.$completeContentAction(index)},$scope.stopAction=function(){PromotedContentService.stopContentAction()},$scope.markDirty=function(){PromotedContentService.markDirtySelectedPZone()},$scope.disableControls=function(){return PromotedContentService.isPZoneRefreshPending()}},link:function(scope,element,attr){scope.sortableOptions={beforeStop:function(e,ui){ui.helper.css("margin-top",0),ui.item.parent().removeClass("ui-sortable-dragging")},cancel:".ui-sortable-unsortable",change:function(e,ui){ui.helper.css("margin-top",$(window).scrollTop())},containment:"promoted-content-list",distance:3,opacity:.75,placeholder:"dropzone",start:function(e,ui){ui.item.parent().addClass("ui-sortable-dragging"),ui.helper.css("margin-top",$(window).scrollTop())},stop:function(){scope.markDirty()}}},restrict:"E",scope:{},templateUrl:CmsConfig.buildComponentPath("promoted-content/promoted-content-list/promoted-content-list.html")}}),angular.module("promotedContentList",["promotedContentList.directive"]),angular.module("promotedContentOperationsList.directive",["bulbs.cms.site.config","promotedContent.service"]).directive("promotedContentOperationsList",function(_,CmsConfig,moment){return{controller:function(moment,$scope,PromotedContentService){$scope.pzoneData=PromotedContentService.getData(),$scope.scheduleDateFrom=moment(),$scope.scheduleDateTo=moment().add(3,"days"),$scope.deleteStatus={message:"",isError:!1},PromotedContentService.$ready().then(function(){$scope.aggregatedOperations=$scope.pzoneData.operations.concat($scope.pzoneData.unsavedOperations)}),$scope.removeOperation=function(operation){PromotedContentService.$removeOperation(operation.id).then(function(){$scope.deleteStatus={message:"Operation successfully removed!",isError:!1}}).catch(function(err){$scope.deleteStatus={message:err,isError:!0}})},$scope.clearDeleteStatus=function(){$scope.deleteStatus.message=""},$scope.setPreviewTime=function(time){PromotedContentService.setPreviewTime(time.add(1,"minute"))},$scope.disableControls=function(){return PromotedContentService.isPZoneRefreshPending()},$scope.operationsStale=function(){return PromotedContentService.isPZoneOperationsStale()},$scope.refreshingOperations=!1,$scope.refreshOperations=function(){$scope.refreshingOperations||($scope.refreshingOperations=!0,PromotedContentService.$refreshOperations({from:$scope.scheduleDateFrom.toISOString(),to:$scope.scheduleDateTo.toISOString()}).finally(function(){$scope.refreshingOperations=!1}))}},link:function(scope,element,attr){var operationTime=function(operation){var compTime;return compTime=operation.whenAsMoment?operation.whenAsMoment:scope.pzoneData.previewTime?scope.pzoneData.previewTime:moment()};scope.aggregatedOperations={},scope.groupDateFormat="M/D/YY @ h:mma";var aggregator=function(){var tempAggregate=scope.pzoneData.operations.concat(scope.pzoneData.unsavedOperations);scope.aggregatedOperations=_.chain(tempAggregate).sortBy(operationTime).groupBy(function(operation){return operationTime(operation).format(scope.groupDateFormat)}).pairs().map(function(pair){return[moment(pair[0],scope.groupDateFormat),pair[1]]}).sortBy(function(pair){return pair[0]}).value()};scope.$watchCollection("pzoneData.operations",aggregator),scope.$watchCollection("pzoneData.unsavedOperations",aggregator)},restrict:"E",scope:{},templateUrl:CmsConfig.buildComponentPath("promoted-content/promoted-content-operations-list/promoted-content-operations-list.html")}}),angular.module("promotedContentOperationsList",["promotedContentOperationsList.directive"]),angular.module("promotedContentPzoneSelect.directive",["bulbs.cms.site.config","promotedContent.service"]).directive("promotedContentPzoneSelect",function(CmsConfig){return{controller:function($scope,PromotedContentService){$scope.pzoneData=PromotedContentService.getData(),$scope.selectedPZoneName="",PromotedContentService.$ready().then(function(){$scope.selectedPZoneName=$scope.pzoneData.selectedPZone.name}),$scope.changePZone=function(name){!function(name){PromotedContentService.$refreshPZones().then(function(){PromotedContentService.$selectPZone(name)})}(name)},$scope.disableControls=function(){return PromotedContentService.isPZoneRefreshPending()}},restrict:"E",scope:{},templateUrl:CmsConfig.buildComponentPath("promoted-content/promoted-content-pzone-select/promoted-content-pzone-select.html")}}),angular.module("promotedContentPzoneSelect",["promotedContentPzoneSelect.directive"]),angular.module("promotedContentSave.directive",["bulbs.cms.site.config","promotedContent.service"]).directive("promotedContentSave",function(CmsConfig){return{controller:function($scope,PromotedContentService){$scope.pzoneData=PromotedContentService.getData(),$scope.savePZone=function(){PromotedContentService.$saveSelectedPZone()},$scope.clearOperations=function(){PromotedContentService.clearUnsavedOperations(),PromotedContentService.$refreshSelectedPZone($scope.pzoneData.previewTime)},$scope.disableControls=function(){return PromotedContentService.isPZoneRefreshPending()}},restrict:"E",scope:{},templateUrl:CmsConfig.buildComponentPath("promoted-content/promoted-content-save/promoted-content-save.html")}}),angular.module("promotedContentSave",["promotedContentSave.directive"]),angular.module("promotedContentSearch.directive",["bulbs.cms.site.config","statusFilter","filterWidget","promotedContent.service","promotedContentArticle"]).directive("promotedContentSearch",function(CmsConfig){return{controller:function(_,moment,$scope,$location,PromotedContentService){$scope.pzoneData=PromotedContentService.getData(),$scope.pageNumber=$location.search().page||"1",$scope.goToPage=function(){PromotedContentService.$refreshAllContent({page:$scope.pageNumber},!0)},$scope.contentIsEnabled=function(content){var notAlreadyInList=$scope.pzoneData.selectedPZone&&_.isUndefined(_.find($scope.pzoneData.selectedPZone.content,{id:content.id})),immediateDraggable=null===$scope.pzoneData.previewTime&&moment().isAfter(content.published),futureDraggable=null!==$scope.pzoneData.previewTime&&moment().isBefore($scope.pzoneData.previewTime)&&$scope.pzoneData.previewTime.isAfter(content.published);return notAlreadyInList&&(immediateDraggable||futureDraggable)},$scope.beginInsert=function(article){PromotedContentService.beginContentInsert(article)},$scope.beginReplace=function(article){PromotedContentService.beginContentReplace(article)},$scope.stopAction=function(){PromotedContentService.stopContentAction()},$scope.disableControls=function(){return PromotedContentService.isPZoneRefreshPending()}},link:function(scope,element,attr){scope.tools=null,scope.openToolsFor=function(article){var doOpen=!1;return scope.disableControls()||(scope.tools=article.id,doOpen=!0),doOpen},scope.closeTools=function(){return scope.tools=null,!0},scope.toolsOpenFor=function(article){return scope.tools===article.id}},restrict:"E",scope:{},templateUrl:CmsConfig.buildComponentPath("promoted-content/promoted-content-search/promoted-content-search.html")}}),angular.module("promotedContentSearch",["promotedContentSearch.directive"]),angular.module("promotedContent.service",["contentServices","moment","restangular"]).service("PromotedContentService",function($,_,moment,$q,Restangular,ContentFactory,ContentListService){var PromotedContentService=this;PromotedContentService._serviceData={allContent:ContentListService.getData(),actionContent:null,action:null,pzones:[],unsavedOperations:[],operations:[],selectedPZone:null,previewTime:null};var _data=PromotedContentService._serviceData,setupDefer=$q.defer(),readableOperationTypes={INSERT:"INSERT",DELETE:"DELETE",REPLACE:"REPLACE"};PromotedContentService.readableOperationTypes=readableOperationTypes;var operationTypeToReadable={promotion_insertoperation:readableOperationTypes.INSERT,promotion_deleteoperation:readableOperationTypes.DELETE,promotion_replaceoperation:readableOperationTypes.REPLACE},readableToOperationType=_.reduce(operationTypeToReadable,function(result,val,key){return result[val]=key,result},{}),pzoneRefreshPending=!1,pzoneOperationsStale=!0;PromotedContentService.isPZoneRefreshPending=function(){return pzoneRefreshPending},PromotedContentService.isPZoneOperationsStale=function(){return pzoneOperationsStale},PromotedContentService.$refreshPZones=function(filters){var deferred=$q.defer();return pzoneRefreshPending?(deferred.reject(),deferred.promise):(pzoneRefreshPending=!0,ContentFactory.all("pzone").getList(filters).then(function(data){return _data.pzones=data,_.each(_data.pzones,function(pzone){pzone.saved=!0}),deferred.resolve(),_data.pzones}).catch(function(err){return deferred.reject(),err}).finally(function(){pzoneRefreshPending=!1}))},PromotedContentService.markDirtySelectedPZone=function(){delete _data.selectedPZone.saved},PromotedContentService.markSavedSelectedPZone=function(){_data.selectedPZone.saved=!0},PromotedContentService.makeOperationsStale=function(){_data.operations=[],pzoneOperationsStale=!0},PromotedContentService.$saveSelectedPZone=function(){ +var defer=$q.defer();return _data.previewTime&&_data.previewTime.isAfter(moment())?(PromotedContentService.makeOperationsStale(),_.each(_data.unsavedOperations,function(operation){operation.when=_data.previewTime?_data.previewTime.toISOString():null,delete operation.client_id}),_data.selectedPZone.all("operations").post(_data.unsavedOperations).then(function(){defer.resolve(_data.selectedPZone)}),PromotedContentService.clearUnsavedOperations()):_data.previewTime?defer.reject("Cannot save operations in the past."):(PromotedContentService.clearUnsavedOperations(),PromotedContentService.makeOperationsStale(),_data.selectedPZone.put().then(function(){defer.resolve(_data.selectedPZone)}).catch(function(err){defer.reject(err)})),defer.promise},PromotedContentService.$refreshAllContent=function(){return ContentListService.$updateContent.apply(ContentListService,arguments)},PromotedContentService.$addOperation=function(props){var defer=$q.defer();if(PromotedContentService.isPreviewTimeImmediate())defer.resolve();else if(PromotedContentService.isPreviewTimePast())defer.reject("Cannot add operations in the past.");else{var lastId=_.max(_data.unsavedOperations,"client_id").client_id,nextId=lastId?lastId+1:0,allProps=_.assign({client_id:nextId,type_name:readableToOperationType[props.cleanType]||"",pzone:_data.selectedPZone.id,applied:!1,content:null,content_title:"",index:null},props),operation=Restangular.restangularizeElement(_data.selectedPZone.all("operations"),allProps);_data.unsavedOperations.push(operation),defer.resolve(operation)}return defer.promise},PromotedContentService.$removeOperation=function(id){var defer=$q.defer(),index=_.findIndex(_data.operations,{id:id}),operation=_data.operations[index];return operation?operation.whenAsMoment.isAfter(moment())?operation.remove().then(function(){_data.operations.splice(index,1),defer.resolve()}).catch(function(err){404===err.status?defer.reject("Cannot find operation to delete."):defer.reject(err)}):defer.reject("Cannot delete an operation in the past."):defer.reject("Could not find saved operation with id "+id+" to delete."),defer.promise},PromotedContentService.clearUnsavedOperations=function(){_data.unsavedOperations=[],PromotedContentService.markSavedSelectedPZone()},PromotedContentService.$refreshOperations=function(params){return _data.selectedPZone.getList("operations",params).then(function(data){return _data.operations=data,_.each(_data.operations,function(operation){operation.cleanType=operationTypeToReadable[operation.type_name],operation.whenAsMoment=moment(operation.when)}),pzoneOperationsStale=!1,_data.operations}).catch(function(err){return err})},PromotedContentService.$selectPZone=function(name){return _data.selectedPZone=_.find(_data.pzones,{name:name})||_data.pzones[0],PromotedContentService.clearUnsavedOperations(),PromotedContentService.$refreshSelectedPZone(_data.previewTime)},PromotedContentService.$removeContentFromPZone=function(contentId){var defer=$q.defer(),i=_.findIndex(_data.selectedPZone.content,{id:contentId});return i>=0?PromotedContentService.$addOperation({cleanType:readableOperationTypes.DELETE,content:contentId,content_title:_data.selectedPZone.content[i].title}).then(function(){PromotedContentService.markDirtySelectedPZone(),_data.selectedPZone.content.splice(i,1),defer.resolve()}).catch(function(err){defer.reject(err)}):defer.reject("Could not find content with given id to delete."),defer.promise};var moveTo=function(indexFrom,indexTo){var ret=!1,content=_data.selectedPZone.content;if(indexFrom>=0&&indexFrom=0&&indexTo0&&(content[indexTo]=splicer[0],ret=!0,PromotedContentService.markDirtySelectedPZone())}return ret};PromotedContentService.moveContentUp=function(index){return moveTo(index,index-1)},PromotedContentService.moveContentDn=function(index){return moveTo(index,index+1)},PromotedContentService.beginContentInsert=function(article){_data.actionContent=article,_data.action=readableOperationTypes.INSERT},PromotedContentService.beginContentReplace=function(article){_data.actionContent=article,_data.action=readableOperationTypes.REPLACE},PromotedContentService.stopContentAction=function(){_data.actionContent=null,_data.action=null},PromotedContentService.$completeContentAction=function(index){var deferred=$q.defer();return _data.action?PromotedContentService.$addOperation({cleanType:_data.action,content:_data.actionContent.id,content_title:_data.actionContent.title,index:index}).then(function(){var duplicateIndex=_.findIndex(_data.selectedPZone.content,{id:_data.actionContent.id});index!==duplicateIndex&&duplicateIndex>=0&&_data.selectedPZone.content.splice(duplicateIndex,1);var replace=_data.action===readableOperationTypes.REPLACE;_data.selectedPZone.content.splice(index,replace?1:0,_data.actionContent),PromotedContentService.stopContentAction(),PromotedContentService.markDirtySelectedPZone(),deferred.resolve()}).catch(deferred.reject):deferred.reject("No action to complete in progress."),deferred.promise},PromotedContentService.setPreviewTime=function(time){return _data.previewTime=time,PromotedContentService.clearUnsavedOperations(),PromotedContentService.$refreshSelectedPZone(_data.previewTime)},PromotedContentService.setPreviewTimeToImmediate=function(){PromotedContentService.setPreviewTime(null)},PromotedContentService.isPreviewTimeImmediate=function(){return null===_data.previewTime},PromotedContentService.isPreviewTimePast=function(){return!PromotedContentService.isPreviewTimeImmediate()&&_data.previewTime.isBefore(moment())},PromotedContentService.$refreshSelectedPZone=function(time){var params={};time&&(params.preview=time.toISOString()),PromotedContentService.makeOperationsStale();var deferred=$q.defer();return pzoneRefreshPending?deferred.reject():(pzoneRefreshPending=!0,_data.selectedPZone.get(params).then(function(data){return deferred.resolve(),_data.selectedPZone=data,_data.selectedPZone}).catch(function(err){return deferred.reject(),err}).finally(function(){PromotedContentService.markSavedSelectedPZone(),pzoneRefreshPending=!1})),deferred.promise},PromotedContentService.getData=function(){return _data},PromotedContentService.$ready=function(){return setupDefer.promise},PromotedContentService.$refreshPZones().then(function(){return PromotedContentService.$refreshAllContent()}).then(function(){return PromotedContentService.$selectPZone()}).then(function(){setupDefer.resolve()})}),angular.module("promotedContentTimePicker.directive",["bulbs.cms.site.config","promotedContent.service"]).directive("promotedContentTimePicker",function(CmsConfig){return{controller:function(moment,$scope,PromotedContentService){$scope.contentData=PromotedContentService.getData(),$scope.setPreviewTime=function(previewTime){PromotedContentService.setPreviewTime(previewTime)},$scope.setPreviewTimeToImmediate=function(){$scope.previewTime=null,PromotedContentService.setPreviewTimeToImmediate()},$scope.disableControls=function(){return PromotedContentService.isPZoneRefreshPending()}},restrict:"E",scope:{},templateUrl:CmsConfig.buildComponentPath("promoted-content/promoted-content-time-picker/promoted-content-time-picker.html")}}),angular.module("promotedContentTimePicker",["promotedContentTimePicker.directive"]),angular.module("promotedContent",["bulbs.cms.site.config","promotedContentPzoneSelect","promotedContentList","promotedContentSearch","promotedContentTimePicker","promotedContentOperationsList"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/promotion/",{controller:["$window","CmsConfig",function($window){$window.document.title=CmsConfig.getCmsName()+" | Promotion Tool"}],templateUrl:CmsConfig.buildComponentPath("promoted-content/promoted-content.html"),reloadOnSearch:!1})}),angular.module("lineItems.edit.directive",["apiServices.lineItem.factory","bulbs.cms.site.config","lodash","saveButton.directive","topBar"]).directive("lineItemsEdit",function(CmsConfig){return{controller:function(_,$location,$q,$routeParams,$scope,LineItem){"new"===$routeParams.id?($scope.model=LineItem.$build(),$scope.isNew=!0):$scope.model=LineItem.$find($routeParams.id),window.onbeforeunload=function(e){if(!_.isEmpty($scope.model.$dirty())||$scope.isNew||$scope.needsSave)return"You have unsaved changes."},$scope.$on("$destroy",function(){delete window.onbeforeunload}),$scope.saveModel=function(){var promise;if($scope.model)promise=$scope.model.$save().$asPromise().then(function(data){$location.path("/cms/app/line-items/edit/"+data.id+"/")});else{var deferred=$q.defer();deferred.reject(),promise=deferred.promise}return promise}},restrict:"E",scope:{getModelId:"&modelId"},templateUrl:CmsConfig.buildComponentPath("reporting/reporting-line-items-edit/reporting-line-items-edit.html")}}),angular.module("lineItems.edit",["bulbs.cms.site.config","lineItems.edit.directive"]).config(function($routeProvider){$routeProvider.when("/cms/app/line-items/edit/:id/",{controller:function($routeParams,$scope,$window,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Edit Line Item",$scope.routeId=$routeParams.id},template:'',reloadOnSearch:!1})}),angular.module("lineItems.list",["apiServices.lineItem.factory","bulbs.cms.site.config","listPage"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/line-items/",{controller:function($modal,$scope,$window,LineItem){$window.document.title=CmsConfig.getCmsName()+" | Line Items",$scope.modelFactory=LineItem,$scope.LineItemExportModal=function(){return $modal.open({templateUrl:"/views/modals/line-item-export-modal.html",controller:"LineitemexportmodalCtrl"})},$scope.utilityButtons=[{title:"Export CSV",click:function(){$scope.LineItemExportModal()},buttonClasses:"add-item btn btn-primary",iconClasses:"font-awesome"}]},templateUrl:CmsConfig.buildComponentPath("reporting/reporting-line-items-list/reporting-line-items-list.html")})}),angular.module("rateOverrides.edit.directive",["apiServices.rateOverride.factory","apiServices.featureType.factory","bulbs.cms.site.config","lodash","saveButton.directive","topBar"]).directive("rateOverridesEdit",function(CmsConfig){return{controller:function(_,$location,$http,$q,$routeParams,$scope,ContentFactory,FeatureType,RateOverride,Raven){var resourceUrl="/cms/api/v1/contributions/role/";"new"===$routeParams.id?($scope.model=RateOverride.$build(),$scope.isNew=!0):($scope.model=RateOverride.$find($routeParams.id),$scope.model.$promise.then(function(){$scope.model.hasOwnProperty("role")&&null!==$scope.model.role&&($scope.model.role=$scope.model.role.id)})),window.onbeforeunload=function(e){if(!_.isEmpty($scope.model.$dirty())||$scope.isNew||$scope.needsSave)return"You have unsaved changes."},$scope.$on("$destroy",function(){delete window.onbeforeunload}),$scope.getPaymentType=function(roleId){if($scope.hasOwnProperty("roles")){for(var i=0;i<$scope.roles.length;i++)if($scope.roles[i].id===roleId)return $scope.roles[i].payment_type;return null}},$scope.isFeatureRated=function(){return"FeatureType"===$scope.getPaymentType($scope.model.role)},$scope.isHourlyRated=function(){return"Hourly"===$scope.getPaymentType($scope.model.role)},$scope.isFlatRated=function(){return"Flat Rate"===$scope.getPaymentType($scope.model.role)},$scope.addFeatureType=function(){$scope.model.hasOwnProperty("featureTypes")||($scope.model.featureTypes=[]),$scope.model.featureTypes.push({featureType:null,rate:0})},$scope.getFeatureTypes=function(){$http({method:"GET",url:resourceUrl}).success(function(data){$scope.featureTypes=data.results||data}).error(function(data,status,headers,config){Raven.captureMessage("Error fetching FeatureTypes",{extra:data})})},$scope.getRoles=function(){$http({method:"GET",url:resourceUrl}).success(function(data){$scope.roles=data.results||data}).error(function(data,status,headers,config){Raven.captureMessage("Error fetching Roles",{extra:data})})},$scope.searchFeatureTypes=function(searchTerm){return FeatureType.simpleSearch(searchTerm)},$scope.saveModel=function(){var promise;if($scope.model)promise=$scope.model.$save().$asPromise().then(function(data){$location.path("/cms/app/rate-overrides/edit/"+data.id+"/"),$scope.model.hasOwnProperty("role")&&null!==$scope.model.role&&($scope.model.role=$scope.model.role.id)});else{var deferred=$q.defer();deferred.reject(),promise=deferred.promise}return promise},$scope.getRoles(),$scope.getFeatureTypes()},restrict:"E",scope:{getModelId:"&modelId"},templateUrl:CmsConfig.buildComponentPath("reporting/reporting-rate-overrides-edit/reporting-rate-overrides-edit.html")}}),angular.module("rateOverrides.edit",["bulbs.cms.site.config","rateOverrides.edit.directive"]).config(function($routeProvider){$routeProvider.when("/cms/app/rate-overrides/edit/:id/",{controller:function($routeParams,$scope,$window,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Edit Rate Override",$scope.routeId=$routeParams.id},template:'',reloadOnSearch:!1})}),angular.module("rateOverrides.list",["apiServices.rateOverride.factory","bulbs.cms.site.config","listPage"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/rate-overrides/",{controller:function($scope,$window,RateOverride){$window.document.title=CmsConfig.getCmsName()+" | Rate Overrides",$scope.modelFactory=RateOverride},templateUrl:CmsConfig.buildComponentPath("reporting/reporting-rate-overrides-list/reporting-rate-overrides-list.html")})}),angular.module("roles.edit.directive",["apiServices.reporting.factory","bulbs.cms.site.config","lodash","saveButton.directive","topBar"]).constant("PAYMENT_TYPES",[{name:"Flat Rate",value:"Flat Rate"},{name:"FeatureType",value:"FeatureType"},{name:"Hourly",value:"Hourly"},{name:"Manual",value:"Manual"}]).directive("rolesEdit",function(CmsConfig){return{controller:function(_,$location,$q,$routeParams,$scope,Role,PAYMENT_TYPES){$scope.page="contributions",$scope.PAYMENT_TYPES=PAYMENT_TYPES,"new"===$routeParams.id?($scope.model=Role.$build(),$scope.isNew=!0):$scope.model=Role.$find($routeParams.id).$then(function(){$scope.model.feature_type_rates.$fetch(),$scope.model.flat_rates.$fetch(),$scope.model.hourly_rates.$fetch()}),window.onbeforeunload=function(e){if(!_.isEmpty($scope.model.$dirty())||$scope.isNew||$scope.needsSave)return"You have unsaved changes."},$scope.$on("$destroy",function(){delete window.onbeforeunload}),$scope.rateEditable=function(){var paymentTypes=PAYMENT_TYPES.slice(0,3);return!!paymentTypes.indexOf($scope.model.paymentType>=0)},$scope.getDirtyRates=function(){var dirty=[];return $scope.model.hasOwnProperty("flat_rate")&&!_.isEmpty($scope.model.flat_rate.$dirty())&&dirty.push($scope.model.flat_rate),$scope.model.hasOwnProperty("hourly_rate")&&!_.isEmpty($scope.model.hourly_rate.$dirty())&&dirty.push($scope.model.hourly_rate),$scope.model.feature_type_rates.forEach(function(rate){_.isEmpty(rate.$dirty())||dirty.push(rate)}),dirty},$scope.saveDirtyRates=function(){var dirtyRates=$scope.getDirtyRates();dirtyRates.forEach(function(rate){rate.$save()})},$scope.saveModel=function(){var promise;if($scope.saveDirtyRates(),$scope.model)promise=$scope.model.$save().$asPromise().then(function(data){$location.path("/cms/app/roles/edit/"+data.id+"/")});else{var deferred=$q.defer();deferred.reject(),promise=deferred.promise}return promise}},restrict:"E",scope:{getModelId:"&modelId"},templateUrl:CmsConfig.buildComponentPath("reporting/reporting-roles-edit/reporting-roles-edit.html")}}),angular.module("roles.edit",["bulbs.cms.site.config","roles.edit.directive"]).config(function($routeProvider){$routeProvider.when("/cms/app/roles/edit/:id/",{controller:function($routeParams,$scope,$window,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Edit Role",$scope.routeId=$routeParams.id},template:'',reloadOnSearch:!1})}),angular.module("roles.list",["apiServices.reporting.factory","bulbs.cms.site.config","listPage"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/roles/",{controller:function($scope,$window,Role){$window.document.title=CmsConfig.getCmsName()+" | Roles",$scope.modelFactory=Role},templateUrl:CmsConfig.buildComponentPath("reporting/reporting-roles-list/reporting-roles-list.html")})}),angular.module("reports",["lineItems.edit","lineItems.list","rateOverrides.edit","rateOverrides.list","roles.edit","roles.list"]),angular.module("sections.edit.directive",["apiServices.section.factory","BettyCropper","bulbs.cms.site.config","copyButton","customSearch","lodash","saveButton.directive","sections.settings","topBar"]).directive("sectionsEdit",function(CmsConfig){return{controller:function(_,$location,$q,$scope,SECTIONS_LIST_REL_PATH,Section){$scope.LIST_URL=CmsConfig.buildExternalUrl(SECTIONS_LIST_REL_PATH),$scope.needsSave=!1;var modelId=$scope.getModelId();"new"===modelId?($scope.model=Section.$build(),$scope.isNew=!0):$scope.model=Section.$find($scope.getModelId()),window.onbeforeunload=function(e){if(!_.isEmpty($scope.model.$dirty())||$scope.isNew||$scope.needsSave)return"You have unsaved changes."},$scope.$on("$destroy",function(){delete window.onbeforeunload}),$scope.saveModel=function(){var promise;if($scope.model)promise=$scope.model.$save().$asPromise().then(function(data){"new"===modelId&&$location.path("/cms/app/section/edit/"+data.id+"/"),$scope.isNew=!1,$scope.needsSave=!1});else{var deferred=$q.defer();deferred.reject(),promise=deferred.promise}return promise}},restrict:"E",scope:{getModelId:"&modelId"},templateUrl:CmsConfig.buildComponentPath("sections/sections-edit/sections-edit.html")}}),angular.module("sections.edit",["bulbs.cms.site.config","sections.edit.directive"]).config(function($routeProvider){$routeProvider.when("/cms/app/section/edit/:id/",{controller:function($routeParams,$scope,$window,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Edit Section",$scope.routeId=$routeParams.id},template:'',reloadOnSearch:!1})}),angular.module("sections.list",["apiServices.section.factory","bulbs.cms.site.config","listPage","sections.settings"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/section/",{controller:function($scope,$window,SECTIONS_LIST_REL_PATH,Section){$window.document.title=CmsConfig.getCmsName()+" | Section",$scope.modelFactory=Section,$scope.LIST_URL=CmsConfig.buildExternalUrl(SECTIONS_LIST_REL_PATH)},templateUrl:CmsConfig.buildComponentPath("sections/sections-list/sections-list-page.html")})}),angular.module("sections.settings",[]).value("SECTIONS_LIST_REL_PATH","/section/"),angular.module("sections",["sections.list","sections.edit"]),angular.module("bulbs.cms.sendToEditorModal.api",["bulbs.cms.dateTimeFilter","bulbs.cms.site.config","bulbs.cms.utils","lodash"]).service("SendToEditorApi",["_","$http","CmsConfig","dateTimeFormatFilter","moment","Utils",function(_,$http,CmsConfig,dateTimeFormatFilter,moment,Utils){var endpoint=function(article){return CmsConfig.buildApiUrlRoot("content",article.id,"send/")},parsePayload=function(payload){var data=_.cloneDeep(payload.data);return data};return{sendToEditor:function(article,status,notes){return $http.post(endpoint(article),{status:status,notes:notes}).then(function(response){return parsePayload(response)})}}}]),angular.module("bulbs.cms.sendToEditorModal",["bulbs.cms.sendToEditorModal.api","bulbs.cms.site.config","Raven","ui.bootstrap","ui.bootstrap.modal"]).directive("sendToEditorModalOpener",["$modal","$q","CmsConfig","SendToEditorApi","Raven",function($modal,$q,CmsConfig,SendToEditorApi,Raven){return{restrict:"A",scope:{modalArticle:"=",modalOnBeforeOpen:"&",modalOnCancel:"&",modalOnOk:"&"},link:function(scope,element){scope.statuses=CmsConfig.getArticleEditoralStatuses(),scope.modalInstance=!1;var setupAndOpenModal=function(){scope.clearError=function(){scope.errorMessage=""},scope.sendToEditor=function(status,note){return scope.clearError(),SendToEditorApi.sendToEditor(scope.modalArticle,status,"Status: "+status+"\n\n"+note).then(scope.modalInstance.close).catch(function(response){Raven.captureMessage("Error attempting to send to editor",{response:response}),scope.errorMessage="An error occurred!"})},scope.modalInstance=$modal.open({scope:scope,templateUrl:CmsConfig.buildComponentPath("send-to-editor-modal","send-to-editor-modal.html")}),scope.modalInstance.result.then(scope.modalOnOk).catch(scope.modalOnCancel).finally(function(){scope.modalInstance=!1})};element.addClass("send-to-editor-modal-opener"),element.on("click",function(){scope.modalInstance||$q.when(scope.modalOnBeforeOpen()).then(function(result){result!==!1&&setupAndOpenModal()})})}}}]),angular.module("specialCoverage.edit.directive",["apiServices.campaign.factory","apiServices.specialCoverage.factory","bulbs.cms.site.config","campaignAutocomplete","copyButton","customSearch","lodash","specialCoverage.settings","topBar","ui.bootstrap.tooltip","videoList","bulbs.cms.superFeatures.tab"]).directive("specialCoverageEdit",function(CmsConfig){return{controller:function(_,$location,$q,$scope,$modal,Campaign,SPECIAL_COVERAGE_LIST_REL_PATH,SpecialCoverage){$scope.ACTIVE_STATES=SpecialCoverage.ACTIVE_STATES,$scope.LIST_URL=CmsConfig.buildExternalUrl(SPECIAL_COVERAGE_LIST_REL_PATH),$scope.needsSave=!1,$scope.tunicCampaignIdMapping={};var modelId=$scope.getModelId();"new"===modelId?($scope.model=SpecialCoverage.$build(),$scope.isNew=!0):$scope.model=SpecialCoverage.$find($scope.getModelId()).$then(function(){$scope.model.$loadTunicCampaign().then(function(campaign){$scope.tunicCampaignIdMapping[campaign.id]=campaign})}),window.onbeforeunload=function(e){if(!_.isEmpty($scope.model.$dirty())||$scope.isNew||$scope.needsSave)return"You have unsaved changes."},$scope.$on("$destroy",function(){delete window.onbeforeunload}),$scope.saveModel=function(){var promise;if($scope.model)promise=$scope.model.$save().$asPromise().then(function(data){"new"===modelId&&$location.path("/cms/app/special-coverage/edit/"+data.id+"/"),$scope.isNew=!1,$scope.needsSave=!1});else{var deferred=$q.defer();deferred.reject(),promise=deferred.promise}return promise},$scope.previewLinkModal=function(){return $modal.open({templateUrl:"/views/modals/preview-link-modal.html",scope:$scope,resolve:{}})},$scope.tunicCampaignFormatter=function(campaignId){if(campaignId in $scope.tunicCampaignIdMapping){var campaign=$scope.tunicCampaignIdMapping[campaignId];return campaign.name+" - "+campaign.number}},$scope.searchCampaigns=function(searchTerm){return $scope.model.$searchCampaigns({search:searchTerm}).then(function(campaigns){return campaigns.forEach(function(campaign){$scope.tunicCampaignIdMapping[campaign.id]=campaign}),campaigns.map(function(campaign){return campaign.id})})}},restrict:"E",scope:{getModelId:"&modelId"},templateUrl:CmsConfig.buildComponentPath("special-coverage/special-coverage-edit/special-coverage-edit.html")}}),angular.module("specialCoverage.edit",["bulbs.cms.site.config","specialCoverage.edit.directive"]).config(function($routeProvider){$routeProvider.when("/cms/app/special-coverage/edit/:id/",{controller:function($routeParams,$scope,$window,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Edit Special Coverage",$scope.routeId=$routeParams.id},template:''})}),angular.module("specialCoverage.list",["apiServices.specialCoverage.factory","bulbs.cms.site.config","listPage","moment","specialCoverage.settings"]).config(function($injector,$routeProvider,CmsConfigProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get);$routeProvider.when("/cms/app/special-coverage/",{controller:function($scope,$window,CmsConfig,SPECIAL_COVERAGE_LIST_REL_PATH,SpecialCoverage){$window.document.title=CmsConfig.getCmsName()+" | Special Coverage",$scope.modelFactory=SpecialCoverage,$scope.LIST_URL=CmsConfig.buildExternalUrl(SPECIAL_COVERAGE_LIST_REL_PATH)},templateUrl:CmsConfig.buildComponentPath("special-coverage/special-coverage-list/special-coverage-list-page.html")})}),angular.module("specialCoverage.settings",[]).value("SPECIAL_COVERAGE_LIST_REL_PATH","/special/"),angular.module("specialCoverage",["specialCoverage.list","specialCoverage.edit"]),angular.module("bulbs.cms.staticImage",["bulbs.cms.site.config"]).directive("staticImage",["CmsConfig",function(CmsConfig){return{templateUrl:CmsConfig.buildComponentPath("static-image","static-image.html"),restrict:"E",scope:{image:"=",ratio:"@"},link:function(scope,element,attrs){scope.$watch("image",function(){scope.image&&scope.image.id?scope.imageUrl=CmsConfig.buildImageApiUrl(scope.image.id,scope.ratio||"16x9","1200.jpg"):scope.imageUrl=!1})}}}]),angular.module("statusFilter.config",["bulbs.cms.site.config","contentServices.listService","moment"]).provider("StatusFilterOptions",function(moment){var _statuses=[{label:"Draft",key:"status",value:"Draft"},{label:"Awaiting Review",key:"status",value:"Waiting for Editor"},{label:"Published",key:"before",value:function(){return moment().format("YYYY-MM-DDTHH:mmZ")}},{label:"Scheduled",key:"after",value:function(){return moment().format("YYYY-MM-DDTHH:mmZ")}},{label:"All",key:null,value:null}];this.setStatuses=function(statuses){_statuses=statuses},this.$get=function(){return{getStatuses:function(){return _statuses}}}}),angular.module("statusFilter.directive",["bulbs.cms.site.config","contentServices.listService","statusFilter.config"]).directive("statusFilter",function($location,_,StatusFilterOptions,ContentListService,CmsConfig){return{templateUrl:CmsConfig.buildComponentPath("status-filter/status-filter.html"),restrict:"E",scope:{},controller:"ContentlistCtrl",link:function(scope,element,attrs){function getValue(option){var value;return value="function"==typeof option.value?option.value():option.value}scope.options=StatusFilterOptions.getStatuses(),scope.isActive=function(option){if(option.key&&option.key in $location.search()&&$location.search()[option.key]===getValue(option))return!0;if(!option.key){var possibleKeys=_.pluck(scope.options,"key"),searchKeys=_.keys($location.search());return!(_.intersection(possibleKeys,searchKeys).length>0)}return!1},scope.filterByStatus=function(option){var value,search={};return option.key&&(value=getValue(option),search[option.key]=value),ContentListService.$updateContent(search,!1)}}}}),angular.module("statusFilter",["statusFilter.directive"]),angular.module("bulbs.cms.superFeatures.api",["bulbs.cms.dateTimeFilter","bulbs.cms.site.config","bulbs.cms.utils","lodash","moment"]).service("SuperFeaturesApi",["_","$http","$sce","CmsConfig","dateTimeFormatFilter","moment","Utils",function(_,$http,$sce,CmsConfig,dateTimeFormatFilter,moment,Utils){var superFeatureEndpoint=CmsConfig.buildApiUrlRoot.bind(null,"super-feature"),contentEndpoint=CmsConfig.buildApiUrlRoot.bind(null,"content"),parsePayload=function(payload){var data=_.cloneDeep(payload);return payload.published&&(data.published=moment.tz(payload.published,CmsConfig.getTimezoneName())),data},cleanData=function(data){var payload=_.chain(data).omit("published").cloneDeep().value();return data.published&&(payload.published=data.published.format()),payload};return{createSuperFeature:function(data){var payload=cleanData(data);return $http.post(contentEndpoint(Utils.param({doctype:CmsConfig.getSuperFeaturesType()})),payload).then(function(response){return parsePayload(response.data)})},deleteSuperFeature:function(data){return $http.delete(contentEndpoint(data.id,"/"))},fields:[{title:"Super Feature Name",sorts:"title",content:function(superFeature){return $sce.trustAsHtml(superFeature.title)}},{title:"Total Nested Pages",content:"children_count"},{title:"Publish Date",content:function(superFeature){var now=moment(),cellContent="";return superFeature.published?now.isSameOrAfter(superFeature.published)?cellContent=dateTimeFormatFilter(superFeature.published):now.isBefore(superFeature.published)&&(cellContent=dateTimeFormatFilter(superFeature.published,"[Scheduled for] M/D/YY h:mma z")):cellContent="Draft",cellContent}}],getSuperFeature:function(id){return $http.get(superFeatureEndpoint(id,"/")).then(function(response){return parsePayload(response.data)})},getSuperFeatures:function(params){return $http.get(superFeatureEndpoint(Utils.param(params))).then(function(response){return{results:response.data.results.map(function(result){return parsePayload(result)})}})},getSuperFeatureRelations:function(id){return $http.get(superFeatureEndpoint(id,"relations/")).then(function(response){return{results:response.data.map(function(result){return parsePayload(result)})}})},name:"Super Feature",namePlural:"Super Features",updateSuperFeature:function(data){var payload=cleanData(data);return $http.put(contentEndpoint(data.id,"/"),payload).then(function(response){return parsePayload(response.data)})},updateSuperFeatureRelationsOrdering:function(id,relations){var remappedRelations=relations.map(function(relation){return _.pick(relation,"id","ordering")});return $http.put(superFeatureEndpoint(id,"relations","ordering/"),remappedRelations)},updateAllRelationPublishDates:function(id){return $http.put(superFeatureEndpoint(id,"set-children-dates/"))}}}]),angular.module("bulbs.cms.superFeatures.edit",["bulbs.cms.breadcrumb","bulbs.cms.contentSearch","bulbs.cms.dynamicContent","bulbs.cms.site.config","bulbs.cms.superFeatures.api","bulbs.cms.superFeatures.relations"]).directive("superFeaturesEdit",["CmsConfig","ContentFactory","SuperFeaturesApi","Utils",function(CmsConfig,ContentFactory,SuperFeaturesApi,Utils){return{link:function(scope){scope.breadcrumbs=[{label:"Super Features",href:"/cms/app/super-features"},{label:function(){return scope.article.title}}];var addParentToBreadcrumb=function(article){angular.isNumber(article.parent)&&SuperFeaturesApi.getSuperFeature(article.parent).then(function(superFeature){scope.breadcrumbs.splice(1,0,{label:superFeature.title,href:"/cms/app/edit/"+superFeature.id+"/"+CmsConfig.getSuperFeaturesType()}),addParentToBreadcrumb(superFeature)})};addParentToBreadcrumb(scope.article),scope.maxRecircItems=3,scope.fullRecircContents=[];var retrieveContent=function(contentId){return ContentFactory.one("content",contentId).get()};scope.includeRecirc=function(contentId){var recirc=scope.article.recirc_query;angular.isUndefined(recirc.included_ids)&&(recirc.included_ids=[]);var newRecircIdsLength=recirc.included_ids.push(contentId);retrieveContent(contentId).then(function(content){scope.fullRecircContents[newRecircIdsLength-1]=content})},scope.removeRecirc=function(index){Utils.removeFrom(scope.article.recirc_query.included_ids,index),Utils.removeFrom(scope.fullRecircContents,index)},scope.article.recirc_query.included_ids&&scope.article.recirc_query.included_ids.forEach(function(contentId,i){retrieveContent(contentId).then(function(content){scope.fullRecircContents[i]=content})})},scope:!1,restrict:"E",templateUrl:CmsConfig.buildComponentPath("super-features","super-features-edit","super-features-edit.html")}}]),angular.module("bulbs.cms.superFeatures.list",["bulbs.cms.site.config","bulbs.cms.superFeatures.api","bulbsCmsApp.nonRestmodListPage","lodash","moment","ngRoute","statusFilter.config"]).config(["_","$injector","$routeProvider","CmsConfigProvider","StatusFilterOptionsProvider",function(_,$injector,$routeProvider,CmsConfigProvider,StatusFilterOptionsProvider){var CmsConfig=$injector.invoke(CmsConfigProvider.$get),StatusFilterOptions=$injector.invoke(StatusFilterOptionsProvider.$get);$routeProvider.when("/cms/app/super-features/",{ +controller:["$scope","$window","SuperFeaturesApi",function($scope,$window,SuperFeaturesApi){$window.document.title=CmsConfig.getCmsName()+" | Super Feature",$scope.modelFactory=SuperFeaturesApi,$scope.editPageUrlBuilder=function(item){return"/cms/app/edit/"+item.id+"/"+(item.polymorphic_ctype||CmsConfig.getSuperFeaturesType())},$scope.statusFilterOptions=StatusFilterOptions.getStatuses().filter(function(option){return"All"!==option.label}).map(function(option){var params={};return option.key&&(params[option.key]=_.isFunction(option.value)?option.value():option.value),{title:option.label,params:params}})}],templateUrl:CmsConfig.buildComponentPath("super-features","super-features-list","super-features-list.html")})}]),angular.module("bulbs.cms.superFeatures.relations",["bettyEditable","bulbs.cms.dateTimeFilter","bulbs.cms.site.config","bulbs.cms.superFeatures.api","bulbs.cms.sendToEditorModal","bulbs.cms.titleModal","bulbs.cms.utils","confirmationModal","lodash","moment","Raven","statusFilter.config"]).directive("superFeaturesRelations",["_","$q","CmsConfig","moment","Raven","SuperFeaturesApi","StatusFilterOptions","Utils",function(_,$q,CmsConfig,moment,Raven,SuperFeaturesApi,StatusFilterOptions,Utils){return{link:function(scope,element,attrs){var reportError=function(message,data){Raven.captureMessage(message,data),scope.errorMessage=message};scope.clearError=function(){scope.errorMessage=""},scope.$watch("relations",function(newRelations,oldRelations){angular.equals(newRelations,oldRelations)||scope.clearError()},!0);var normalizeOrderings=function(relations){relations.forEach(function(relation,i){relation.ordering=i+1})};SuperFeaturesApi.getSuperFeatureRelations(scope.article.id).then(function(response){scope.relations=response.results,normalizeOrderings(scope.relations)}).catch(function(response){var message="An error occurred retrieving relations!";reportError(message,{response:response})}),scope.statuses=StatusFilterOptions.getStatuses().filter(function(status){return!!status.value});var relationFormPrefix="relationForm_",orderingFormPrefix="orderingInputForm_";scope.wrapperForm={},scope.makeRelationFormName=function(relation){return relationFormPrefix+relation.id},scope.makeOrderingFormName=function(relation){return orderingFormPrefix+relation.id},scope.getRelationForm=function(relation){return scope.wrapperForm[scope.makeRelationFormName(relation)]},scope.getOrderingForm=function(relation){return scope.wrapperForm[scope.makeOrderingFormName(relation)]},scope.isAtLeastOneRelationFormDirty=function(){return Object.keys(scope.wrapperForm).reduce(function(isDirty,key){return key.startsWith(relationFormPrefix)?isDirty||scope.wrapperForm[key].$dirty:isDirty},!1)};var lock=Utils.buildLock();scope.transactionsLocked=lock.isLocked;var reorder=function(operation){return function(){var funcArgs=arguments,payload=scope.relations.map(function(relation){return _.pick(relation,"id","ordering")});return operation.bind(null,payload).apply(null,funcArgs),normalizeOrderings(payload),SuperFeaturesApi.updateSuperFeatureRelationsOrdering(scope.article.id,payload).then(function(){operation.bind(null,scope.relations).apply(null,funcArgs),normalizeOrderings(scope.relations)}).catch(function(response){var message="An error occurred attempting to reorder a child!";reportError(message,{response:response})})}};scope.moveRelation=lock(reorder(Utils.moveTo)),scope.addRelation=lock(function(title){return SuperFeaturesApi.createSuperFeature({parent:scope.article.id,superfeature_type:scope.article.default_child_type,title:title,ordering:(_.max(scope.relations,"ordering").ordering||0)+1}).then(function(relation){scope.relations.push(relation)}).catch(function(response){var message="An error occurred attempting to add a child page!";reportError(message,{response:response})})}),scope.updateRelationsPublishDates=lock(function(){return SuperFeaturesApi.updateAllRelationPublishDates(scope.article.id).then(function(response){scope.relations.forEach(function(relation){relation.published=moment.tz(scope.article.published,CmsConfig.getTimezoneName())})}).catch(function(response){var message="An error occurred attempting to update child publish dates!";reportError(message,{response:response})})}),scope.saveRelation=lock(function(relation){var relationCopy=angular.copy(relation);return relationCopy.parent=scope.article.id,SuperFeaturesApi.updateSuperFeature(relationCopy).catch(function(response){var titleDisplay=relation.title?'"'+relation.title+'"':"a relation",message="An error occurred attempting to update "+titleDisplay+"!";reportError(message,{response:response})}).finally(function(){scope.getRelationForm(relation).$setPristine()})}),scope.deleteRelation=lock(function(relation){return SuperFeaturesApi.deleteSuperFeature(relation).then(function(){var index=scope.relations.indexOf(relation);reorder(Utils.removeFrom)(index)}).catch(function(response){var titleDisplay=relation.title?'"'+relation.title+'"':"a relation",message="An error occurred attempting to delete "+titleDisplay+"!";reportError(message,{response:response})})})},restrict:"E",scope:{article:"="},templateUrl:CmsConfig.buildComponentPath("super-features","super-features-relations","super-features-relations.html")}}]),angular.module("bulbs.cms.superFeatures.tab.item",["bulbs.cms.site.config","bulbs.cms.superFeatures.api","filters.moment"]).directive("superFeaturesTabItem",function(CmsConfig,SuperFeaturesApi){return{link:function(scope){var requestSuperFeature=function(){angular.isNumber(scope.model)&&SuperFeaturesApi.getSuperFeature(scope.model).then(function(superFeature){scope.superFeature=superFeature})};scope.$watch("model",requestSuperFeature)},restrict:"E",scope:{model:"="},templateUrl:CmsConfig.buildComponentPath("super-features","super-features-tab","super-features-tab-item","super-features-tab-item.html")}}),angular.module("bulbs.cms.superFeatures.tab",["autocompleteBasic","bulbs.cms.site.config","bulbs.cms.utils","jquery","ui.sortable","bulbs.cms.superFeatures.tab.item","bulbs.cms.superFeatures.api"]).directive("superFeaturesTab",function($,SuperFeaturesApi,CmsConfig){return{controller:function(_,$scope,Utils,Video){$scope.moveUp=function(index){Utils.moveTo($scope.superFeatures,index,index-1),$scope.onUpdate()},$scope.moveDown=function(index){Utils.moveTo($scope.superFeatures,index,index+1),$scope.onUpdate()},$scope.delete=function(index){Utils.removeFrom($scope.superFeatures,index),$scope.onUpdate()},$scope.addSuperFeature=function(superFeature){$scope.addSuperFeatureCallback({superFeature:superFeature}),$scope.onUpdate()},$scope.searchSuperFeature=function(query){return SuperFeaturesApi.getSuperFeatures({search:query}).then(function(response){return response.results})}},link:function(scope,element,attr){scope.sortableOptions={beforeStop:function(e,ui){ui.helper.css("margin-top",0)},change:function(e,ui){ui.helper.css("margin-top",$(window).scrollTop())},containment:"super-feature-list",distance:3,opacity:.75,placeholder:"dropzone",start:function(e,ui){ui.helper.css("margin-top",$(window).scrollTop())}}},restrict:"E",scope:{addSuperFeatureCallback:"&addSuperFeature",superFeatures:"=",onUpdate:"&"},templateUrl:CmsConfig.buildComponentPath("super-features","super-features-tab","super-features-tab.html")}}),angular.module("bulbs.cms.superFeatures",["bulbs.cms.superFeatures.list","bulbs.cms.superFeatures.edit"]),angular.module("templateTypeField.directive",["bulbs.cms.site.config"]).directive("templateTypeField",function(CmsConfig){return{controller:function(_,$scope,ContentFactory,TEMPLATE_TYPES){$scope.templateTypes=_.filter(TEMPLATE_TYPES,{content_type:$scope.content.polymorphic_ctype})},restrict:"E",scope:{content:"="},templateUrl:CmsConfig.buildComponentPath("template-type-field/template-type-field.html")}}),angular.module("templateTypeField",["templateTypeField.directive"]).value("TEMPLATE_TYPES",[{name:"Small Width",slug:"small-width",content_type:"content_content"},{name:"Large Width",slug:"large-width",content_type:"content_content"}]),angular.module("bulbs.cms.titleModal",["bulbs.cms.site.config","ui.bootstrap","ui.bootstrap.modal"]).directive("titleModalOpener",["$modal","CmsConfig",function($modal,CmsConfig){return{restrict:"A",scope:{modalBodyBefore:"@",modalBodyAfter:"@",modalCancelText:"@",modalOkText:"@",modalOnCancel:"&",modalOnOk:"&",modalTitle:"@"},link:function(scope,element){element.addClass("title-modal-opener"),element.on("click",function(){scope.modalInstance||(scope.modalInstance=$modal.open({scope:scope,templateUrl:CmsConfig.buildComponentPath("title-modal","title-modal.html")}),scope.modalInstance.result.then(scope.modalOnOk).catch(scope.modalOnCancel).finally(function(){scope.modalInstance=!1}))})}}}]),angular.module("topBar.directive",["bulbs.cms.site.config"]).directive("topBar",function(CmsConfig){return{restrict:"E",scope:{logoHref:"@",itemsDropdownTitle:"@",itemsDropdown:"=",itemsTop:"=",saveFunction:"=",saveDisableWhen:"&"},templateUrl:CmsConfig.buildComponentPath("top-bar/top-bar-base.html"),link:function(scope){scope.NAV_LOGO=CmsConfig.getNavLogoPath()}}}),angular.module("topBar.item.factory",[]).factory("TopBarItem",function(){var TopBarItem=function(params){this.displayText=params.displayText||"",this.displayIconClasses=params.displayIconClasses||"",this.containerClasses=params.containerClasses||"",this.clickFunction=params.clickFunction||function(){}};return TopBarItem}),angular.module("topBar",["topBar.directive","topBar.item.factory"]),angular.module("apiServices.mixins.fieldDisplay",["restmod"]).factory("FieldDisplay",function($parse,restmod){var parserWrap=function(value){return function(value){var parsed=$parse(value);return function(record){return parsed({record:record})}}(value)},getOrdering=function(sorts){return function(sorts){return function(direction){var ordering="";return ordering="desc"===direction?"-"+sorts:sorts}}(sorts)};return restmod.mixin(function(){this.define("Scope.$fieldDisplays",function(){var fieldDisplays=this.getProperty("fieldDisplays");if(fieldDisplays){var i;for(i=0;i0)for(var key in featureTypes){var obj=featureTypes[key];obj.hasOwnProperty("featureType")&&(obj.featureType.hasOwnProperty("name")?_req.data.feature_types[key].feature_type=obj.featureType.name:obj.featureType.hasOwnProperty("value")&&(_req.data.feature_types[key].feature_type.slug=obj.featureType.value))}},"before-render":function(record){if(record.hasOwnProperty("feature_types"))for(var key in record.feature_types)record.feature_types[key].hasOwnProperty("feature_type")&&record.feature_types[key].feature_type.hasOwnProperty("name")&&(record.feature_types[key].feature_type=record.feature_types[key].feature_type.name)}}})}),angular.module("apiServices.reporting.factory",["apiServices","apiServices.mixins.fieldDisplay"]).factory("Role",function(_,restmod){var roleEndpoint="contributions/role";return restmod.model(roleEndpoint).mix("FieldDisplay","NestedDirtyModel",{flat_rates:{hasMany:restmod.model(null,{}).mix("NestedDirtyModel"),path:"flat_rates",params:{},hooks:{"after-fetch-many":function(){this.length>0?this.$owner.flat_rate=this[0]:this.$owner.flat_rate=this.$owner.flat_rates.$create({rate:0})}}},hourly_rates:{hasMany:restmod.model(null,{rate:0}).mix("NestedDirtyModel"),path:"hourly_rates",params:{},hooks:{"after-fetch-many":function(){this.length>0?this.$owner.hourly_rate=this[0]:this.$owner.hourly_rate=this.$owner.hourly_rates.$create({rate:0})}}},feature_type_rates:{hasMany:restmod.model(null,{}).mix("NestedDirtyModel"),path:"feature_type_rates",params:{},hooks:{"after-fetch-many":function(){var next=this.$metadata.next;_.isUndefined(next)||null===next||(this.$owner.feature_type_rates.$page++,this.$owner.feature_type_rates.$fetch())}}},$config:{name:"Role",plural:"Roles",primaryKey:"id",fieldDisplays:[{title:"Role",value:"record.name"},{title:"Payment Type",value:"record.paymentType"}]}})}),angular.module("apiServices.section.factory",["apiServices","apiServices.customSearch.count.factory","apiServices.mixins.fieldDisplay"]).factory("Section",function(_,CustomSearchCount,restmod){var sectionEndpoint="section";return restmod.model(sectionEndpoint).mix("FieldDisplay","NestedDirtyModel",{$config:{name:"Section",plural:"Sections",primaryKey:"id",fieldDisplays:[{title:"Section Name",value:"record.name",sorts:"name"},{title:"Article Count",value:"record.$resultCount"}]},query:{init:{}},promoted:{init:!0},$hooks:{"after-fetch":function(){this.$refreshResultCount()},"after-fetch-many":function(){_.each(this,function(record){record.$refreshResultCount()})}},$extend:{Record:{$refreshResultCount:function(){var record=this;return CustomSearchCount.$retrieveResultCount(this.query).then(function(count){record.$resultCount=count})}}}})}),angular.module("apiServices.specialCoverage.factory",["apiServices","apiServices.campaign.factory","apiServices.mixins.fieldDisplay","cms.tunic.config","filters.moment","VideohubClient.api"]).factory("SpecialCoverage",function(_,$http,$parse,$q,restmod,TunicConfig,Video){var ACTIVE_STATES={INACTIVE:"Inactive",PROMOTED:"Pin to HP"};return restmod.model("special-coverage").mix("FieldDisplay","NestedDirtyModel",{$config:{name:"Special Coverage",plural:"Special Coverages",primaryKey:"id",fieldDisplays:[{title:"List Title",value:"record.name",sorts:"name"},{title:"Sponsor",value:'record.campaign.sponsorName || "--"',sorts:"campaign__sponsor_name"},{title:"Campaign",value:'record.campaign.campaignLabel || "--"',sorts:"campaign__campaign_label"},{title:"Start Date",value:'record.startDate.format("MM/DD/YY") || "--"',sorts:"start_date"},{title:"End Date",value:'record.endDate.format("MM/DD/YY") || "--"',sorts:"end_date"}]},end_date:{encode:"moment_to_date_string"},start_date:{encode:"moment_to_date_string"},endDate:{decode:"date_string_to_moment"},startDate:{decode:"date_string_to_moment"},campaign:{belongsTo:"Campaign",prefetch:!0,key:"campaign"},listUrl:{mask:"CU"},query:{init:{}},videos:{belongsToMany:"Video",keys:"videos"},super_features:{init:[],keys:"super_features"},active:{init:!0},promoted:{init:!1},$hooks:{"after-fetch":function(){this.$loadVideosData()},"after-save":function(){this.$loadVideosData()}},$extend:{Record:{$loadVideosData:function(){_.each(this.videos,function(video){video.$fetch()})},$loadTunicCampaign:function(){return _.isNumber(this.tunicCampaignId)?$http.get(TunicConfig.buildBackendApiUrl("campaign/"+this.tunicCampaignId+"/")).then(function(result){return result.data}):$q.reject()},$searchCampaigns:function(params){return $http.get(TunicConfig.buildBackendApiUrl("campaign/"),{params:params}).then(function(response){return response.data.results})},addVideo:function(video){var added=!1,existingVideo=_.find(this.videos,function(existingVideo){return video.id===existingVideo.id});return existingVideo||(this.videos.push(video),added=!0),added},addSuperFeature:function(superFeature){var existingSuperFeature=this.superFeatures.find(function(existingSuperFeatureId){return superFeature.id===existingSuperFeatureId});existingSuperFeature||this.superFeatures.push(superFeature.id)},$activeState:function(activeState){return _.isString(activeState)?activeState===ACTIVE_STATES.ACTIVE?(this.active=!0,this.promoted=!1):activeState===ACTIVE_STATES.PROMOTED?(this.active=!0,this.promoted=!0):(this.active=!1,this.promoted=!1):(activeState=ACTIVE_STATES.INACTIVE,this.active&&this.promoted?activeState=ACTIVE_STATES.PROMOTED:this.active&&(activeState=ACTIVE_STATES.ACTIVE)),activeState}},Model:{ACTIVE_STATES:_.clone(ACTIVE_STATES)}}})}),angular.module("bulbs.cms.base.config",["bulbs.cms.config","bulbs.cms.customSearch.config","ngClipboard"]).config(["CmsConfigProvider","CustomSearchConfigProvider","ngClipProvider",function(CmsConfigProvider,CustomSearchConfigProvider,ngClipProvider){CmsConfigProvider.setContentPartialsPath("/content_type_views").setComponentPath("/components").setDateTimeFormatHumanReadable("M/D/YY h:mma z").setDirectivePartialsPath("/views").setSharedPath("/shared").setTopBarMapping("nav","/views/nav.html").setTopBarMapping("reportbar","/views/reportbar.html").setTopBarMapping("toolbar","/views/toolbar.html").setUnpublishedPath("unpublished"),CustomSearchConfigProvider.addConditionField("Content Type","content-type","name","doctype").addConditionField("Feature Type","feature-type","name","slug").addConditionField("Tag","tag","name","slug").addConditionType("is any of","any").addConditionType("is all of","all").addConditionType("is none of","none").addTimePeriod("Past Day","Past day").addTimePeriod("Past Week","Past week").addTimePeriod("Past Month","Past month"),ngClipProvider.setPath("/bower_components/zeroclipboard/dist/ZeroClipboard.swf")}]),angular.module("bulbs.cms.config",["bulbs.cms.utils","lodash"]).provider("CmsConfig",["_","UtilsProvider",function(_,Utils){var CmsConfigError=BulbsCmsConfigError.build("CmsConfig"),checkOrError=function(value,test,errorMsg){if(test(value))return value;throw new CmsConfigError(errorMsg)},pathBuilder=function(start){return function(){return Utils.path.join(arguments)}.bind(null,start)},apiUrlRoot="",articleEditoralStatuses=[],autoAddAuthor=!1,componentPath="",contentPartialsPath="",dateTimeFormatHumanReadable="",directivePartialsPath="",cmsName="",contentApiUrl="",externalUrl="",firebaseMaxArticleHistory=25,firebaseSiteRoot="",firebaseUrl="",imageApiUrl="",imageApiKey="",inlineObjectsPath="",internalUrl="",liveBlogAuthorSelectorDirectiveName="",navLogoPath="",sharedPath="",superFeaturesType="",timezoneName="America/Chicago",topBarMappings={},unpublishedPath="",videoPath="",videoThumbnailUrl="";return this.addArticleEditoralStatus=function(label,value){return checkOrError(label,_.isString,"article editoral status label must be a string!"),checkOrError(value,_.isString,"article editoral status value must be a string!"),articleEditoralStatuses.push({label:label,value:value}),this},this.setApiUrlRoot=function(value){return apiUrlRoot=checkOrError(value,_.isString,"api url root must be a string!"),this},this.setAutoAddAuthor=function(value){return autoAddAuthor=checkOrError(value,_.isBoolean,"auto add author must be a boolean!"),this},this.setComponentPath=function(value){return componentPath=checkOrError(value,_.isString,"component path must be a string!"),this},this.setContentPartialsPath=function(value){return contentPartialsPath=checkOrError(value,_.isString,"content partials path must be a string!"),this},this.setDateTimeFormatHumanReadable=function(value){return dateTimeFormatHumanReadable=checkOrError(value,_.isString,"date time format human readable must be a string!"),this},this.setDirectivePartialsPath=function(value){return directivePartialsPath=checkOrError(value,_.isString,"directive partials path must be a string!"),this},this.setCmsName=function(value){return cmsName=checkOrError(value,_.isString,"cms name must be a string!"),this},this.setContentApiUrl=function(value){return contentApiUrl=checkOrError(value,_.isString,"content api url must be a string!"),this},this.setExternalUrl=function(value){return externalUrl=checkOrError(value,_.isString,"external url must be a string!"),this},this.setFirebaseMaxArticleHistory=function(value){return firebaseMaxArticleHistory=checkOrError(value,_.isNumber,"firebase max article history must be a number!"),this},this.setFirebaseSiteRoot=function(value){return firebaseSiteRoot=checkOrError(value,_.isString,"firebase site url must be a string!"),this},this.setFirebaseUrl=function(value){return firebaseUrl=checkOrError(value,_.isString,"firebase url must be a string!"),this},this.setImageApiUrl=function(value){return imageApiUrl=checkOrError(value,_.isString,"image api url must be a string!"),window.BC_ADMIN_URL=imageApiUrl,this},this.setImageApiKey=function(value){return imageApiKey=checkOrError(value,_.isString,"image api key must be a string!"),window.BC_API_KEY=imageApiKey,this},this.setInlineObjectsPath=function(value){return inlineObjectsPath=checkOrError(value,_.isString,"inline objects path must be a string!"),this},this.setInternalUrl=function(value){return internalUrl=checkOrError(value,_.isString,"internal url must be a string!"),this},this.setLiveBlogAuthorSelectorDirectiveName=function(value){return liveBlogAuthorSelectorDirectiveName=checkOrError(value,_.isString,"live blog author selector directive name must be a string!"),this},this.setNavLogoPath=function(value){return navLogoPath=checkOrError(value,_.isString,"nav logo path must be a string!"),this},this.setSharedPath=function(value){return sharedPath=checkOrError(value,_.isString,"shared path must be a string!"),this},this.setSuperFeaturesType=function(value){return superFeaturesType=checkOrError(value,_.isString,"super features type must be a string!"),this},this.setTimezoneName=function(name){return timezoneName=checkOrError(name,moment.tz.zone,'given timezone name "'+name+'" is not a valid timezone!'),this},this.setTopBarMapping=function(name,template){var key=checkOrError(name,_.isString,"top bar mapping name must be a string!"),value=checkOrError(template,_.isString,"top bar mapping template must be a string!");return topBarMappings[key]=value,this},this.setUnpublishedPath=function(value){return unpublishedPath=checkOrError(value,_.isString,"unpublished path must be a string!"),this},this.setVideoPath=function(value){return videoPath=checkOrError(value,_.isString,"video path must be a string!"),this},this.setVideoThumbnailUrl=function(value){return videoThumbnailUrl=checkOrError(value,_.isString,"video thumbnail url must be a string!"),this},this.$get=[function(){return{buildApiUrlRoot:pathBuilder(apiUrlRoot),buildComponentPath:pathBuilder(componentPath),buildContentApiUrl:pathBuilder(contentApiUrl),buildContentPartialsPath:pathBuilder(contentPartialsPath),buildDirectivePartialsPath:pathBuilder(directivePartialsPath),buildExternalUrl:pathBuilder(externalUrl),buildFirebaseSiteUrl:pathBuilder(Utils.path.join(firebaseUrl,firebaseSiteRoot)),buildFirebaseUrl:pathBuilder(firebaseUrl),buildImageApiUrl:pathBuilder(imageApiUrl),buildInternalUrl:pathBuilder(internalUrl),buildSharedPath:pathBuilder(sharedPath),buildUnpublishedUrl:pathBuilder(Utils.path.join(internalUrl,unpublishedPath)),buildVideoUrl:pathBuilder(Utils.path.join(externalUrl,videoPath)),buildVideoThumbnailUrl:pathBuilder(videoThumbnailUrl),getAutoAddAuthor:_.constant(autoAddAuthor),getCmsName:_.constant(cmsName),getDateTimeFormatHumanReadable:_.constant(dateTimeFormatHumanReadable),getFirebaseMaxArticleHistory:_.constant(firebaseMaxArticleHistory),getImageApiKey:_.constant(imageApiKey), +getInlineObjecsPath:_.constant(inlineObjectsPath),getLiveBlogAuthorSelectorDirectiveName:_.constant(liveBlogAuthorSelectorDirectiveName),getNavLogoPath:_.constant(navLogoPath),getSuperFeaturesType:_.constant(superFeaturesType),getTimezoneName:_.constant(timezoneName),getArticleEditoralStatuses:_.constant(_.cloneDeep(articleEditoralStatuses)),getTopBarMapping:function(name){if(_.has(topBarMappings,name))return topBarMappings[name];throw new CmsConfigError('no top bar mapping exists for name "'+name+'"!')}}}],this}]),window.BulbsCmsError=function(name,message){"function"==typeof Error.captureStackTrace?Error.captureStackTrace(this,this.constructor):this.stack=(new Error).stack,this.name=name||"CMS Error",this.message=message||"Something was misconfigured."},BulbsCmsError.prototype=Object.create(Error.prototype),BulbsCmsError.prototype.constructor=window.BulbsCmsError,BulbsCmsError.build=function(name){return function(message){return new BulbsCmsError(name,message)}},window.BulbsCmsConfigError=function(name,message){window.BulbsCmsError.call(this,name,message)},BulbsCmsConfigError.prototype=Object.create(window.BulbsCmsError.prototype),BulbsCmsConfigError.prototype.constructor=window.BulbsCmsConfigError,BulbsCmsConfigError.build=function(name){return function(message){return new BulbsCmsConfigError("Configuration Error ("+(name||"name not given!")+")",message)}},angular.module("contentServices.factory",["bulbs.cms.site.config","restangular"]).factory("ContentFactory",["CmsConfig","Restangular",function(CmsConfig,Restangular){return Restangular.withConfig(function(RestangularConfigurer){RestangularConfigurer.setBaseUrl(CmsConfig.buildApiUrlRoot())})}]),angular.module("contentServices.listService",["contentServices.factory"]).service("ContentListService",function(_,$location,$q,ContentFactory){var ContentListService=this;this._serviceData={filters:$location.search()||{},content:[],totalItems:0};var _data=this._serviceData;ContentListService.updateFilters=function(addFilters,merge){return merge?_data.filters=_.assign($location.search()||{},addFilters):_data.filters=addFilters,$location.search(_data.filters),_data.filters},ContentListService.$updateContent=function(addFilters,merge){var updateParams=ContentListService.updateFilters(addFilters||_data.filters,merge);return ContentFactory.all("content").getList(updateParams).then(function(data){return _data.content=data,_data.totalItems=data.metadata.count,_data})},ContentListService.getData=function(){return _data}}),angular.module("contentServices",["contentServices.factory","contentServices.listService"]),angular.module("copyButton",["bulbs.cms.site.config"]).directive("copyButton",function(CmsConfig){return{controller:function($scope,$timeout){$scope.okCopy=!1,$scope.okCopyButton=function(){$scope.okCopy=!0,$timeout(function(){$scope.okCopy=!1},1e3)}},restrict:"E",scope:{buttonClassesDefault:"@",buttonClassesSuccess:"@",buttonText:"@",content:"@"},templateUrl:CmsConfig.buildSharedPath("copy-button/copy-button.html")}}),angular.module("filters.moment",["moment"]).filter("date_string_to_moment",function(moment){return function(dateStr){if(dateStr&&dateStr.length){var m=moment(new Date(dateStr));if(m.isValid())return m}return null}}).filter("moment_to_date_string",function(moment){return function(momentObj){return moment.isMoment(momentObj)&&momentObj.isValid()?momentObj.format():""}}),angular.module("listPage",["bulbs.cms.site.config","bulbs.cms.utils","confirmationModal","copyButton","lodash"]).directive("listPage",function(CmsConfig){return{controller:function(_,$scope,$location,$parse,Utils){$scope.pathJoin=Utils.path.join,$scope.name=$scope.modelFactory.identity(),$scope.namePlural=$scope.modelFactory.identity(!0),$scope.fields=$scope.modelFactory.$fieldDisplays(),$scope.$list=$scope.modelFactory.$collection(),$scope.orderingFilter={},$scope.searchFilter={},$scope.toggledFilters={},$scope.copyContentInContext=function(record){var value="";return $scope.toolCopyContent&&(value=$parse($scope.toolCopyContent)({record:record})),value},$scope.$retrieve=_.debounce(function(addParams){$scope.loadingResults=!0;var allParams=_.merge({},$scope.orderingFilter,$scope.toggledFilters,$scope.searchFilter,addParams);return $scope.$list.$refresh(allParams).$then(function(){$scope.loadingResults=!1})},250),$scope.$search=function(query){$scope.searchFilter={},query&&($scope.searchFilter[$scope.searchParameter]=query),$scope.$list.$page=1,$scope.$retrieve()},$scope.filterButtonsParsed=$scope.filterButtons(),$scope.$toggleFilters=function(params){$scope.toggledFilters=params,$scope.$list.$page=1,$scope.$retrieve()},$scope.sortingField=null,$scope.sortDirection="asc",$scope.$sort=function(field){var direction;direction=field.title===$scope.sortingField&&"desc"===$scope.sortDirection?"asc":"desc",function(field,direction){$scope.orderingFilter={ordering:field.getOrdering(direction)},$scope.$retrieve().$then(function(){$scope.sortingField=field.title,$scope.sortDirection=direction})}(field,direction)},$scope.$add=function(){$location.path($scope.cmsEditPageUrl({item:{id:"new"}}))},$scope.$remove=function(item){item.$destroy()},$scope.goToEditPage=function(item){$location.path($scope.cmsEditPageUrl({item:item}))},$scope.activeFilterButton=_.chain($scope.filterButtonsParsed).findWhere({active:!0}).tap(function(button){button&&($scope.toggledFilters=button.params)}).result("title").value()||"",$scope.$retrieve()},link:function(scope,element,attrs){scope.showAddButton=!("disableAddButton"in attrs)},restrict:"E",scope:{cmsEditPageUrl:"&",filterButtons:"&",modelFactory:"=",searchParameter:"@",toolCopyContent:"@",utilityButtons:"="},templateUrl:CmsConfig.buildSharedPath("list-page/list-page.html")}}),angular.module("bulbsCmsApp.nonRestmodListPage",["bulbs.cms.site.config","confirmationModal","copyButton","lodash","bulbs.cms.utils"]).directive("nonRestmodListPage",function(CmsConfig){return{controller:function(_,$scope,$location,$parse,Utils){$scope.pathJoin=Utils.path.join,$scope.orderingFilter={},$scope.searchFilter={},$scope.toggledFilters={},$scope.pageNumber=1,$scope.copyContentInContext=function(record){var value="";return $scope.toolCopyContent&&(value=$parse($scope.toolCopyContent)({record:record})),value},$scope.$retrieve=_.debounce(function(addParams){$scope.loadingResults=!0;var allParams=_.merge({},$scope.orderingFilter,$scope.toggledFilters,$scope.searchFilter,addParams);return $scope.getItems({params:allParams}).then(function(response){$scope.items=response.results,$scope.loadingResults=!1})},250),$scope.$search=function(query){$scope.searchFilter={},query&&($scope.searchFilter[$scope.searchParameter]=query),$scope.pageNumber=1,$scope.$retrieve()},$scope.filterButtonsParsed=$scope.filterButtons(),$scope.$toggleFilters=function(params){$scope.toggledFilters=params,$scope.pageNumber=1,$scope.$retrieve()},$scope.sortingField=null,$scope.sortDirection="asc",$scope.$sort=function(fieldName){var direction;direction=fieldName===$scope.sortingField&&"desc"===$scope.sortDirection?"":"-",$scope.orderingFilter={ordering:direction+fieldName},$scope.$retrieve($scope.orderingFilter.ordering).then(function(){$scope.sortingField=fieldName,$scope.sortDirection="-"===direction?"desc":"asc"})},$scope.cellContents=function(item,field){var cellContents="-";return _.isFunction(field.content)?cellContents=field.content(item):_.isString(field.content)?cellContents=item[field.content]:field.sorts&&(cellContents=item[field.sorts]),cellContents},$scope.$add=function(){$location.path($scope.cmsEditPageUrl({item:{id:"new"}}))},$scope.$remove=function(removedItem){$scope.destroyItem({item:removedItem}),_.remove($scope.items,function(item){return item===removedItem})},$scope.goToEditPage=function(item){$location.path($scope.cmsEditPageUrl({item:item}))},$scope.activeFilterButton=_.chain($scope.filterButtonsParsed).findWhere({active:!0}).tap(function(button){button&&($scope.toggledFilters=button.params)}).result("title").value()||"",$scope.$retrieve()},link:function(scope,element,attrs){scope.showAddButton=!("disableAddButton"in attrs)},restrict:"E",scope:{cmsEditPageUrl:"&",destroyItem:"&",getItems:"&",filterButtons:"&",modelFields:"&",modelName:"&",modelNamePlural:"&",searchParameter:"@",toolCopyContent:"@"},templateUrl:CmsConfig.buildSharedPath("non-restmod-list-page/non-restmod-list-page.html")}}),angular.module("cms.tunic.config",["lodash"]).provider("TunicConfig",["_",function(_){var apiPath="",backendRoot="",requestToken="",error=function(message){return new Error("Configuration Error (TunicConfig) "+message)};this.setApiPath=function(value){if(!_.isString(value))throw error("apiPath must be a string!");return apiPath=value,this},this.setBackendRoot=function(value){if(!_.isString(value))throw error("backendRoot must be a string!");return backendRoot=value,this},this.setRequestToken=function(value){if(!_.isString(value))throw error("requestToken must be a string!");return requestToken=value,this},this.$get=function(){return{getRequestToken:_.constant(requestToken),buildBackendApiUrl:function(relUrl){return backendRoot+apiPath+(relUrl||"")},shouldBeIntercepted:function(url){var urlTest=backendRoot+apiPath;return""!==urlTest&&url.startsWith(urlTest)}}}}]),angular.module("cms.tunic.interceptor",["cms.tunic.config"]).service("TunicInterceptor",["TunicConfig",function(TunicConfig){return this.request=function(config){return TunicConfig.shouldBeIntercepted(config.url)&&(config.headers=config.headers||{},config.headers.Authorization="Token "+TunicConfig.getRequestToken()),config},this}]),angular.module("cms.tunic",["cms.tunic.config","cms.tunic.interceptor"]),angular.module("bulbs.cms.utils",["lodash"]).provider("Utils",["_",function(_){var Utils=this;return Utils.slugify=function(text){return text.toString().toLowerCase().replace(/\s+/g,"-").replace(/[^\w\-]+/g,"").replace(/\-\-+/g,"-").replace(/^-+/,"").replace(/-+$/,"")},Utils.moveTo=function(list,indexFrom,indexTo,allowOutOfBounds){var ret=!1,modIndexTo=indexTo;if(allowOutOfBounds&&(indexTo<0?modIndexTo=0:indexTo>=list.length&&(modIndexTo=list.length-1)),indexFrom>=0&&indexFrom=0&&modIndexTo0&&(list[modIndexTo]=splicer[0],ret=!0)}return ret},Utils.removeFrom=function(list,index){return list.splice(index,1).length>0},Utils.param=function(params){params||(params={});var keys=Object.keys(params),query="";return keys.length>0&&(query+="?",query+=keys.map(function(key){return key+"="+params[key]}).join("&")),query},Utils.path={join:function(){var sep="/",replace=new RegExp(sep+"{1,}","g"),argsArr=_.flattenDeep(arguments),protocolPrefix="";if(argsArr.length>0&&"string"==typeof argsArr[0]){var matches=argsArr[0].match(/^(https?:)?\/\//);matches&&(protocolPrefix=matches[0],argsArr[0]=argsArr[0].replace(protocolPrefix,""))}return protocolPrefix+argsArr.join(sep).replace(replace,sep)}},Utils.$get=["$q",function($q){return Utils.buildLock=function(){var locked=!1,wrapper=function(func){return function(){return locked?$q.reject():(locked=!0,$q.when(func.apply(null,arguments)).finally(function(){locked=!1}))}};return wrapper.isLocked=function(){return locked},wrapper},Utils}],Utils}]),angular.module("videoList.video.directive",["bulbs.cms.site.config","filters.moment"]).directive("videoListVideo",function(CmsConfig){return{restrict:"E",scope:{model:"="},templateUrl:CmsConfig.buildSharedPath("video-list/video-list-video/video-list-video.html")}}),angular.module("videoList",["autocompleteBasic","bulbs.cms.site.config","bulbs.cms.utils","jquery","ui.sortable","VideohubClient.api","VideohubClient.settings","videoList.video.directive"]).directive("videoList",function($,CmsConfig){return{controller:function(_,$scope,Utils,Video,VIDEOHUB_DEFAULT_CHANNEL){$scope.videoChannel=VIDEOHUB_DEFAULT_CHANNEL,$scope.moveUp=function(index){Utils.moveTo($scope.videos,index,index-1),$scope.onUpdate()},$scope.moveDown=function(index){Utils.moveTo($scope.videos,index,index+1),$scope.onUpdate()},$scope.delete=function(index){Utils.removeFrom($scope.videos,index),$scope.onUpdate()},$scope.addVideo=function(video){$scope.addVideoCallback({video:video}),$scope.onUpdate()},$scope.searchVideos=function(query){return Video.$postSearch({query:query,channel:VIDEOHUB_DEFAULT_CHANNEL})}},link:function(scope,element,attr){scope.sortableOptions={beforeStop:function(e,ui){ui.helper.css("margin-top",0)},change:function(e,ui){ui.helper.css("margin-top",$(window).scrollTop())},containment:"video-list",distance:3,opacity:.75,placeholder:"dropzone",start:function(e,ui){ui.helper.css("margin-top",$(window).scrollTop())}}},restrict:"E",scope:{addVideoCallback:"&addVideo",videos:"=",onUpdate:"&"},templateUrl:CmsConfig.buildSharedPath("video-list/video-list.html")}}),angular.module("bulbsCmsApp").directive("activeNav",function($location){return{template:'
  • {{label}}
  • ',restrict:"E",scope:{},replace:!0,link:function(scope,element,attrs){scope.href=attrs.href,scope.label=attrs.label,0===$location.path().indexOf(scope.href)&&element.addClass("active")}}}),angular.module("bulbsCmsApp").directive("addImage",function($http,$window){return{restrict:"E",templateUrl:"/views/add-image.html",scope:{article:"="},link:function(scope,element,attrs){var attrName=attrs.attrName||"images";scope.article[attrName]=scope.article[attrName]||[],"false"===attrs.caption&&(scope.hideCaption=!0),scope.format=attrs.format||"jpg",scope.crop=attrs.crop||"16x9",scope.placeholderText=attrs.placeholderText||"Optional Image",scope.addAnImage=function(){$window.uploadImage({onSuccess:function(data){scope.$apply(function(){scope.article[attrName].push({id:data.id.toString(),alt:null,caption:null}),setTimeout($window.picturefill,200)})},onError:function(data){scope.$apply(function(){$window.alert("Error: ",data)})},onProgress:function(data){}})}}}}),angular.module("bulbsCmsApp").directive("articlecontainer",function(){return{restrict:"E",templateUrl:"/views/promotion-tool-article-container.html",scope:{article:"="},link:function(scope,element,attrs){scope.ratio=attrs.ratio}}}),angular.module("bulbsCmsApp").directive("authorsField",function($,CmsConfig,userNameDisplayFilter,Utils){return{templateUrl:"/views/taglike-autocomplete-field.html",restrict:"E",replace:!0,scope:{article:"=",inputLabelText:"@",inputLabelTextSub:"@"},link:function(scope,element,attrs){scope.name="author",scope.label=scope.inputLabelText||"Authors",scope.labelSub=scope.inputLabelTextSub,scope.placeholder="Authors",scope.resourceUrl=CmsConfig.buildApiUrlRoot("author",Utils.param({ordering:"name",search:""})),scope.display=userNameDisplayFilter,scope.$watch("article.authors",function(){scope.objects=scope.article.authors},!0),scope.add=function(o,input){for(var t in scope.article.authors)if(scope.article.authors[t].id===o.id)return;scope.article.authors.push(o),$(input).val("")},scope.delete=function(e){var author=$(e.target).parents("[data-taglikeobject]").data("taglikeobject"),id=author.id,newauthors=[];for(var i in scope.article.authors)scope.article.authors[i].id!==id&&newauthors.push(scope.article.authors[i]);scope.article.authors=newauthors}}}}),angular.module("bulbsCmsApp").directive("bulbsAutocomplete",function($http,$location,$compile,$timeout,$,Login,Raven){var autocomplete_dropdown_template='';return{restrict:"AC",link:function(scope,element,attrs){function getAutocompletes(val){$timeout.cancel(inputTimeout),inputCounter=0,$http({method:"GET",url:scope.resourceUrl+val}).success(function(data){var results=data.results||data;scope.autocomplete_list=results.splice(0,5)}).error(function(data,status,headers,config){Raven.captureMessage("Error in getAutocompletes",{extra:data})})}var $elem=$(element).find("input");$elem.attr("autocomplete","off");var dropdown=$($compile(autocomplete_dropdown_template)(scope));$(dropdown).css({position:"absolute",top:$elem.position().top+$elem.outerHeight(),left:$elem.position().left,minWidth:$elem.outerWidth(),display:"none"}),$elem.parent().append(dropdown),$(dropdown).fadeIn("fast"),scope.$watch(function(){return{top:$elem.position().top+$elem.outerHeight(),left:$elem.position().left,minWidth:$elem.outerWidth()}},function(newValue,oldValue){$(dropdown).css({top:newValue.top,left:newValue.left,minWidth:newValue.minWidth})},!0);var inputTimeout,inputCounter=0;$elem.on("focus",function(e){$elem.on("input",function(){var val=$elem.val();""===val?scope.autocomplete_list=[]:($timeout.cancel(inputTimeout),inputTimeout=$timeout(function(){getAutocompletes(val)},200),inputCounter>2&&getAutocompletes(val))}),$(dropdown).fadeIn("fast")}),$elem.on("blur",function(e){$(dropdown).fadeOut("fast")}),$(dropdown).on("mouseover",".entry",function(e){$(dropdown).find(".selected").removeClass("selected"),$(this).addClass("selected")}),$elem.on("keyup",function(e){if(40===e.keyCode)if(0===$("div.selected",dropdown).length)$("div.entry",dropdown).first().addClass("selected");else{var curDownSelect=$("div.selected",dropdown),curDownSelectNext=curDownSelect.next("div");0===curDownSelectNext.length?$("div.entry",dropdown).first().addClass("selected"):curDownSelectNext.addClass("selected"),curDownSelect.removeClass("selected")}if(38===e.keyCode)if(0===$("div.selected",dropdown).length)$("div.entry",dropdown).last().addClass("selected");else{var curSelect=$("div.selected",dropdown),curSelectNext=curSelect.prev("div");0===curSelectNext.length?$("div.entry",dropdown).last().addClass("selected"):curSelectNext.addClass("selected"),curSelect.removeClass("selected")}if(13===e.keyCode){var selected=$("div.selected",dropdown);0===selected.length&&scope.onClick($elem.val(),!0),selected.click()}}),scope.onClick=function(o,freeForm){scope.add(o,$elem,freeForm||!1),scope.autocomplete_list=[]}}}}),angular.module("bulbsCmsApp").directive("cmsNotification",function(){return{restrict:"E",templateUrl:"/views/cms-notification.html",scope:{cmsNotification:"="},controller:"CmsNotificationCtrl"}}),angular.module("bulbsCmsApp").directive("cmsNotifyContainer",function(){return{restrict:"E",scope:{},templateUrl:"/views/cms-notify-container.html",controller:"CmsNotifyContainerCtrl"}}),angular.module("bulbsCmsApp").directive("contributorField",function($){return{templateUrl:"/views/textlike-autocomplete-field.html",restrict:"E",replace:!0,scope:{override:"="},link:function(scope,element,attrs){scope.name="contributor",scope.label="Contributors",scope.placeholder="Contributors",scope.resourceUrl="/cms/api/v1/author/?ordering=name&search=",scope.$watch("override.contributor",function(){scope.override.hasOwnProperty("contributor")&&null!==scope.override.contributor&&(scope.model=scope.override.contributor.full_name||scope.override.contributor.fullName)}),scope.display=function(o){return o&&o.full_name||""},scope.add=function(o,input){scope.override.hasOwnProperty("contributor")&&null!==scope.override.contributor&&scope.override.contributor.id===o.id||(scope.override.contributor=o,$("#feature-type-container").removeClass("newtag"),$("#feature-type-container").addClass("newtag"))},scope.delete=function(e){scope.override.contributor=null,scope.model=null}}}}),angular.module("bulbsCmsApp").directive("createContent",function($http,$window,$,IfExistsElse,Login,ContentFactory,CmsConfig,Raven){return{restrict:"E",templateUrl:CmsConfig.buildDirectivePartialsPath("create-content.html"),controller:function($scope){function saveArticle(){$("button.go").removeClass("btn-danger").addClass("btn-success").html(' Going'),$http({url:"/cms/api/v1/content/?doctype="+$scope.contentType,method:"POST",data:$scope.init}).success(function(resp){var new_id=resp.id,new_path="/cms/app/edit/"+new_id+"/";$scope.rating_type&&(new_path+="?rating_type="+$scope.rating_type),$window.location.href=$window.location.origin+new_path}).error(function(data,status,headers,config){403===status?$("button.go").html(' Please Log In'):$("button.go").removeClass("btn-success").addClass("btn-danger").html(' Error'),$scope.gotSave=!1})}$scope.gotTags=!1,$scope.gotUser=!1,$scope.gotSave=!1,$scope.$watch(function(){return $scope.gotTags&&$scope.gotUser&&$scope.gotSave},function(val){val&&saveArticle($scope.init)}),$scope.newArticle=function(e){var init={title:$scope.newTitle};angular.extend($scope.init,init),$scope.tag?IfExistsElse.ifExistsElse(ContentFactory.all("tag").getList({ordering:"name",search:$scope.tag,types:"core_section"}),{slug:$scope.tag},function(tag){$scope.init.tags=[tag],$scope.gotTags=!0},function(value){console.log("couldnt find tag "+value.slug+" for initial value")},function(data,status,headers,config){Raven.captureMessage("Error Creating Article",{extra:data})}):$scope.gotTags=!0,CmsConfig.getAutoAddAuthor()?ContentFactory.one("me").get().then(function(data){$scope.init.authors=[data],$scope.gotUser=!0}):$scope.gotUser=!0,$scope.gotSave=!0}},link:function(scope,element,attrs){scope.panel=1,$(element).find("a.create-content").on("click",function(e){$("a.create-content.active").removeClass("active"),$(this).addClass("active")}),$(element).find("a.create-content").on("click",function(e){return scope.contentTypeLabel=$(this).text(),scope.contentType=$(this).data("content_type")||null,scope.init=$(this).data("init")||{},scope.tag=$(this).data("tag")||null,scope.rating_type=$(this).data("rating_type")||null,scope.$apply(),!$(this).hasClass("go-next")||(e.preventDefault(),$("#create button.next-pane").click(),!1)}),$("button.next-pane:not(.hide)").on("click",function(e){scope.panel=2,$(".new-title").focus()}),$(element).on("keydown",".editor",function(e){13===e.keyCode&&scope.newTitle&&$(element).find(".go").click()}),$("#create").on("hidden.bs.modal",function(){scope.newTitle="",scope.panel=1})}}}),angular.module("bulbsCmsApp").directive("devicepreview",function($){return{restrict:"E",templateUrl:"/views/devicepreview.html",link:function(scope,element,attrs){var pP=$("#page-prev"),tN=pP.find(".nav a"),cO=pP.find(".tab-content .active");tN.click(function(e){var newId=$(this).attr("href").split("#")[1];e.preventDefault(),cO.attr("id",newId)}),$("#page-prev").on("show.bs.collapse",function(){$(this).find(".fa").removeClass("fa-plus-square-o").addClass("fa-minus-square-o")}),$("#page-prev").on("hide.bs.collapse",function(){$(this).find(".fa").removeClass("fa-minus-square-o").addClass("fa-plus-square-o")})}}}),angular.module("bulbsCmsApp").directive("encodeStatus",function($http,$interval,$,Zencoder){return{templateUrl:"/views/encode-status.html",restrict:"E",link:function(scope,element,attrs){function updateEncodeStatuses(){var updateEncodeStatus=function(i){return function(videoid){Zencoder.encodingVideos[videoid].encode_status_endpoints&&Zencoder.encodingVideos[videoid].encode_status_endpoints.json&&$http({method:"GET",url:Zencoder.encodingVideos[videoid].encode_status_endpoints.json,headers:{"X-CSRFToken":void 0}}).success(function(data){scope.encodingVideos[videoid].job_status=data,"finished"===data.state&&(scope.encodingVideos[videoid].finished=!0)})}(i)};for(var i in Zencoder.encodingVideos)scope.encodingVideos[i]&&scope.encodingVideos[i].finished||(scope.encodingVideos[i]=Zencoder.encodingVideos[i],updateEncodeStatus(i))}scope.encodingVideos={},scope.$watch(function(){return Zencoder.encodingVideos},function(){updateEncodeStatuses()},!0),$interval(function(){$("iframe").filter(function(){return this.src.match(/\/video\/embed\/?/)}).each(function(){var idRegex=/\/video\/embed\/?\?id=(\d+)/,id=idRegex.exec(this.src)[1];id in Zencoder.encodingVideos||Zencoder.getVideo(id).then(function(data){Zencoder.encodingVideos[id]=data.data})}),updateEncodeStatuses()},5e3)}}}),angular.module("bulbsCmsApp").directive("featuretypeField",function(IfExistsElse,ContentFactory,Raven,$){return{templateUrl:"/views/textlike-autocomplete-field.html",restrict:"E",scope:{article:"=",hideLabel:"="},replace:!0,link:function(scope,element,attrs){scope.name="feature_type",scope.label="Feature Type",scope.placeholder="Feature Type",scope.resourceUrl="/cms/api/v1/things/?type=feature_type&q=",scope.$watch("article.feature_type",function(){scope.model=scope.article.feature_type}),scope.display=function(o){return o&&o.name||""},scope.add=function(o,input,freeForm){var fVal=freeForm?o:o.name;IfExistsElse.ifExistsElse(ContentFactory.all("things").getList({type:"feature_type",q:fVal}),{name:fVal},function(ft){scope.article.feature_type=ft.name,$("#feature-type-container").removeClass("newtag")},function(value){scope.article.feature_type=value.name,$("#feature-type-container").addClass("newtag")},function(data,status){Raven.captureMessage("Error Adding Feature Type",{extra:data})})},scope.delete=function(e){scope.article.feature_type=null}}}}),angular.module("bulbsCmsApp").directive("hideIfForbidden",function($http){function hideElement(element){element.addClass("hidden")}return{restrict:"A",link:function(scope,element,attrs){$http({method:"OPTIONS",url:attrs.optionsUrl,noPermissionIntercept:!0}).success(function(data,status){403===status&&hideElement(element)}).error(function(data,status){403===status&&hideElement(element)})}}}),angular.module("bulbsCmsApp").directive("lazyInclude",function($,$compile,$q,$http,$templateCache,Gettemplate){return{restrict:"A",scope:!0,link:function(scope,element,attrs){var templateUrl="/views/"+attrs.template,$element=$(element);scope.$evalAsync(function(){scope.$watch(function(){return $element.is(":visible")},function(visible){visible&&!scope.loaded&&(scope.loaded=!0,Gettemplate.get(templateUrl).then(function(html){var template=angular.element(html),compiledEl=$compile(template)(scope);element.html(compiledEl),element.css("height","auto")}))})})}}});angular.module("bulbsCmsApp").directive("loggedInUser",function(CurrentUserApi){return{restrict:"E",replace:!0,templateUrl:"/views/logged-in-user.html",scope:{},link:function(scope,element,attrs){CurrentUserApi.getCurrentUserWithCache().then(function(user){scope.currentUser=user,scope.logout=CurrentUserApi.logout})}}});angular.module("bulbsCmsApp").directive("navBar",function(CmsConfig,CurrentUserApi){return{restrict:"E",scope:!1,templateUrl:function(tElement,tAttrs){return CmsConfig.getTopBarMapping(tAttrs.view)},link:function(scope){scope.NAV_LOGO=CmsConfig.getNavLogoPath(),CurrentUserApi.getCurrentUserWithCache().then(function(user){scope.currentUser=user})}}}),angular.module("bulbsCmsApp").directive("responsiveImage",function($window,$){return{link:function(scope,element,attrs){attrs.$observe("imageId",function(val){$(element).find("img").remove(),element.attr("data-image-id",attrs.imageId),element.attr("data-crop",attrs.crop),attrs.imageId&&$(element).is(":visible")&&($window.pictureFillElement(element[0]),element.show())})}}}),angular.module("bulbsCmsApp").directive("roleField",function($http,$,Raven){return{templateUrl:"/views/rolefield.html",restrict:"E",replace:!0,scope:{model:"="},link:function(scope,element,attrs){var resourceUrl="/cms/api/v1/contributions/role/";scope.roleValue=null,scope.roleOptions=[],scope.$watch("model.role",function(){for(var i=0;i
    Save",busy:"Saving",finished:"Saved",error:"Error"})}),scope.save=function(){if(attrs.confirmClickWith){var message=attrs.confirmClickWith;if(!$window.confirm(message))return}scope.colors=scope.colors_tmp,element.prop("disabled",!0).html(" "+scope.config.busy);var save_promise=scope.getPromise(),saveSuccess=function(result){return scope.colors=scope.colors_tmp,element.prop("disabled",!1).html(" "+scope.config.finished),$timeout(function(){element.html(scope.config.idle)},1e3).then(function(){return result})};if(save_promise){var promise=save_promise.then(saveSuccess).catch(function(reason){return scope.colors="btn-danger",element.prop("disabled",!1).html(" "+scope.config.error),$q.reject(reason)});scope.saveCbk&&scope.saveCbk({promise:promise})}else saveSuccess()}}}}),angular.module("bulbsCmsApp").directive("sectionsField",function(_,IfExistsElse,ContentFactory,Raven,$){return{templateUrl:"/views/taglike-autocomplete-field.html",restrict:"E",replace:!0,link:function(scope,element,attrs){scope.name="section",scope.label="Sections",scope.placeholder="Enter a section",scope.resourceUrl="/cms/api/v1/tag/?ordering=name&types=core_section&search=",scope.display=function(o){return o.name},scope.$watch("article.tags",function(){scope.objects=_.where(scope.article.tags,{type:"core_section"})},!0),scope.add=function(o,input,freeForm){var tagVal=freeForm?o:o.name;IfExistsElse.ifExistsElse(ContentFactory.all("tag").getList({ordering:"name",search:tagVal,types:"core_section"}),{name:tagVal},function(tag){scope.article.tags.push(tag)},function(){console.log("Can't create sections.")},function(data,status){Raven.captureMessage("Error Adding Section",{extra:data})}),$(input).val("")},scope.delete=function(e){var tag=$(e.target).parents("[data-taglikeobject]").data("taglikeobject"),name=tag.name,newtags=[];for(var i in scope.article.tags)scope.article.tags[i].name!==name&&newtags.push(scope.article.tags[i]);scope.article.tags=newtags}}}}),angular.module("bulbsCmsApp").directive("slideshowPane",function($http,$window,$compile,$){return{restrict:"E",templateUrl:"/views/slideshow-pane.html",scope:{article:"=",image:"=",index:"="},link:function(scope,element,attrs){var $element=$(element);"false"===attrs.caption&&(scope.hideCaption=!0),scope.format=attrs.format||"jpg",scope.crop=attrs.crop||"16x9",scope.removeImage=function(index){scope.article.slides.splice(index,1)},scope.editImage=function(index){var loadingClass="loading-class";$window.openImageDrawer(scope.article.slides[index].id,function(data){function removeLoadingGif(){$element.find(".image ."+loadingClass).remove()}removeLoadingGif(),$element.find(".image").data("imageId")!==data.id&&($element.find(".image img").on("load",removeLoadingGif),$element.find(".image img").after(''),scope.article.slides[index].id=data.id.toString(),scope.$apply(),$window.picturefill(),$element.find(".image img")[0].complete&&removeLoadingGif())},function(){},function(oldImage){scope.article.slides[index]=oldImage,$window.picturefill()})}}}}),angular.module("bulbsCmsApp").directive("tagsField",function(_,IfExistsElse,ContentFactory,Raven,$){return{templateUrl:"/views/taglike-autocomplete-field.html",restrict:"E",scope:{article:"="},replace:!0,link:function(scope,element,attrs){scope.name="tag",scope.label="Tags",scope.placeholder="Enter a tag",scope.resourceUrl="/cms/api/v1/tag/?ordering=name&types=content_tag&search=",scope.display=function(o){return o.name},scope.$watch("article.tags",function(){scope.objects=_.where(scope.article.tags,{type:"content_tag"})},!0),scope.add=function(o,input,freeForm){var tagVal=freeForm?o:o.name;IfExistsElse.ifExistsElse(ContentFactory.all("tag").getList({ordering:"name", +search:tagVal}),{name:tagVal},function(tag){scope.article.tags.push(tag)},function(value){scope.article.tags.push({name:value.name,type:"content_tag",new:!0})},function(data,status){Raven.captureMessage("Error Adding Tag",{extra:data})}),$(input).val("")},scope.delete=function(e){var tag=$(e.target).parents("[data-taglikeobject]").data("taglikeobject"),name=tag.name,newtags=[];for(var i in scope.article.tags)scope.article.tags[i].name!==name&&newtags.push(scope.article.tags[i]);scope.article.tags=newtags}}}}),angular.module("bulbsCmsApp").directive("targeting",function(){return{restrict:"E",templateUrl:"/views/targeting.html",link:function(scope,element,attrs){scope.addTargetingRow=function(index){scope.targetingArray.push([])},scope.removeTargetingRow=function(index){scope.targetingArray.splice(index,1)}}}}),angular.module("bulbsCmsApp").directive("videoUpload",function($http,$window,$timeout,$sce,$){return{templateUrl:"/views/mainvideo.html",scope:{article:"="},restrict:"E",link:function(scope,element,attrs){function abortUpload(){setProgress(0),scope.req&&scope.req.abort(),scope.video={},setProgress(0)}function abortEncode(){$.ajax("https://app.zencoder.com/api/v2/jobs/"+scope.video.job_id+"/cancel.json?api_key="+$window.videoAttrs.zencoderApiKey,{type:"PUT",success:function(data){scope.video.status=3,fakeInput.val("Encoding failed! Please try again.")}})}function setProgress(progress){return 0===progress||100===progress?void progressEl.hide():void((0===scope.lastProgress||Math.abs(progress-scope.lastProgress)>2)&&(progressBar.css("width",Math.floor(progress)+"%"),scope.lastProgress=progress,progressEl.show()))}function updateEncodeProgress(){progressBar.addClass("progress-bar-success"),delete $http.defaults.headers.common["X-Requested-With"],$http({url:"https://app.zencoder.com/api/v2/jobs/"+scope.video.job_id+"/progress.json",method:"GET",params:{api_key:$window.videoAttrs.zencoderApiKey},useXDomain:!0}).success(function(data){"waiting"===data.state||"pending"===data.state||"processing"===data.state?(scope.video.status=2,data.progress>5?(setProgress(data.progress),$timeout(updateEncodeProgress,500)):$timeout(updateEncodeProgress,2e3)):(setProgress(0),"finished"===data.state&&(scope.video.status=1),"failed"!==data.state&&"cancelled"!==data.state||(scope.video.status=3,fakeInput.val("Encoding failed! Please try again.")))}).error(function(data){$(".alert-danger").fadeIn().delay(1e3).fadeOut()})}console.log("video upload here"),console.log(scope.video_id),scope.$watch("article.video",function(){scope.article.video&&(scope.embedUrl=$sce.trustAsUrl("/video/embed?id="+scope.article.video),$http({method:"GET",url:"/videos/api/video/"+scope.article.video+"/"}).success(function(data){console.log("getting video from API"),console.log(data),scope.video=data,$window.initVideoWidget(data.id)}))}),scope.$watch("video",function(){});var progressEl=element.find("div.progress"),progressBar=element.find("div.progress-bar"),fakeInput=element.find("input.fake-input");scope.lastProgress=0,scope.addVideo=function(){console.log("chooseFile"),$window.uploadVideo(element.find(".video-container")[0],{onSuccess:function(videoid){scope.$apply(function(){console.log("addVideo onSuccess callback"),console.log(videoid),scope.article.video=videoid})},onError:function(data){console.log("addVideo onError callback"),console.log(data)},onProgress:function(data){console.log("addVideo onProgress callback"),console.log(data)}})},scope.clearVideo=function(areYouSure){areYouSure?($("#s3upload-file-input").val(""),scope.article.video=null):$("#confirm-clear-video-modal").modal("show")},scope.abort=function(){return scope.encoding?void abortEncode():void abortUpload()};var initialCheckRan=!1;scope.$watch("video",function(){scope.video&&scope.video.job_id&&!initialCheckRan&&(updateEncodeProgress(),initialCheckRan=!0)})}}}),angular.module("bulbsCmsApp").directive("videoEmbed",function(CmsConfig){return{template:'
    ',restrict:"E",link:function(scope,element,attrs){scope.videoUrl=CmsConfig.buildVideoUrl(attrs.videoId)}}}),angular.module("bulbsCmsApp").directive("videoField",function(Zencoder){return{templateUrl:"/views/video-field.html",restrict:"E",scope:{article:"="},link:function(scope,element,attrs){scope.removeVideo=function(){scope.article.video=null},scope.uploadVideo=function(){Zencoder.onVideoFileUpload().then(function(success){console.log(success),scope.article.video=success.attrs.id},angular.noop,function(progress){console.log(progress),scope.uploadProgress=progress})},scope.thumbnailModal=function(){Zencoder.openVideoThumbnailModal(scope.article.video).result.then(function(resolve){console.log("thumbnail modal resolve"),console.log(resolve)},function(reject){console.log("thumbnail modal rejected")})}}}}),angular.module("bulbsCmsApp").directive("autocompleteMenu",function($timeout,$animate,$compile){return{restrict:"E",replace:!0,transclude:!0,controller:angular.noop,scope:{items:"=items",pIndex:"=index",select:"&select"},link:function($scope,element,attrs){$scope.selectItem=function(index){$scope.select(index)},$scope.setIndex=function(index){$scope.index=index,attrs.index&&($scope.pIndex=parseInt(index,10))},attrs.index&&$scope.$watch("pIndex",function(value){$scope.index=parseInt(value,10)}),$scope.label=function(index){var viewValue=$scope.items[index][attrs.labelAttr];return"function"==typeof viewValue&&(viewValue=viewValue()),viewValue}},template:''}}),angular.module("bulbsCmsApp").directive("autocomplete",function($timeout,$animate,$compile){return{restrict:"E",replace:!0,require:"ngModel",transclude:!0,controller:function($scope,$element,$attrs,$injector){$scope.service=$injector.get($attrs.service),$scope.placeholder=$attrs.placeholder||""},link:function($scope,element,attrs,ngModel,transclude){function queryData(query){var searchParams={};searchParams[attrs.searchParam||"search"]=query,$scope.service.getList(searchParams).then(function(results){results.length>5?menuScope.items=results.slice(0,5):menuScope.items=results,timeoutId=null})}function appendMenu(){isMenuAppended||(isMenuAppended=!0,menuScope.index=0,$animate.enter(menuEl,element.parent(),element)),styleMenu()}function reset(){ngModel.$render(),menuScope.items=[],menuScope.index=0,$animate.leave(menuEl).finally(function(){isMenuAppended=!1})}function styleMenu(){var offset=(window.getComputedStyle(element[0]),element.offset());offset.left="auto",offset.right="auto",offset.top=element.outerHeight(),offset.minWidth=element.outerWidth(),angular.forEach(offset,function(value,key){!isNaN(value)&&angular.isNumber(value)&&(value+="px"),menuEl[0].style[key]=value,menuEl.css("z-index",1e3)})}var isMenuAppended=!1,inputEl=element.find("input"),timeoutId=null;ngModel.$render=function(){if(ngModel.$viewValue){var viewValue=ngModel.$viewValue[attrs.labelAttr];"function"==typeof viewValue&&(viewValue=viewValue()),element.find("input").val(viewValue),inputEl.attr("disabled","disabled")}},$scope.openMenu=function(e){inputEl.removeAttr("disabled"),inputEl[0].focus()},inputEl.on("blur keyup change",function(){if(void 0===inputEl.attr("disabled")){appendMenu();var value=inputEl.val();value&&(timeoutId&&$timeout.cancel(timeoutId),timeoutId=$timeout(function(){queryData(value)},150))}});var menuScope=$scope.$new();menuScope.items=[],menuScope.index=0,menuScope.select=function(index){ngModel.$setViewValue(menuScope.items[index]),reset()};var menuEl=angular.element(document.createElement("autocomplete-menu"));menuEl.attr({items:"items",select:"select(index)",index:"index","label-attr":attrs.labelAttr}),transclude(menuScope,function(clone){menuEl.append(clone)}),$compile(menuEl)(menuScope),element.find("input").on("keyup",function(e){switch(e.which){case 27:""===inputEl.val()?reset():inputEl.val("");break;case 40:$scope.$apply(function(){menuScope.index=(menuScope.index+1)%menuScope.items.length});break;case 38:$scope.$apply(function(){menuScope.index?menuScope.index=menuScope.index-1:menuScope.index=menuScope.items.length-1});break;case 13:menuScope.index>=0&&(ngModel.$setViewValue(menuScope.items[menuScope.index]),reset());break;default:return}})},templateUrl:"/views/autocomplete.html"}}),angular.module("bulbsCmsApp").controller("BadrequestmodalCtrl",function($scope,$modalInstance,detail){$scope.detail=detail}),angular.module("bulbsCmsApp").controller("ChangelogmodalCtrl",function($scope,$modalInstance,_,ContentFactory,article){$scope.article=article,$scope.users={},ContentFactory.all("log").getList({content:article.id}).then(function(data){$scope.changelog=data;var userIds=_.unique(_.pluck(data,"user")),resp=function(data){$scope.users[data.id]=data};for(var i in userIds)ContentFactory.one("author",userIds[i]).get().then(resp)})}),angular.module("bulbsCmsApp").controller("CmsNotificationCtrl",function($scope,moment){var valid=function(){$scope.postDateValid=$scope.postDate&&(!$scope.notifyEndDate||$scope.postDate<$scope.notifyEndDate),$scope.notifyEndDateValid=$scope.notifyEndDate&&$scope.postDate&&$scope.notifyEndDate>$scope.postDate,$scope.titleValid=$scope.cmsNotification.title&&$scope.cmsNotification.title.length>0&&$scope.cmsNotification.title.length<=110,$scope.cmsNotificationValid=$scope.postDateValid&&$scope.notifyEndDateValid&&$scope.titleValid};$scope.postDate=$scope.cmsNotification.post_date?moment($scope.cmsNotification.post_date):null,$scope.$watch("postDate",function(){$scope.postDate?($scope.cmsNotification.post_date=$scope.postDate.format(),$scope.notifyEndDate=$scope.postDate.clone().add({days:3})):$scope.cmsNotification.post_date=null}),$scope.notifyEndDate=$scope.cmsNotification.notify_end_date?moment($scope.cmsNotification.notify_end_date):null,$scope.$watch("notifyEndDate",function(){$scope.notifyEndDate?$scope.cmsNotification.notify_end_date=$scope.notifyEndDate.format():$scope.cmsNotification.notify_end_date=null}),$scope.cmsNotificationDirty=!1,$scope.$watch("cmsNotification",function(newValue,oldValue){angular.equals(newValue,oldValue)||($scope.cmsNotificationDirty=!0,valid())},!0),valid(),$scope.saveCmsNotification=function(){$scope.$parent.userIsSuperuser&&$scope.cmsNotificationDirty&&$scope.cmsNotificationValid&&$scope.$parent.$saveCmsNotification($scope.cmsNotification).then(function(newCmsNotification){$scope.cmsNotification=newCmsNotification,$scope.cmsNotificationDirty=!1}).catch(function(error){console.log("CMS Alert save failed",error)})},$scope.deleteCmsNotification=function(){$scope.$parent.userIsSuperuser&&$scope.$parent.$deleteCmsNotification($scope.cmsNotification).catch(function(error){console.log("CMS Alert delete failed",error)})}}),angular.module("bulbsCmsApp").controller("CmsNotificationsCtrl",function($q,$window,$scope,CmsConfig,CmsNotificationsApi,CurrentUserApi,_,moment){$window.document.title=CmsConfig.getCmsName()+" | CMS Alerts",CurrentUserApi.getCurrentUserWithCache().then(function(user){user.is_superuser&&($scope.userIsSuperuser=!0),CmsNotificationsApi.getList().then(function(cmsNotifications){var removeIndicies=[];_.each(cmsNotifications,function(cmsNotification,i){!user.is_superuser&&moment(cmsNotification.post_date).isAfter(moment())&&removeIndicies.push(i)}),_.each(removeIndicies,function(i){cmsNotifications.splice(i,1)}),$scope.cmsNotifications=cmsNotifications})}),$scope.newCmsNotification=function(){var cmsNotification={post_date:null,notify_end_date:null};return $scope.cmsNotifications.unshift(cmsNotification),cmsNotification},$scope.$saveCmsNotification=function(cmsNotification){var saveDefer=$q.defer(),savePromise=saveDefer.promise;return $scope.userIsSuperuser?"id"in cmsNotification?cmsNotification.put().then(function(updatedCmsNotification){saveDefer.resolve(updatedCmsNotification)}):$scope.cmsNotifications.post(cmsNotification).then(function(newCmsNotification){var i=$scope.cmsNotifications.indexOf(cmsNotification);$scope.cmsNotifications[i]=newCmsNotification,saveDefer.resolve(newCmsNotification)}).catch(function(error){saveDefer.reject(error)}):saveDefer.reject("Insufficient permissions."),savePromise},$scope.$deleteCmsNotification=function(cmsNotification){var deleteDefer=$q.defer(),deletePromise=deleteDefer.promise,removeFromList=function(index){$scope.cmsNotifications.splice(index,1),deleteDefer.resolve()};if($scope.userIsSuperuser){var i=$scope.cmsNotifications.indexOf(cmsNotification);i>-1?_.isFunction(cmsNotification.remove)?cmsNotification.remove().then(function(){removeFromList(i)}).catch(function(error){deleteDefer.reject(error)}):removeFromList(i):deleteDefer.reject("Cannot find notification in notification list. Unable to delete.")}else deleteDefer.reject("You do not have sufficient permissions to delete a notification.");return deletePromise}}),angular.module("bulbsCmsApp").controller("CmsNotifyContainerCtrl",function($scope,ipCookie,moment,CmsNotificationsApi,URLify,_){var genCookieKey=function(id){return"dismissed-cms-notification-"+id},updateCmsNotificationsDisplay=function(cmsNotifications){var now=moment();$scope.cmsNotifications=_.filter(cmsNotifications,function(cmsNotification){if(!ipCookie(genCookieKey(cmsNotification.id))&&moment(cmsNotification.post_date).isBefore(now)&&moment(cmsNotification.notify_end_date).isAfter(now))return!0})};CmsNotificationsApi.getList().then(function(cmsNotifications){updateCmsNotificationsDisplay(cmsNotifications)}),$scope.dismissCmsNotification=function(cmsNotification){var cookieKey=URLify(genCookieKey(cmsNotification.id));ipCookie(cookieKey,!0,{expires:moment(cmsNotification.notify_end_date).add({days:1}).diff(moment(),"days"),path:"/cms/app"}),updateCmsNotificationsDisplay($scope.cmsNotifications)}}),angular.module("bulbsCmsApp").controller("ContenteditCtrl",function($scope,$routeParams,$http,$window,$location,$timeout,$interval,$compile,$q,$modal,$,_,CmsConfig,moment,keypress,Raven,PNotify,IfExistsElse,VersionStorageApi,ContentFactory,FirebaseApi,FirebaseArticleFactory,Login,VersionBrowserModalOpener){var listener=new keypress.Listener;listener.simple_combo("cmd s",function(e){$scope.saveArticle()}),listener.simple_combo("ctrl s",function(e){$scope.saveArticle()});var saveHTML=" Save",navbarSave=".navbar-save",getArticleCallback=function(data){$window.article=$scope.article=data,$scope.last_saved_article=angular.copy(data),FirebaseApi.$connection.onConnect(function(){$scope.firebaseConnected=!0}).onDisconnect(function(){$scope.firebaseConnected=!1}),FirebaseArticleFactory.$retrieveCurrentArticle().then(function($article){var currentUser,savePNotify,$activeUsers=$article.$activeUsers(),$versions=$article.$versions();return $versions.$loaded(function(){$versions.$watch(function(e){if("child_added"===e.event){var newVersion=_.sortBy($versions,function(version){return-version.timestamp})[0];if(currentUser&&newVersion.user.id!==currentUser.id){savePNotify&&savePNotify.remove();var msg=""+newVersion.user.displayName+" -- "+moment(newVersion.timestamp).format("MMM Do YYYY, h:mma")+"
    ";$scope.articleIsDirty&&(msg+=" You have unsaved changes that may conflict when you save."),msg+=" Open the version browser to see their latest version.",savePNotify=new PNotify({title:"Another User Saved!",text:msg,type:"error",mouse_reset:!1,hide:!1,confirm:{confirm:!0,buttons:[{text:"Open Version Browser",addClass:"btn-primary",click:function(notice){notice.mouse_reset=!1,notice.remove(),VersionBrowserModalOpener.open($scope,$scope.article)}},{addClass:"hide"}]},buttons:{closer_hover:!1,sticker:!1}})}}})}),$activeUsers.$watch(function(){$scope.activeUsers=_.chain($activeUsers).groupBy(function(user){return user.id}).map(function(group){var groupedUser=group[0];return groupedUser.count=group.length,currentUser&&groupedUser.id===currentUser.id&&(groupedUser.displayName="You"),groupedUser}).sortBy(function(user){return"You"===user.displayName?"":user.displayName}).value()}),$article.$registerCurrentUserActive().then(function(user){currentUser=user}),$article})},getContent=function(){return ContentFactory.one("content",$routeParams.id).get().then(getArticleCallback)},saveArticleErrorCbk=function(data){$(navbarSave).removeClass("btn-success").addClass("btn-danger").html(" Error"),400===status&&($scope.errors=data),$scope.saveArticleDeferred.reject()},saveArticleSuccessCbk=function(resp){VersionStorageApi.$create($scope.article,$scope.articleIsDirty),$(navbarSave).html(" Saved!"),setTimeout(function(){$(navbarSave).html(saveHTML)},2500),$window.article=$scope.article=resp,$scope.last_saved_article=angular.copy(resp),$scope.articleIsDirty=!1,$scope.articleIsNew=!1,$scope.errors=null,"new"===$routeParams.id&&$location.path("/cms/app/edit/"+$scope.article.id+"/"+$routeParams.contentType),$location.search("rating_type",null),$scope.saveArticleDeferred.resolve(resp)};$scope.PARTIALS_URL="/views/",$scope.buildContentPartialsPath=CmsConfig.buildContentPartialsPath,$scope.page="edit",$scope.contentEditGlobals={canSave:!0},$scope.$watch("article.title",function(){$window.document.title=CmsConfig.getCmsName()+" | Editing "+($scope.article&&$(""+$scope.article.title+"").text())}),$scope.saveArticleDeferred=$q.defer(),$scope.saveArticleIfDirty=function(){return $scope.articleIsDirty?$scope.saveArticle():$q.when($scope.article)},$scope.saveArticle=function(){return $scope.contentEditGlobals.saveError?$scope.saveArticleDeferred.reject():($(navbarSave).removeClass("btn-danger").addClass("btn-success").html(" Saving"),"new"===$routeParams.id?$scope.postValidationSaveArticle():ContentFactory.one("content",$routeParams.id).get().then(function(data){data.last_modified&&$scope.article.last_modified&&moment(data.last_modified)>moment($scope.article.last_modified)?($scope.saveArticleDeferred.reject(),$modal.open({templateUrl:"/views/modals/last-modified-guard-modal.html",controller:"LastmodifiedguardmodalCtrl",scope:$scope,resolve:{articleOnPage:function(){return $scope.article},articleOnServer:function(){return data}}})):$scope.postValidationSaveArticle()}).catch(saveArticleErrorCbk),$scope.saveArticleDeferred.promise)},$scope.postValidationSaveArticle=function(){"Published"!==$scope.article.status&&$scope.article.polymorphic_ctype!==CmsConfig.getSuperFeaturesType()&&($scope.article.slug=$window.URLify($scope.article.title,50));var params={};return"new"===$routeParams.id&&(params.doctype=$scope.article.polymorphic_ctype),$scope.article.save(params).then(saveArticleSuccessCbk).catch(saveArticleErrorCbk),$scope.saveArticleDeferred.promise},$scope.articleIsDirty="new"===$routeParams.id,$scope.$watch(function(){return!angular.equals($scope.article,$scope.last_saved_article)},function(isDirty){$scope.articleIsDirty=isDirty}),$scope.$watch("articleIsDirty",function(){$scope.articleIsDirty?$window.onbeforeunload=function(){return"You have unsaved changes. Do you want to continue?"}:$window.onbeforeunload=function(){}}),$scope.publishSuccessCbk=function(){return getContent()},$scope.trashSuccessCbk=function(){$timeout(function(){$window.history.back()},1500)};var initialize=function(){"new"===$routeParams.id?($scope.articleIsNew=!0,$scope.article=ContentFactory.oneUrl("content"),$scope.article.polymorphic_ctype=$routeParams.contentType):getContent()};initialize()}),angular.module("bulbsCmsApp").controller("ContentlistCtrl",function($scope,$http,$timeout,$location,$window,$q,$,CmsConfig,ContentListService){$scope.contentData=[],ContentListService.$updateContent({page:1,exclude:CmsConfig.getSuperFeaturesType()}).then(function(data){$scope.contentData=data}),$window.document.title=CmsConfig.getCmsName()+" | Content",$scope.pageNumber=$location.search().page||"1",$scope.myStuff=!1,$scope.search=$location.search().search,$scope.collapse={},$scope.goToPage=function(){ContentListService.$updateContent({page:$scope.pageNumber},!0)},$scope.publishSuccessCbk=function(data){var i;for(i=0;i<$scope.contentData.content.length&&$scope.contentData.content[i].id!==data.article.id;i++);for(var field in data.response)$scope.contentData.content[i][field]=data.response[field];return $q.when()},$scope.trashSuccessCbk=function(){$timeout(function(){ContentListService.$updateContent(),$("#confirm-trash-modal").modal("hide")},1500)},$("body").on("shown.bs.collapse",".panel-collapse",function(e){$scope.$digest()})}).directive("ngConfirmClick",[function(){return{link:function(scope,element,attr){var msg=attr.ngConfirmClick||"Are you sure?",clickAction=attr.confirmedClick;element.bind("click",function(){window.confirm(msg)&&scope.$eval(clickAction)})}}}]),angular.module("bulbsCmsApp").controller("ContentworkflowCtrl",function($scope,$http,$modal,$window,moment,VersionBrowserModalOpener,TemporaryUrlModalOpener,CmsConfig){$scope.TIMEZONE_LABEL=moment.tz(CmsConfig.getTimezoneName()).format("z"),$scope.trashContentModal=function(articleId){return $modal.open({templateUrl:"/views/modals/confirm-trash-modal.html",controller:"TrashcontentmodalCtrl",scope:$scope,resolve:{articleId:function(){return articleId}}})},$scope.pubTimeModal=function(article){return $modal.open({templateUrl:"/views/modals/publish-date-modal.html",controller:"PubtimemodalCtrl",scope:$scope,resolve:{article:function(){return article}}})},$scope.sendToEditorModal=function(article){return $modal.open({templateUrl:"/views/modals/send-to-editor-modal.html",controller:"SendtoeditormodalCtrl",scope:$scope,resolve:{article:function(){return article}}})},$scope.changelogModal=function(article){return $modal.open({templateUrl:"/views/modals/changelog-modal.html",controller:"ChangelogmodalCtrl",scope:$scope,resolve:{article:function(){return article}}})},$scope.thumbnailModal=function(article){return $modal.open({templateUrl:"/views/modals/thumbnail-modal.html",controller:"ThumbnailModalCtrl",scope:$scope,resolve:{article:function(){return article}}})},$scope.versionBrowserModal=function(article){VersionBrowserModalOpener.open($scope,article)},$scope.temporaryUrlModal=function(article){TemporaryUrlModalOpener.open($scope,article)},$scope.descriptionModal=function(article){return $modal.open({templateUrl:"/views/modals/description-modal.html",controller:"DescriptionModalCtrl",scope:$scope,size:"lg",resolve:{article:function(){return article}}})},$scope.getStatus=function(article){return article&&article.published?moment(article.published)>moment()?"scheduled":"published":"unpublished"}}),angular.module("bulbsCmsApp").controller("ContributionsCtrl",function($scope,$routeParams,$http,$window,$location,$timeout,$compile,$q,$modal,_,CmsConfig,ContributionRoleService,ContentService){function save(){angular.element("#save-btn").html(' Saving'),$scope.contributions.save($scope.contributions).then(function(contributions){angular.element("#save-btn").addClass("btn-success").removeClass("btn-danger"),angular.element("#save-btn").html(' Save'),$scope.clean=!0},function(res){angular.element("#save-btn").addClass("btn-danger").removeClass("btn-success"),angular.element("#save-btn").html(' Error')})}function add(){$scope.contributions.push({contributor:null,content:$scope.contentId,rate:{rate:0},role:null}),$scope.collapsed.push(!1)}function getRoles(){return ContributionRoleService.getList().then(function(roles){$scope.roles=roles,getContributions()})}function getContributions(){return ContentService.one($scope.contentId).all("contributions").getList().then(function(contributions){for(var i in contributions)null!==contributions[i]&&void 0!==contributions[i].role&&(contributions[i].hasOwnProperty("rate")&&"object"==typeof contributions[i].rate&&null!==contributions[i].rate&&(contributions[i].rate=contributions[i].rate.rate),"object"==typeof contributions[i].role&&(contributions[i].paymentType=contributions[i].role.payment_type,contributions[i].roleObject=contributions[i].role,contributions[i].role=contributions[i].role.id));$scope.contributions=contributions,$scope.collapsed=new Array(contributions.length),$scope.contributions.forEach(function(item,index){$scope.contributionLabels[index]=_.find($scope.roles,function(role){return role.id===item.role}).name,$scope.collapsed[index]=!0})})}function getContent(){ContentService.one($scope.contentId).get().then(function(content){$scope.content=content,$scope.article={id:content.id}})}function remove(index){$scope.contributions.splice(index,1),$scope.collapsed.splice(index,1)}function updateLabel(index){$scope.contributionLabels[index]=_.find($scope.roles,function(role){return $scope.contributions[index].roleObject=role,$scope.contributions[index].paymentType=role.payment_type,role.id===$scope.contributions[index].role}).name}$scope.NAV_LOGO=CmsConfig.getNavLogoPath(),$scope.contentId=parseInt($routeParams.id,10),$scope.paymentType="",$scope.contributions=[],$scope.contributionLabels=[],$scope.roles=[],$scope.collapsed=[],$scope.page="contributions",$scope.clean=!0,$scope.save=save,$scope.add=add,$scope.remove=remove,$scope.updateLabel=updateLabel,$scope.isFlatRate=function(contribution){return!(!contribution.hasOwnProperty("roleObject")||"Flat Rate"!==contribution.roleObject.payment_type)},$scope.isFeatureType=function(contribution){return!(!contribution.hasOwnProperty("roleObject")||"FeatureType"!==contribution.roleObject.payment_type)&&($scope.setFeatureRate(contribution),!0)},$scope.setFeatureRate=function(contribution){for(var i in contribution.roleObject.rates.feature_type){var featureTypeRate=contribution.roleObject.rates.feature_type[i];$scope.content.feature_type===featureTypeRate.feature_type&&(contribution.featureTypeRate=featureTypeRate.rate)}},$scope.isHourly=function(contribution){return!(!contribution.hasOwnProperty("roleObject")||"Hourly"!==contribution.roleObject.payment_type)},$scope.isManual=function(contribution){return!(!contribution.hasOwnProperty("roleObject")||"Manual"!==contribution.roleObject.payment_type)},$scope.getHourlyPay=function(contribution){if(contribution.roleObject)return contribution.roleObject.rate?contribution.roleObject.rate/60*(contribution.minutes_worked||0):0},$scope.$watch("contributions",function(newContributions,oldContributions){oldContributions.length>0&&($scope.clean=!1)},!0),getRoles(),getContent()}),angular.module("bulbsCmsApp").controller("DescriptionModalCtrl",function($scope,$modalInstance,article){$scope.article=article}),angular.module("bulbsCmsApp").controller("ForbiddenmodalCtrl",function($scope,detail){$scope.detail=detail}),angular.module("bulbsCmsApp").controller("LastmodifiedguardmodalCtrl",function($scope,$modalInstance,_,moment,ContentFactory,articleOnPage,articleOnServer){$scope.articleOnServer=articleOnServer,ContentFactory.all("log").getList({content:articleOnPage.id}).then(function(log){var latest=_.max(log,function(entry){return moment(entry.action_time)}),lastSavedById=latest.user;ContentFactory.one("author",lastSavedById).get().then(function(data){$scope.lastSavedBy=data})}),$scope.loadFromServer=function(){_.each($scope.articleOnServer,function(value,key){$scope.article[key]=value}),$scope.articleIsDirty=!0,$modalInstance.close()},$scope.saveAnyway=function(){$modalInstance.close(),$scope.$parent.postValidationSaveArticle()}}),angular.module("bulbsCmsApp").controller("LineitemexportmodalCtrl",function($scope,$http,CmsConfig,moment){var now=moment().tz(CmsConfig.getTimezoneName());$scope.start=moment([now.year(),now.month()]),$scope.end=moment([now.year(),now.month()+1]),$scope.apiUrl="/cms/api/v1/contributions/line-item-reporting/?format=csv",$scope.updateDownloadUrl=function(){var start_string=$scope.start.format("YYYY-MM-DD"),end_string=$scope.end.format("YYYY-MM-DD");return $scope.apiUrl+"&start="+start_string+"&end="+end_string},$scope.downloadUrl=$scope.updateDownloadUrl(),$scope.$watchCollection("[start, end]",function(){$scope.downloadUrl=$scope.updateDownloadUrl()})}),angular.module("bulbsCmsApp").controller("LoginmodalCtrl",function($scope,Login,$modalInstance,$){$scope.login=function(){var username=$("input[name='username']").val(),password=$("input[name='password']").val();Login.login(username,password).then(function(){$modalInstance.close()},function(){$modalInstance.dismiss()})}}),angular.module("bulbsCmsApp").controller("PubtimemodalCtrl",function($scope,$http,$modal,$modalInstance,$,CmsConfig,moment,Login,article,Raven){$scope.article=article,$scope.pubButton={idle:"Publish",busy:"Publishing",finished:"Published!",error:"Error!"},$scope.$watch("pickerValue",function(newVal){var pubTimeMoment=moment(newVal);$scope.datePickerValue=moment().year(pubTimeMoment.year()).month(pubTimeMoment.month()).date(pubTimeMoment.date()),$scope.timePickerValue=moment().hour(pubTimeMoment.hour()).minute(pubTimeMoment.minute())});var modelDateFormat="YYYY-MM-DDTHH:mmZ";$scope.setTimeShortcut=function(shortcut){if("now"===shortcut){var now=moment();$scope.pickerValue=now}if("midnight"===shortcut){var midnight=moment().hour(24).minute(0);$scope.pickerValue=midnight}},$scope.setDateShortcut=function(shortcut){var today=moment.tz(CmsConfig.getTimezoneName());"today"===shortcut&&($scope.datePickerValue=moment().year(today.year()).month(today.month()).date(today.date())),"tomorrow"===shortcut&&($scope.datePickerValue=moment().year(today.year()).month(today.month()).date(today.date()+1))},$scope.setPubTime=function(){if(!$scope.article.feature_type)return $modalInstance.dismiss(),void $modal.open({templateUrl:"/views/modals/pubtime-validation-modal.html"});var newDate=moment($scope.datePickerValue),newTime=moment($scope.timePickerValue),newDateTime=moment.tz(CmsConfig.getTimezoneName()).year(newDate.year()).month(newDate.month()).date(newDate.date()).hour(newTime.hour()).minute(newTime.minute()).format(modelDateFormat),data={published:newDateTime};return $http({url:"/cms/api/v1/content/"+$scope.article.id+"/publish/",method:"POST",data:data})},$scope.setPubTimeCbk=function(publish_promise){publish_promise.then(function(result){$scope.article.published=result.data.published,$scope.publishSuccessCbk&&$scope.publishSuccessCbk({article:$scope.article,response:result.data}),$modalInstance.close()}).catch(function(reason){Raven.captureMessage("Error Setting Pubtime",{extra:reason.data}),$modalInstance.dismiss()})},$scope.unpubButton={idle:"Unpublish",busy:"Unpublishing",finished:"Unpublished!",error:"Error"},$scope.unpublish=function(){return $http({url:"/cms/api/v1/content/"+$scope.article.id+"/publish/",method:"POST",data:{published:!1}})},$scope.unpublishCbk=function(unpub_promise){unpub_promise.then(function(result){$scope.publishSuccessCbk&&$scope.publishSuccessCbk({article:$scope.article,response:result.data}),$modalInstance.close()}).catch(function(reason){$scope.publishSuccessCbk&&$scope.publishSuccessCbk({article:$scope.article,response:reason.data}),$modalInstance.dismiss()})},$scope.article.published?$scope.pickerValue=moment.tz($scope.article.published,CmsConfig.getTimezoneName()):$scope.setTimeShortcut("now")}),angular.module("bulbsCmsApp").controller("ReportemailmodalCtrl",function($scope,$http,moment){var reportEmailURL="/cms/api/v1/contributor-email/",now=moment().tz("America/Chicago");$scope.monthOptions=moment.monthsShort(),$scope.reportDeadline=now.add(1,"days"),$scope.reportMonth=$scope.monthOptions[now.month()-1],$scope.reportYear=now.year(),$scope.openReportDeadline=function($event){$event.preventDefault(),$event.stopPropagation(),$scope.startReportDeadline=!0};var getReportStart=function(){return moment().month($scope.reportMonth).year($scope.reportYear).startOf("month")};$scope.sendEmail=function(){var data={deadline:$scope.reportDeadline, +start:getReportStart()};$http.post(reportEmailURL,data)}}),angular.module("bulbsCmsApp").controller("ReportingCtrl",function($http,$scope,$modal,$window,$,$location,$filter,$interpolate,Login,moment,CmsConfig,ContributionReportingService,ContentReportingService,FreelancePayReportingService){function loadReport(report,start,end,order,apiUrl){if($scope.items=[],"undefined"==typeof apiUrl?$scope.apiURL=report.downloadURL:$scope.apiURL=apiUrl,$scope.apiURL+="?page="+$scope.reportParams.pageNumber,$scope.downloadURL=report.downloadURL+"?format=csv",end){var endParam=$filter("date")(end,"yyyy-MM-dd");$scope.reportParams.end=endParam,$scope.apiURL+="&end="+endParam,$scope.downloadURL+="&end="+endParam}if(start){var startParam=$filter("date")(start,"yyyy-MM-dd");$scope.reportParams.start=startParam,$scope.apiURL+="&start="+startParam,$scope.downloadURL+="&start="+startParam}if(order&&($scope.apiURL+="&ordering="+order.key),$scope.publishedFilter&&($scope.apiURL+="&published="+$scope.publishedFilter,$scope.downloadURL+="&published="+$scope.publishedFilter,$scope.reportParams.published=$scope.publishedFilter),$scope.userFilter&&($scope.apiURL+="&staff="+$scope.userFilter,$scope.downloadURL+="&staff="+$scope.userFilter,$scope.reportParams.staff=$scope.userFilter),$scope.moreFilters)for(var key in $scope.moreFilters)"authors"===$scope.moreFilters[key].type?($scope.apiURL+="&contributors="+$scope.moreFilters[key].query,$scope.downloadURL+="&contributors="+$scope.moreFilters[key].query,$scope.reportParams.contributors=$scope.moreFilters[key].query):($scope.apiURL+="&"+$scope.moreFilters[key].type+"="+$scope.moreFilters[key].query,$scope.downloadURL+="&"+$scope.moreFilters[key].type+"="+$scope.moreFilters[key].query,$scope.reportParams[$scope.moreFilters[key].type]=$scope.moreFilters[key].query);$http({method:"GET",url:$scope.apiURL}).then(function(data){$scope.items=[],$scope.pageTotal=data.data.count,data.data.results.forEach(function(lineItem){var item=[];report.headings.forEach(function(heading){var exp=$interpolate("{{item."+heading.expression+"}}"),value=exp({item:lineItem});item.push(value)}),$scope.items.push(item)})})}$window.document.title=CmsConfig.getCmsName()+" | Reporting",$scope.userFilter="",$scope.userFilters=[{name:"All",value:""},{name:"Staff",value:"staff"},{name:"Freelance",value:"freelance"}],$scope.publishedFilter="",$scope.publishedFilters=[{name:"All Content",value:""},{name:"Published",value:"published"}],$scope.reports={Contributions:{service:ContributionReportingService,headings:[{title:"Content ID",expression:"content.id"},{title:"Headline",expression:"content.title"},{title:"FeatureType",expression:"content.feature_type"},{title:"Contributor",expression:"user.payroll_name"},{title:"Role",expression:"role"},{title:"Pay",expression:"pay"},{title:"Date",expression:"content.published | date: 'MM/dd/yyyy'"}],downloadURL:"/cms/api/v1/contributions/reporting/",orderOptions:[{label:"Order by User",key:"user"},{label:"Order by Content",key:"content"}]},Content:{service:ContentReportingService,headings:[{title:"Content ID",expression:"id"},{title:"Headline",expression:"title"},{title:"Feature Type",expression:"feature_type"},{title:"Video",expression:"video_id"},{title:"Article Cost",expression:"value"},{title:"Date Published",expression:"published | date: 'MM/dd/yyyy'"}],orderOptions:[],downloadURL:"/cms/api/v1/contributions/contentreporting/"},"Freelance Pay":{service:FreelancePayReportingService,headings:[{title:"Contributor",expression:"contributor.full_name"},{title:"Contribution #",expression:"contributions_count"},{title:"Pay",expression:"pay"},{title:"Payment Date",expression:"payment_date | date: 'MM/dd/yyyy'"}],orderOptions:[],downloadURL:"/cms/api/v1/contributions/freelancereporting/"}},$scope.items=[],$scope.headings=[],$scope.orderOptions=[],$scope.startInitial=moment().startOf("month").format("YYYY-MM-DD"),$scope.endInitial=moment().endOf("month").format("YYYY-MM-DD"),$scope.reportParams={pageNumber:1,start:$scope.startInitial,end:$scope.endInitial},$scope.reportDisabled=!0,$scope.pageTotal=null,$scope.moreFilters=[],$scope.startOpen=!1,$scope.endOpen=!1,$scope.setReport=function($reportingService){$scope.reportDisabled===!0&&($scope.reportDisabled=!1),$scope.reportParams.pageNumber=1,$scope.report=$reportingService},$scope.setUserFilter=function(value){$scope.userFilter=value,loadReport($scope.report,$scope.reportParams.start,$scope.reportParams.end,$scope.orderBy)},$scope.setPublishedFilter=function(value){$scope.publishedFilter=value,"published"===value&&($scope.reportParams.end=moment().format("YYYY-MM-DD")),loadReport($scope.report,$scope.reportParams.start,$scope.reportParams.end,$scope.orderBy)},$scope.openStart=function($event){$event.preventDefault(),$event.stopPropagation(),$scope.startOpen=!0},$scope.openEnd=function($event){$event.preventDefault(),$event.stopPropagation(),$scope.endOpen=!0},$scope.orderingChange=function(){loadReport($scope.report,$scope.reportParams.start,$scope.reportParams.end,$scope.orderBy)},$scope.downloadIsValid=function(){return"undefined"!==$scope.report},$scope.$watch("report",function(report){report&&($scope.orderOptions=report.orderOptions,report.orderOptions.length>0?$scope.orderBy=report.orderOptions[0]:$scope.orderBy=null,$scope.headings=[],report.headings.forEach(function(heading){$scope.headings.push(heading.title)}),loadReport(report,$scope.reportParams.start,$scope.reportParams.end,$scope.orderBy))}),$scope.$watchCollection("[reportParams.start, reportParams.end]",function(params){if($scope.report){var start=params[0],end=params[1];loadReport($scope.report,start,end,$scope.orderBy)}}),$scope.reportEmailModal=function(){return $modal.open({templateUrl:"/views/modals/report-email-modal.html",controller:"ReportemailmodalCtrl"})},$scope.goToPage=function(){loadReport($scope.report,$scope.reportParams.start,$scope.reportParams.end)}}),angular.module("bulbsCmsApp").controller("TargetingCtrl",function($scope,$http,$window,$q,$location,CmsConfig){$window.document.title=CmsConfig.getCmsName()+" | Targeting Editor";var canceller;$scope.search=function(url){url&&("undefined"==typeof canceller?canceller=$q.defer():(canceller.resolve(),canceller=$q.defer()),$http({method:"GET",url:"/ads/targeting",timeout:canceller.promise,params:{url:$scope.url}}).success(function(data){$scope.targetingArray=[];for(var k in data)$scope.targetingArray.push([k,data[k]])}).error(function(data,status,headers,config){404===status&&($scope.targetingArray=[],$scope.targetingArray.push(["",""]))}))},$scope.save=function(){var data={};for(var i in $scope.targetingArray)data[$scope.targetingArray[i][0]]=$scope.targetingArray[i][1];return $http({method:"POST",url:"/ads/targeting?url="+$scope.url,data:data}).success(function(data){$scope.targetingArray=[];for(var k in data)$scope.targetingArray.push([k,data[k]])})},$scope.keyHandler=function(event,url){13===event.keyCode?this.search(url):27===event.keyCode&&(event.currentTarget.value="")};var search=$location.search();search&&search.url&&($scope.url=decodeURIComponent(search.url))}),angular.module("bulbsCmsApp").value("ARTICLE_TEMPORARY_URL_DAYS_VALID",7).controller("TemporaryUrlModalCtrl",function($scope,$routeParams,CmsConfig,ContentFactory,ARTICLE_TEMPORARY_URL_DAYS_VALID,_,moment){var content=ContentFactory.one("content",$routeParams.id);$scope.TEMP_LINK_DAYS_VALID=ARTICLE_TEMPORARY_URL_DAYS_VALID,$scope.TEMP_URL_BASE=CmsConfig.buildUnpublishedUrl(),$scope.tokens=[],content.getList("list_tokens").then(function(tokenList){$scope.tokens=tokenList;var expiredIndicies=[];_.each($scope.tokens,function(token,i){token.create_date=moment(token.create_date),token.expire_date=moment(token.expire_date),moment().isAfter(token.expire_date)?expiredIndicies.push(i):token.daysTillExpire=token.expire_date.diff(moment(),"days")+1});for(var i=expiredIndicies.length-1;i>=0;i--)$scope.tokens.splice(expiredIndicies[i],1)}),$scope.createToken=function(){var now=moment();ContentFactory.one("content",$routeParams.id).post("create_token",{create_date:now,expire_date:now.clone().add({days:ARTICLE_TEMPORARY_URL_DAYS_VALID})}).then(function(token){token.create_date=moment(token.create_date),token.expire_date=moment(token.expire_date),token.daysTillExpire=token.expire_date.diff(moment(),"days")+1,$scope.tokens.push(token),$scope.newestToken=token})}}),angular.module("bulbsCmsApp").controller("ThumbnailModalCtrl",function($scope,BettyCropper,$modalInstance,article){$scope.article=article,$scope.selectCustomThumbnail=function(){BettyCropper.upload().then(function(success){$scope.article.thumbnail_override=success},function(error){console.log(error)},function(progress){console.log(progress)})}}),angular.module("bulbsCmsApp").controller("TrashcontentmodalCtrl",function($scope,$http,$modalInstance,$,Login,articleId,Raven){$scope.deleteButton={idle:"Delete",busy:"Trashing",finished:"Trashed",error:"Error!"},$scope.trashContent=function(){return $http({method:"POST",url:"/cms/api/v1/content/"+articleId+"/trash/"})},$scope.trashCbk=function(trash_promise){trash_promise.then(function(result){console.log("trash success"),$scope.trashSuccessCbk(),$modalInstance.close()}).catch(function(reason){return 404===reason.status?($scope.trashSuccessCbk(),void $modalInstance.close()):(Raven.captureMessage("Error Deleting Article",{extra:reason}),void $modalInstance.dismiss())})}}),angular.module("bulbsCmsApp").controller("UnpublishCtrl",function($scope,$http,$q){$scope.unpubButton={idle:"Unpublish",busy:"Unpublishing",finished:"Unpublished!",error:"Error"},$scope.unpublish=function(){return $http({url:"/cms/api/v1/content/"+$scope.article.id+"/publish/",method:"POST",data:{published:!1}})},$scope.unpublishCbk=function(unpub_promise){unpub_promise.then(function(result){$scope.publishSuccessCbk&&$scope.publishSuccessCbk({article:$scope.article,response:result.data})}).catch(function(reason){$scope.publishSuccessCbk&&$scope.publishSuccessCbk({article:$scope.article,response:reason.data})})}}),angular.module("bulbsCmsApp").controller("VersionBrowserModalCtrl",function($scope,$modalInstance,_,moment,VersionStorageApi,FirebaseApi,CmsConfig){FirebaseApi.$authorize().then(function(){$scope.maxVersions=CmsConfig.getFirebaseMaxArticleHistory()}),VersionStorageApi.$all().then(function(versions){$scope.versions=_.chain(versions).each(function(version){version.timestamp_display=moment(version.timestamp).format("MMM Do YYYY, h:mma")}).sortBy(function(version){return-version.timestamp}).value(),$scope.selectedVersion=$scope.versions[0],$scope.setPreview=function(version){$scope.selectedVersion=version},$scope.restoreSelected=function(){_.each($scope.selectedVersion.content,function(value,key){$scope.article[key]=value}),$scope.articleIsDirty=!0,$modalInstance.close()}})}),angular.module("bulbsCmsApp").controller("VideothumbnailmodalCtrl",function($scope,$http,$modalInstance,CmsConfig,Zencoder,videoId){function compilePosterUrl(thumbnail){return CmsConfig.buildVideoThumbnailUrl(videoId,"thumbnail_"+pad4(thumbnail)+".png")}function pad4(num){var s="0000"+num;return s.substr(s.length-4)}var DEFAULT_THUMBNAIL=4,MAX_THUMBNAIL=19;$scope.uploadedImage={id:null},$scope.mode="still",Zencoder.getVideo(videoId).then(function(response){$scope.video=response.data,"In Progress"===response.data.status?($scope.inProgress=!0,$scope.video.poster=$scope.video.poster||null):$scope.video.poster=$scope.video.poster||compilePosterUrl(DEFAULT_THUMBNAIL)}),$scope.$watch("video.poster",function(){if($scope.video&&$scope.video.poster){var defaultUrl=CmsConfig.buildVideoThumbnailUrl(videoId,"thumbnail_{{thumbnail}}.png"),thumbnailIndex=defaultUrl.indexOf("{{thumbnail}}");0===$scope.video.poster.indexOf(defaultUrl.substr(0,thumbnailIndex))?($scope.currentThumbnail=Number($scope.video.poster.substr(thumbnailIndex,4)),$scope.uploadedImage.id=null):$scope.currentThumbnail=!1}}),$scope.$watch("uploadedImage.id",function(){$scope.uploadedImage.id&&($scope.video.poster=CmsConfig.buildImageApiUrl("16x9",$scope.uploadedImage.id,"1200.jpg"))}),$scope.nextThumb=function(){$scope.video.poster=compilePosterUrl($scope.currentThumbnail0?$scope.currentThumbnail-1:MAX_THUMBNAIL)},$scope.defaultThumb=function(){$scope.video.poster=compilePosterUrl(DEFAULT_THUMBNAIL)},$scope.setPoster=function(){Zencoder.setVideo($scope.video),$modalInstance.close($scope.video.poster)},$scope.reencode=function(){Zencoder.encode(videoId)}}),angular.module("bulbsCmsApp").factory("AdApi",function(Restangular,adApiConfig){return Restangular.withConfig(function(RestangularConfigurer){RestangularConfigurer.setBaseUrl(adApiConfig.baseUrl)})}).constant("adApiConfig",{baseUrl:"/ads"}),angular.module("bulbsCmsApp").factory("BadRequestInterceptor",function($q,$injector){return{responseError:function(rejection){return $injector.invoke(function($modal){if(400===rejection.status){var detail=rejection.data||{something:["Something was wrong with your request."]};$modal.open({templateUrl:"/views/modals/400-modal.html",controller:"BadrequestmodalCtrl",resolve:{detail:function(){return detail}}})}}),$q.reject(rejection)}}}),angular.module("bulbsCmsApp").factory("BugReportInterceptor",function($q,$window,PNotify){return{responseError:function(rejection){if(rejection.status>=500){var stack={animation:!0,dir1:"up",dir2:"left"};new PNotify({title:"You found a bug!",text:"Looks like something just went wrong, and we need your help to fix it! Report it, and we'll make sure it never happens again.",type:"error",confirm:{confirm:!0,align:"left",buttons:[{text:"Report Bug",addClass:"btn-danger pnotify-report-bug",click:function(notice){notice.remove(),$window.showBugReportModal()}},{addClass:"hidden"}]},buttons:{sticker:!1},icon:"fa fa-bug pnotify-error-icon",addclass:"stack-bottomright",stack:stack})}return $q.reject(rejection)}}}),angular.module("bulbsCmsApp").config(function(RestangularProvider,bulbsApiConfig){RestangularProvider.setResponseExtractor(function(response,operation,what,url){var newResponse=response;return"getList"===operation&&"undefined"!=typeof response.results&&(newResponse=response.results,newResponse.metadata={count:response.count,next:response.next,previous:response.previous}),newResponse}),bulbsApiConfig.requestSuffix&&RestangularProvider.setRequestSuffix(bulbsApiConfig.requestSuffix)}).constant("bulbsApiConfig",{requestSuffix:"/"}),angular.module("bulbsCmsApp").factory("CmsNotificationsApi",function($q,ContentFactory){return ContentFactory.service("cms_notifications")}),angular.module("bulbsCmsApp").factory("FirebaseApi",function(FirebaseRefFactory,$firebase,$rootScope,$q,CurrentUserApi,CmsConfig){var rootRef=FirebaseRefFactory.newRef(CmsConfig.buildFirebaseSiteUrl()),authDefer=$q.defer(),$authorize=authDefer.promise;$authorize.catch(function(error){error&&console.error("Firebase login failed:",error)}),CurrentUserApi.getCurrentUserWithCache().then(function(user){"firebase_token"in user&&user.firebase_token?rootRef.auth(user.firebase_token,function(error){error?authDefer.reject(error):authDefer.resolve(rootRef)}):authDefer.reject()});var connectedRef=FirebaseRefFactory.newRef(CmsConfig.buildFirebaseUrl(".info/connected"));connectedRef.on("value",function(connected){connected.val()?$rootScope.$emit("firebase-reconnected"):$rootScope.$emit("firebase-disconnected"),$rootScope.$emit("firebase-connection-state-changed")});var $connection={onConnect:function(callback){return $rootScope.$on("firebase-reconnected",callback),$connection},onDisconnect:function(callback){return $rootScope.$on("firebase-disconnected",callback),$connection},onChange:function(callback){$rootScope.$on("firebase-connection-state-changed",callback)}};return{$authorize:function(){return $authorize},$connection:$connection}}),angular.module("bulbsCmsApp").factory("FirebaseArticleFactory",function($q,$firebase,$routeParams,_,moment,FirebaseApi,CurrentUserApi,CmsConfig){var createArticle=function(rootRef,articleId){var articleRef=rootRef.child("articles/"+articleId),$activeUsers=$firebase(articleRef.child("users")).$asArray(),$versions=$firebase(articleRef.child("versions")).$asArray(),addCurrentUserToActiveUsers=function(){var registeredDeferred=$q.defer(),registeredPromise=registeredDeferred.promise;return CurrentUserApi.getCurrentUserWithCache().then(function(user){var simplifiedUser=_.pick(user,["id","displayName"]);$activeUsers.$add(simplifiedUser).then(function(userRef){userRef.onDisconnect().remove(),registeredDeferred.resolve(simplifiedUser)}).catch(function(error){registeredDeferred.reject(error)})}).catch(function(error){registeredDeferred.reject(error)}),registeredPromise},registerCurrentUserActive=function(){return FirebaseApi.$connection.onConnect(addCurrentUserToActiveUsers),addCurrentUserToActiveUsers()};return{ref:articleRef,$activeUsers:function(){return $activeUsers},$versions:function(){return $versions},$registerCurrentUserActive:registerCurrentUserActive,$createVersion:function(articleData){var createDefer=$q.defer(),$createPromise=createDefer.promise;return CurrentUserApi.getCurrentUserWithCache().then(function(user){var simplifiedUser=_.pick(user,["id","displayName"]),numVersions=$versions.length;numVersions+1>CmsConfig.getFirebaseMaxArticleHistory()&&_.chain($versions).sortBy(function(version){return version.timestamp}).every(function(version){return $versions.$remove(version),numVersions--,numVersions+1>CmsConfig.getFirebaseMaxArticleHistory()});var versionData={timestamp:moment().valueOf(),user:simplifiedUser,content:articleData};$versions.$add(versionData).then(createDefer.resolve).catch(createDefer.reject)}),$createPromise}}};return{$retrieveArticle:function(articleId){var retrieveDeferred=$q.defer(),retrievePromise=retrieveDeferred.promise;return FirebaseApi.$authorize().then(function(rootRef){retrieveDeferred.resolve(createArticle(rootRef,articleId))}).catch(function(error){retrieveDeferred.reject(error)}),retrievePromise},$retrieveCurrentArticle:function(){return this.$retrieveArticle($routeParams.id)}}}),angular.module("bulbsCmsApp").service("FirebaseRefFactory",function(Firebase){return{newRef:function(url){return new Firebase(url)}}}),angular.module("bulbsCmsApp").service("Gettemplate",function($templateCache,$q,$http){this.get=function(templateUrl){var template=$templateCache.get(templateUrl);if(template)return $q.when(template);var deferred=$q.defer();return $http.get(templateUrl,{cache:!0}).success(function(html){$templateCache.put(templateUrl,html),deferred.resolve(html)}),deferred.promise}}),angular.module("bulbsCmsApp").service("IfExistsElse",function($window,$http){this.ifExistsElse=function(restQ,propertiesToValues,existsCbk,elseCbk,errorCbk){restQ.then(function(data){for(var resList=data.results||data,j=0;j0)$modal.open({templateUrl:"/views/modals/login-modal.html",controller:"LoginmodalCtrl"});else{var detail=rejection.data&&rejection.data.detail||"Forbidden";$modal.open({templateUrl:"/views/modals/403-modal.html",controller:"ForbiddenmodalCtrl",resolve:{detail:function(){return detail}}})}}),$q.reject(rejection))}}}),angular.module("bulbsCmsApp").factory("TemporaryUrlModalOpener",function($modal){var modal=null;return{open:function($scope,article){return modal&&modal.close(),modal=$modal.open({templateUrl:"/views/modals/temporary-url-modal.html",controller:"TemporaryUrlModalCtrl",scope:$scope,resolve:{article:function(){return article}}})}}}),angular.module("bulbsCmsApp").factory("VersionStorageApi",function($q,FirebaseApi,FirebaseArticleFactory,LocalStorageBackup,_){var firebaseAvailableDefer=$q.defer(),$firebaseAvailable=firebaseAvailableDefer.promise;FirebaseApi.$authorize().then(function(){firebaseAvailableDefer.resolve(FirebaseArticleFactory.$retrieveCurrentArticle())}).catch(function(){firebaseAvailableDefer.reject()});var _omitter=_.memoize(function(value,key){return _.isFunction(value)||_.find(key,function(c){return"."===c||"#"===c||"$"===c||"/"===c||"["===c||"]"===c})},function(value,key){return[key,value]}),_deepScrub=function(obj){var clone,transValue;return _.isUndefined(obj)?clone=null:_.isPlainObject(obj)?(clone={},_.forOwn(obj,function(value,key){transValue=_deepScrub(value),_omitter(transValue,key)||(clone[key]=transValue)})):_.isArray(obj)?(clone=[],_.each(obj,function(value,key){transValue=_deepScrub(value),_omitter(transValue,key)||clone.push(transValue)})):clone=obj,clone},scrubArticle=function(articleData){return _deepScrub(articleData)};return{$create:function(rawArticleData,articleIsDirty){var articleData=scrubArticle(rawArticleData),createDefer=$q.defer(),createPromise=createDefer.promise;return $firebaseAvailable.then(function($currentArticle){articleIsDirty||$currentArticle.$versions().length<1?$currentArticle.$createVersion(articleData).then(function(versionData){createDefer.resolve(versionData)}).catch(function(){createDefer.reject()}):createDefer.reject()}).catch(function(){LocalStorageBackup.$versions().then(function(versions){articleIsDirty||versions.length<1?LocalStorageBackup.$create(articleData).then(function(versionData){createDefer.resolve(versionData)}).catch(function(error){createDefer.reject(error)}):createDefer.reject()})}),createPromise},$all:function(){var allDefer=$q.defer(),allPromise=allDefer.promise;return $firebaseAvailable.then(function($currentArticle){$currentArticle.$versions().$loaded(function(versions){allDefer.resolve(versions)})}).catch(function(){LocalStorageBackup.$versions().then(function(versions){allDefer.resolve(versions)}).catch(function(error){allDefer.reject(error)})}),allPromise.then(function(versions){return _.sortBy(versions,function(version){return-version.timestamp})})}}}),angular.module("bulbsCmsApp").factory("VersionBrowserModalOpener",function($modal){var modal=null;return{open:function($scope,article){return modal&&modal.close(),modal=$modal.open({templateUrl:"/views/modals/version-browser-modal.html",controller:"VersionBrowserModalCtrl",scope:$scope,size:"lg",resolve:{article:function(){return article}}})}}}),angular.module("bulbsCmsApp").service("Zencoder",function($http,$q,$modal,$){function getNewVideoUploadCredentials(file){var data={name:file.name};data=$.param(data);var newVideoDeferred=$q.defer();return $http({method:"POST",url:newVideoUrl,data:data,headers:{"Content-Type":"application/x-www-form-urlencoded"}}).success(function(data){newVideoDeferred.resolve({file:file,attrs:data})}).error(function(data){newVideoDeferred.reject(data)}),newVideoDeferred.promise}function uploadToS3(videoObject){var s3deferred=$q.defer(),formData=new FormData;return formData.append("key",videoObject.attrs.key),formData.append("AWSAccessKeyId",videoObject.attrs.AWSAccessKeyId),formData.append("acl",videoObject.attrs.acl),formData.append("success_action_status",videoObject.attrs.success_action_status),formData.append("policy",videoObject.attrs.policy),formData.append("signature",videoObject.attrs.signature),formData.append("file",videoObject.file),$.ajax(videoObject.attrs.upload_endpoint,{processData:!1,contentType:!1,data:formData,type:"POST",xhr:function(){var req=$.ajaxSettings.xhr();return req&&req.upload.addEventListener("progress",function(e){var percent=e.loaded/e.total*100;s3deferred.notify(percent)},!1),req},success:function(data){s3deferred.resolve(videoObject)},error:function(data){s3deferred.reject(data)}}),s3deferred.promise}function encode(videoObject){var encodeDeferred=$q.defer();return $http({method:"POST",url:"/video/"+videoObject.attrs.id+"/encode"}).success(function(data){videoObject.attrs.encode_status_endpoints=data,_encodingVideos[videoObject.attrs.id]=videoObject.attrs,encodeDeferred.resolve(videoObject)}).error(function(data){encodeDeferred.reject(data)}),encodeDeferred.promise}var newVideoUrl="/video/new",fileInputId="#bulbs-cms-hidden-video-file-input",inputTemplate='';this.onVideoFileUpload=function(){var clickDeferred=$q.defer();angular.element(fileInputId).remove();var file,fileInput=angular.element(inputTemplate);return angular.element("body").append(fileInput),fileInput.click(),fileInput.unbind("change"),fileInput.bind("change",function(elem){0!==this.files.length?(file=this.files[0],file.size>1073741824&&clickDeferred.reject("Upload file cannot be larger than 1024MB."),0!==file.type.indexOf("video/")&&clickDeferred.reject("You must upload a video file.")):clickDeferred.reject("Please select a file."),getNewVideoUploadCredentials(file).then(uploadToS3).then(encode,angular.noop,function(uploadPercentComplete){clickDeferred.notify(uploadPercentComplete)}).then(function(videoObject){clickDeferred.resolve(videoObject)},function(error){clickDeferred.reject(error)})}),clickDeferred.promise},this.encode=function(videoId){encode({attrs:{id:videoId}})},this.openVideoThumbnailModal=function(videoId){return $modal.open({templateUrl:"/views/modals/video-thumbnail-modal.html",controller:"VideothumbnailmodalCtrl",resolve:{videoId:function(){return videoId}}})},this.getVideo=function(videoId){var url="/video/"+videoId;return $http({method:"GET",url:url})},this.setVideo=function(video){var url="/video/"+video.id,data=$.param(video);return $http({method:"POST",url:url,data:data,headers:{"Content-Type":"application/x-www-form-urlencoded"}})};var _encodingVideos={};this.encodingVideos=_encodingVideos}),angular.module("bulbsCmsApp").filter("truncateByCharacters",function(){return function(input,chars,breakOnWord){if(isNaN(chars))return input;if(chars<=0)return"";if(input&&input.length>=chars){if(input=input.substring(0,chars),breakOnWord)for(;" "===input.charAt(input.length-1);)input=input.substr(0,input.length-1);else{var lastspace=input.lastIndexOf(" ");lastspace!==-1&&(input=input.substr(0,lastspace))}return 1===chars?input+".":input+"..."}return input}}),angular.module("bulbsCmsApp").filter("truncateByWords",function(){return function(input,words){if(isNaN(words))return input;if(words<=0)return"";if(input){var inputWords=input.split(/\s+/);inputWords.length>words&&(input=inputWords.slice(0,words).join(" ")+"...")}return input}}),angular.module("bulbsCmsApp").filter("tzDate",function(dateFilter,moment,CmsConfig){return function(input,format){if(!input)return"";var inDate=moment.tz(input,CmsConfig.getTimezoneName()),newdate=inDate.format("YYYY-MM-DDTHH:mm"),formattedDate=dateFilter(newdate,format);return format.toLowerCase().indexOf("h")>-1&&(formattedDate+=" "+inDate.format("z")),formattedDate}}); +//# sourceMappingURL=scripts.min.js.1477511784778.map \ No newline at end of file diff --git a/dist/scripts/scripts.min.js.1477511784778.map b/dist/scripts/scripts.min.js.1477511784778.map new file mode 100644 index 000000000..73004ce6c --- /dev/null +++ b/dist/scripts/scripts.min.js.1477511784778.map @@ -0,0 +1 @@ +{"version":3,"sources":["scripts.js"],"names":["w","computeStyle","element","image","selection","scale","selector","id","elementWidth","$","width","elementHeight","height","s_width","x1","x0","s_height","y1","y0","tmp_selection","imageUrl","BC_ADMIN_URL","rules","background-image","background-size","scaleNumber","background-position","background-repeat","createStyle","classname","styleNode","document","createElement","type","className","css","temp","rule","styleSheet","cssText","appendChild","createTextNode","find","append","num","by_scale","Math","floor","computeAspectRatio","_w","_h","aspectRatio","ceil","crop","picturefill","ps","getAttribute","getElementsByTagName","i","il","length","el","div","offsetWidth","offsetHeight","format","remove","ajax","url","headers","X-Betty-Api-Key","BC_API_KEY","Content-Type","undefined","success","proxy","res","imageData","this","padding-bottom","cropDetails","selections","error","background-color","angular","module","constant","window","_","URLify","moment","PNotify","keypress","Raven","OnionEditor","config","$locationProvider","$routeProvider","$sceProvider","html5Mode","when","templateUrl","controller","reloadOnSearch","otherwise","redirectTo","enabled","$provide","$httpProvider","decorator","$delegate","exception","cause","captureException","interceptors","push","run","$rootScope","$http","$cookies","defaults","common","csrftoken","deleteHeaders","delete","factory","Restangular","setBaseUrl","setRequestSuffix","extendModel","obj","extend","getFullName","first_name","last_name","all","extendAuthor","author","contributor","authors","extendCollection","collection","save","data","post","then","contributions","restangularizeCollection","service","withConfig","RestangularConfigurer","user","toString","full_name","username","content","title","published","BettyCropperService","$interpolate","$q","CmsConfig","BettyImage","upload","uploadImageDeferred","defer","fileInputId","fileInput","inputTemplate","click","unbind","bind","e","target","files","reject","file","indexOf","size","FormData","method","buildImageApiUrl","getImageApiKey","X-CSRFToken","transformRequest","identity","transformResponse","headersGetter","parseJSON","resolve","promise","get","detailPatch","name","credit","updateSelection","ratio","detail","BettyImageFactory","Selection","prototype","scaleToFit","fitRatio","thisRatio","scaled","round","getStyles","scaledSelection","position","exp","idStr","segmentedId","substr","base_url","source","SelectionFactory","scaleBy","scaledToFit","value","directive","$scope","BULBS_AUTOCOMPLETE_EVENT_KEYPRESS","AUTOCOMPLETE_BASIC_DEBOUNCE","writables","searchTerm","autocompleteItems","$getItems","searchFunction","map","item","displayFormatter","updateAutocomplete","debounce","results","delayClearAutocomplete","delay","clearAutocomplete","$digest","clearSelectionOverlay","showSelectionOverlay","updateNgModel","onSelect","handleKeypress","$event","keyCode","isEmpty","$broadcast","handleSelect","link","scope","iElement","iAttrs","ngModelCtrl","defaultFormatter","context","valueFormatter","viewValue","itemValueFormatter","modelValue","itemDisplayFormatter","$formatters","$render","selectedValue","$viewValue","$parsers","unbindInitialValue","$watch","initialValue","newValue","isUndefined","copy","$setViewValue","require","restrict","hideSearchIcon","inputId","inputPlaceholder","ngDisabled","buildComponentPath","BettyCropper","openImageCropModal","addStyles","editable","hideMetas","placeholderText","onChange","$element","isDefined","caption","alt","bettyImage","console","log","progress","edit","response","setStyles","attrs","imageStyling","parent","ratioWidth","parseInt","split","ratioHeight","newImage","oldImage","equals","resize","removeImage","editImage","$sce","linksList","renderValue","trustAsHtml","isFunction","$window","$timeout","report","reportButton","idle","busy","finished","modalVisible","showThankYou","showModal","dismissModal","sendToWebtech","firstRes","secondRes","thirdRes","location","href","user_agent","navigator","userAgent","sendToWebtechCbk","entry","showBugReportModal","$apply","TunicConfig","uuid4","campaign","isObject","searchCampaigns","buildBackendApiUrl","params","search","uuid","generate","ngModel","isNumber","$modelValue","result","$commitViewValue","label","$location","$routeParams","Campaign","model","$build","isNew","$find","onbeforeunload","$dirty","needsSave","$on","addPixel","pixel","campaign_type","pixels","deletePixel","without","saveModel","$save","$asPromise","path","deferred","getModelId","PIXEL_TYPES","getCmsName","routeId","template","$injector","CmsConfigProvider","invoke","$get","modelFactory","ConfirmationModal","modalBody","modalCancelText","modalOkText","modalOnCancel","modalOnOk","modalTitle","modalInstance","addClass","on","$modal","open","$modalInstance","confirm","$close","cancel","$dismiss","ContentFactory","searchContent","getList","one","autocompleteOnSelect","currentUser","pendingGetCurrentUserRequest","parsePayload","payload","cloneDeep","displayName","email","getCurrentUser","buildApiUrlRoot","finally","getCurrentUserWithCache","logout","filter","provider","CustomSearchConfigError","BulbsCmsConfigError","build","checkOrError","test","errorMsg","conditionFieldMappings","conditionTypes","timePeriodMappings","requestCapMs","addConditionField","endpoint","valueStructureName","valueStructureValue","isString","value_structure","addConditionType","addTimePeriod","setRequestCapMs","getConditionFields","getConditionTypes","getRequestCapMs","getTimePeriods","controllerService","onUpdate","CustomSearchService","customSearchService","resetFilters","setPage","setQuery","addedFilterOn","removedFilterOn","$conditionalContentRetrieve","$filterContentByIncluded","$filterContentByExcluded","$retrieveContent","$contentRetrieve","CustomSearchConfig","fieldTypes","groupsConditionsGet","groupIndex","conditionIndex","field","items","groupsGet","timePeriods","$update","$groupsUpdateResultCountFor","CustomSearch","defaultData","groups","includedIds","excludedIds","pinnedIds","$page","$query","_data","_$getContent","queryData","self","contentQuery","page","query","assign","preview","index","$retrieveGroupCount","count","$result_count","groupsResultCountGet","groupsList","groupsAdd","conditions","time","groupsRemove","splice","groupsClear","groupsConditionsAdd","values","groupsConditionsList","groupsConditionsRemove","groupsTimePeriodSet","groupsTimePeriodGet","groupsTimePeriodRemove","groupsConditionsValuesAdd","matches","existingValue","groupsConditionsValuesClear","groupsConditionsValuesList","groupsConditionsValuesRemove","valueIndex","includesList","includesAdd","uniq","excludesRemove","includesRemove","includesHas","includes","excludesList","excludesAdd","pinsRemove","excludesHas","pinsList","pinsAdd","pinsHas","getPage","getQuery","isInvalidDateValue","dateValue","isDate","isMoment","date","getDateTimeFormatHumanReadable","tz","getTimezoneName","nowInTimezone","dateInTimezone","clone","TIMEZONE_LABEL","dateTime","modDatetime","toDate","newTime","year","dayOfYear","hours","minutes","newDate","setDate","selectedDate","isDateTimeInvalid","isValid","setDateToday","today","setDateTomorrow","tomorrow","add","setTimeNow","now","setTimeMidnight","midnightTonight","startOf","clearDatetime","close","chooseDatetime","modalClearText","modalOnBeforeClose","modalOnClear","modalOnClose","retrieveSchema","schema","attr","ctrls","formField","$validators","rgbhex","clearDate","formCtrl","form","integer","FIELD_TYPES_META","Utils","itemOrderingMemory","redoOrdering","v","newItem","readOnly","moveItem","fromIndex","toIndex","moveTo","removeItem","removeFrom","elements","$compile","$form","hideLabel","onValidityChange","internalForm","has","fieldKeys","Object","keys","fields","includeOnlyProvided","isArray","includeOnly","hideOnlyChildLabel","intersection","forEach","fieldSchema","fieldMeta","object","invalid","tagName","html","max_length","input","array","boolean","color","datetime","richtext","string","DynamicContentFormError","BulbsCmsError","DynamicContentApi","schemaSrc","validityCallback","catch","errorMessage","event","doPrevent","d","srcElement","toUpperCase","disabled","isContentEditable","preventDefault","Zencoder","safeApply","fn","$$phase","$root","editor","getEditor","replace","read","getContent","defaultValue","formatting","options","role","multiline","placeholder","text","container","domain","linkDomain","searchHandler","linkSearchHandler","statsContainer","inlineObjects","getInlineObjecsPath","insertDialog","editDialog","video","onVideoFileUpload","openVideoThumbnailModal","videoEmbedUrl","buildVideoUrl","hide","setContent","setTimeout","setChangeHandler","$isEmpty","scribe","allowsBlockElements","arguments","updateQueryData","included_ids","excluded_ids","pinned_ids","updateConditionData","article","filters","getAutocompletes","filterInputTimeout","filterInputCounter","autocompleteArray","q","arrowSelect","direction","$toSelect","$entries","$selected","first","prev","next","last","selectEntry","applyFilterChange","filterObject","$input","trigger","getFilterObjects","filterParamsToTypes","tags","feature_types","filterParam","filterType","getQueryToLabelMappings","filterInputValue","queryToLabelMappings","val","triggerHandler","addFilter","clearSearch","clearFilters","removeClass","fadeOut","fadeIn","newFilterValue","contains","deleteFilter","key","toDelete","toSplice","ContentListService","$updateContent","filterObjects","STATES","DONE","PROGRESS","ERROR","doClick","state","clickFunction","disableWhen","cssBtnClassComplete","cssBtnClassError","cssBtnClassProgress","cssIconComplete","textError","textProgress","textComplete","pre","ratios","selectedCrop","cropMode","thumb_container_styles","scaleData","Jcrop","allowSelect","allowMove","allowResize","keySupport","jcrop_api","image_url","setThumbStyles","selectCrop","setOptions","setSelect","thumb_styles","padding-top","padding-left","padding-right","jcrop_selection","tellSelect","newSelection","x","x2","y","y2","saveAndQuit","saveAndNext","nextRatioIndex","backdrop","liveBlogEndpoint","liveBlogEntryEndpoint","created","updated","cleanData","chain","omit","createEntry","updateEntry","put","deleteEntry","getEntries","param","liveblog","getLiveBlogAuthorSelectorDirectiveName","oldValue","CurrentUserApi","LiveBlogApi","reportError","message","captureMessage","titleDisplay","headline","clearError","entries","panelOpen","isPanelOpen","togglePanel","collapseAll","expandAll","entryForm","wrapperForm","makeEntryFormName","getEntryForm","isEntryFormSaveDisabled","transactionsLocked","$pristine","lock","buildLock","isLocked","addEntry","created_by","updated_by","unshift","saveEntry","oldUpdateBy","oldUpdated","$setPristine","publishAndSave","oldDate","sort","entry1","entry2","entry1IsMoment","entry2IsMoment","isBefore","NOTIFICATIONS_LIST_REL_PATH","Notification","LIST_URL","buildExternalUrl","modelId","Answer","Poll","getPoll","answers","embedCode","validatePublication","endDate","end_date","publishedField","pollForm","endDateField","$setValidity","comesAfterPublishedValid","postPoll","answerPromise","answer","postAnswer","updatePollAnswers","deletedAnswers","updatePoll","newId","addAnswer","notOnSodahead","times","removeAnswer","answerId","deletedAnswer","a","PromotedContentService","pzoneData","getData","moveUp","moveContentUp","moveDown","moveContentDn","$removeContentFromPZone","completeAction","$completeContentAction","stopAction","stopContentAction","markDirty","markDirtySelectedPZone","disableControls","isPZoneRefreshPending","sortableOptions","beforeStop","ui","helper","change","scrollTop","containment","distance","opacity","start","stop","scheduleDateFrom","scheduleDateTo","deleteStatus","isError","$ready","aggregatedOperations","operations","concat","unsavedOperations","removeOperation","operation","$removeOperation","err","clearDeleteStatus","setPreviewTime","operationsStale","isPZoneOperationsStale","refreshingOperations","refreshOperations","$refreshOperations","from","toISOString","to","operationTime","compTime","whenAsMoment","previewTime","groupDateFormat","aggregator","tempAggregate","sortBy","groupBy","pairs","pair","$watchCollection","selectedPZoneName","selectedPZone","changePZone","$refreshPZones","$selectPZone","savePZone","$saveSelectedPZone","clearOperations","clearUnsavedOperations","$refreshSelectedPZone","pageNumber","goToPage","$refreshAllContent","contentIsEnabled","notAlreadyInList","immediateDraggable","isAfter","futureDraggable","beginInsert","beginContentInsert","beginReplace","beginContentReplace","tools","openToolsFor","doOpen","closeTools","toolsOpenFor","_serviceData","allContent","actionContent","action","pzones","setupDefer","readableOperationTypes","INSERT","DELETE","REPLACE","operationTypeToReadable","promotion_insertoperation","promotion_deleteoperation","promotion_replaceoperation","readableToOperationType","reduce","pzoneRefreshPending","pzoneOperationsStale","each","pzone","saved","markSavedSelectedPZone","makeOperationsStale","client_id","apply","$addOperation","props","isPreviewTimeImmediate","isPreviewTimePast","lastId","max","nextId","allProps","type_name","cleanType","applied","content_title","restangularizeElement","findIndex","status","contentId","indexFrom","indexTo","ret","splicer","duplicateIndex","setPreviewTimeToImmediate","contentData","LineItem","LineItemExportModal","utilityButtons","buttonClasses","iconClasses","FeatureType","RateOverride","resourceUrl","$promise","hasOwnProperty","getPaymentType","roleId","roles","payment_type","isFeatureRated","isHourlyRated","isFlatRated","addFeatureType","featureTypes","featureType","rate","getFeatureTypes","extra","getRoles","searchFeatureTypes","simpleSearch","Role","PAYMENT_TYPES","$then","feature_type_rates","$fetch","flat_rates","hourly_rates","rateEditable","paymentTypes","slice","paymentType","getDirtyRates","dirty","flat_rate","hourly_rate","saveDirtyRates","dirtyRates","SECTIONS_LIST_REL_PATH","Section","dateTimeFormatFilter","sendToEditor","notes","SendToEditorApi","modalArticle","modalOnBeforeOpen","statuses","getArticleEditoralStatuses","setupAndOpenModal","note","SPECIAL_COVERAGE_LIST_REL_PATH","SpecialCoverage","ACTIVE_STATES","tunicCampaignIdMapping","$loadTunicCampaign","previewLinkModal","tunicCampaignFormatter","campaignId","number","$searchCampaigns","campaigns","_statuses","setStatuses","getStatuses","StatusFilterOptions","getValue","option","isActive","possibleKeys","pluck","searchKeys","filterByStatus","superFeatureEndpoint","contentEndpoint","createSuperFeature","doctype","getSuperFeaturesType","deleteSuperFeature","sorts","superFeature","cellContent","isSameOrAfter","getSuperFeature","getSuperFeatures","getSuperFeatureRelations","namePlural","updateSuperFeature","updateSuperFeatureRelationsOrdering","relations","remappedRelations","relation","pick","updateAllRelationPublishDates","SuperFeaturesApi","breadcrumbs","addParentToBreadcrumb","maxRecircItems","fullRecircContents","retrieveContent","includeRecirc","recirc","recirc_query","newRecircIdsLength","removeRecirc","StatusFilterOptionsProvider","editPageUrlBuilder","polymorphic_ctype","statusFilterOptions","newRelations","oldRelations","normalizeOrderings","ordering","relationFormPrefix","orderingFormPrefix","makeRelationFormName","makeOrderingFormName","getRelationForm","getOrderingForm","isAtLeastOneRelationFormDirty","isDirty","startsWith","reorder","funcArgs","moveRelation","addRelation","superfeature_type","default_child_type","updateRelationsPublishDates","saveRelation","relationCopy","deleteRelation","requestSuperFeature","Video","superFeatures","addSuperFeature","addSuperFeatureCallback","searchSuperFeature","TEMPLATE_TYPES","templateTypes","content_type","slug","modalBodyBefore","modalBodyAfter","logoHref","itemsDropdownTitle","itemsDropdown","itemsTop","saveFunction","saveDisableWhen","NAV_LOGO","getNavLogoPath","TopBarItem","displayText","displayIconClasses","containerClasses","$parse","restmod","parserWrap","parsed","record","getOrdering","mixin","define","fieldDisplays","getProperty","fieldDisplay","evaluate","API_URL_ROOT","restmodProvider","rebase","$config","style","urlPrefix","mix","plural","primaryKey","init","encode","start_date","decode","startDate","$extend","Model","$search","CustomSearchSettings","Count","countEndpoint","$retrieveResultCount","$create","$response","root","CustomSearchCount","CustomSearchGroupCount","searchEndpoint","jsonRootSingle","$hooks","before-save","_req","groupCountEndpoint","notificationEndpoint","created_on","createdOn","deleteAnswers","deletePromise","answerUrl","putAnswer","oldAnswers","newAnswer","oldAnswer","answer_text","pollId","poll","Error","$filter","cleanPayload","originalPayload","momentToDateString","question_text","pollUrl","getPolls","deletePoll","contributorEndpoint","paymentDate","payment_date","rateOverrideEndpoint","feature_type","before-render","roleEndpoint","hasMany","hooks","after-fetch-many","$owner","$metadata","sectionEndpoint","promoted","after-fetch","$refreshResultCount","Record","$resultCount","INACTIVE","PROMOTED","belongsTo","prefetch","listUrl","mask","videos","belongsToMany","super_features","active","$loadVideosData","after-save","tunicCampaignId","addVideo","added","existingVideo","existingSuperFeature","existingSuperFeatureId","$activeState","activeState","ACTIVE","CustomSearchConfigProvider","ngClipProvider","setContentPartialsPath","setComponentPath","setDateTimeFormatHumanReadable","setDirectivePartialsPath","setSharedPath","setTopBarMapping","setUnpublishedPath","setPath","CmsConfigError","pathBuilder","join","apiUrlRoot","articleEditoralStatuses","autoAddAuthor","componentPath","contentPartialsPath","dateTimeFormatHumanReadable","directivePartialsPath","cmsName","contentApiUrl","externalUrl","firebaseMaxArticleHistory","firebaseSiteRoot","firebaseUrl","imageApiUrl","imageApiKey","inlineObjectsPath","internalUrl","liveBlogAuthorSelectorDirectiveName","navLogoPath","sharedPath","superFeaturesType","timezoneName","topBarMappings","unpublishedPath","videoPath","videoThumbnailUrl","addArticleEditoralStatus","setApiUrlRoot","setAutoAddAuthor","isBoolean","setCmsName","setContentApiUrl","setExternalUrl","setFirebaseMaxArticleHistory","setFirebaseSiteRoot","setFirebaseUrl","setImageApiUrl","setImageApiKey","setInlineObjectsPath","setInternalUrl","setLiveBlogAuthorSelectorDirectiveName","setNavLogoPath","setSuperFeaturesType","setTimezoneName","zone","setVideoPath","setVideoThumbnailUrl","buildContentApiUrl","buildContentPartialsPath","buildDirectivePartialsPath","buildFirebaseSiteUrl","buildFirebaseUrl","buildInternalUrl","buildSharedPath","buildUnpublishedUrl","buildVideoThumbnailUrl","getAutoAddAuthor","getFirebaseMaxArticleHistory","getTopBarMapping","captureStackTrace","constructor","stack","create","call","totalItems","updateFilters","addFilters","merge","updateParams","metadata","okCopy","okCopyButton","buttonClassesDefault","buttonClassesSuccess","buttonText","dateStr","m","Date","momentObj","pathJoin","$fieldDisplays","$list","$collection","orderingFilter","searchFilter","toggledFilters","copyContentInContext","toolCopyContent","$retrieve","addParams","loadingResults","allParams","$refresh","searchParameter","filterButtonsParsed","filterButtons","$toggleFilters","sortingField","sortDirection","$sort","$add","cmsEditPageUrl","$remove","$destroy","goToEditPage","activeFilterButton","findWhere","tap","button","showAddButton","getItems","fieldName","cellContents","removedItem","destroyItem","modelFields","modelName","modelNamePlural","apiPath","backendRoot","requestToken","setApiPath","setBackendRoot","setRequestToken","getRequestToken","relUrl","shouldBeIntercepted","urlTest","request","Authorization","slugify","toLowerCase","list","allowOutOfBounds","modIndexTo","sep","RegExp","argsArr","flattenDeep","protocolPrefix","match","locked","wrapper","func","VIDEOHUB_DEFAULT_CHANNEL","videoChannel","addVideoCallback","searchVideos","$postSearch","channel","attrName","hideCaption","addAnImage","uploadImage","onSuccess","onError","alert","onProgress","userNameDisplayFilter","inputLabelText","inputLabelTextSub","labelSub","display","objects","o","t","parents","newauthors","Login","autocomplete_dropdown_template","inputTimeout","inputCounter","autocomplete_list","$elem","dropdown","top","outerHeight","left","minWidth","outerWidth","curDownSelect","curDownSelectNext","curSelect","curSelectNext","selected","onClick","freeForm","cmsNotification","override","fullName","IfExistsElse","saveArticle","contentType","resp","new_id","new_path","rating_type","origin","gotSave","gotTags","gotUser","newArticle","newTitle","tag","ifExistsElse","types","panel","contentTypeLabel","hasClass","focus","pP","tN","cO","$interval","updateEncodeStatuses","updateEncodeStatus","videoid","encodingVideos","encode_status_endpoints","json","job_status","src","idRegex","exec","getVideo","fVal","ft","hideElement","optionsUrl","noPermissionIntercept","$templateCache","Gettemplate","$evalAsync","is","visible","loaded","compiledEl","tElement","tAttrs","view","$observe","imageId","pictureFillElement","show","roleValue","roleOptions","Number","getPromise","saveCbk","colors","colors_tmp","confirmClickWith","prop","save_promise","saveSuccess","reason","where","tagVal","newtags","slides","loadingClass","openImageDrawer","removeLoadingGif","after","complete","new","addTargetingRow","targetingArray","removeTargetingRow","abortUpload","setProgress","req","abort","abortEncode","job_id","videoAttrs","zencoderApiKey","fakeInput","progressEl","lastProgress","abs","progressBar","updateEncodeProgress","api_key","useXDomain","video_id","embedUrl","trustAsUrl","initVideoWidget","uploadVideo","clearVideo","areYouSure","modal","encoding","initialCheckRan","videoUrl","videoId","removeVideo","noop","uploadProgress","thumbnailModal","$animate","transclude","pIndex","select","selectItem","setIndex","labelAttr","$attrs","searchParams","searchParam","menuScope","timeoutId","appendMenu","isMenuAppended","enter","menuEl","styleMenu","reset","leave","offset","getComputedStyle","right","isNaN","inputEl","openMenu","removeAttr","$new","label-attr","which","users","changelog","userIds","unique","valid","postDateValid","postDate","notifyEndDate","notifyEndDateValid","titleValid","cmsNotificationValid","post_date","days","notify_end_date","cmsNotificationDirty","saveCmsNotification","$parent","userIsSuperuser","$saveCmsNotification","newCmsNotification","deleteCmsNotification","$deleteCmsNotification","CmsNotificationsApi","is_superuser","cmsNotifications","removeIndicies","saveDefer","savePromise","updatedCmsNotification","deleteDefer","removeFromList","ipCookie","genCookieKey","updateCmsNotificationsDisplay","dismissCmsNotification","cookieKey","expires","diff","VersionStorageApi","FirebaseApi","FirebaseArticleFactory","VersionBrowserModalOpener","listener","Listener","simple_combo","saveHTML","navbarSave","getArticleCallback","last_saved_article","$connection","onConnect","firebaseConnected","onDisconnect","$retrieveCurrentArticle","$article","savePNotify","$activeUsers","$versions","$loaded","newVersion","version","timestamp","msg","articleIsDirty","mouse_reset","buttons","notice","closer_hover","sticker","activeUsers","group","groupedUser","$registerCurrentUserActive","saveArticleErrorCbk","errors","saveArticleDeferred","saveArticleSuccessCbk","articleIsNew","PARTIALS_URL","contentEditGlobals","canSave","saveArticleIfDirty","saveError","postValidationSaveArticle","last_modified","articleOnPage","articleOnServer","publishSuccessCbk","trashSuccessCbk","history","back","initialize","oneUrl","exclude","myStuff","collapse","ngConfirmClick","clickAction","confirmedClick","$eval","TemporaryUrlModalOpener","trashContentModal","articleId","pubTimeModal","sendToEditorModal","changelogModal","versionBrowserModal","temporaryUrlModal","descriptionModal","getStatus","ContributionRoleService","ContentService","clean","collapsed","getContributions","roleObject","Array","contributionLabels","updateLabel","isFlatRate","contribution","isFeatureType","setFeatureRate","rates","featureTypeRate","isHourly","isManual","getHourlyPay","minutes_worked","newContributions","oldContributions","latest","action_time","lastSavedById","lastSavedBy","loadFromServer","saveAnyway","month","end","apiUrl","updateDownloadUrl","start_string","end_string","downloadUrl","login","password","dismiss","pubButton","newVal","pubTimeMoment","datePickerValue","timePickerValue","hour","minute","modelDateFormat","setTimeShortcut","shortcut","pickerValue","midnight","setDateShortcut","setPubTime","newDateTime","setPubTimeCbk","publish_promise","unpubButton","unpublish","unpublishCbk","unpub_promise","reportEmailURL","monthOptions","monthsShort","reportDeadline","reportMonth","reportYear","openReportDeadline","stopPropagation","startReportDeadline","getReportStart","sendEmail","deadline","ContributionReportingService","ContentReportingService","FreelancePayReportingService","loadReport","order","apiURL","downloadURL","reportParams","endParam","startParam","publishedFilter","userFilter","staff","moreFilters","contributors","pageTotal","lineItem","headings","heading","expression","userFilters","publishedFilters","reports","Contributions","orderOptions","Content","Freelance Pay","startInitial","endInitial","endOf","reportDisabled","startOpen","endOpen","setReport","$reportingService","setUserFilter","orderBy","setPublishedFilter","openStart","openEnd","orderingChange","downloadIsValid","reportEmailModal","canceller","timeout","k","keyHandler","currentTarget","decodeURIComponent","ARTICLE_TEMPORARY_URL_DAYS_VALID","TEMP_LINK_DAYS_VALID","TEMP_URL_BASE","tokens","tokenList","expiredIndicies","token","create_date","expire_date","daysTillExpire","createToken","newestToken","selectCustomThumbnail","thumbnail_override","deleteButton","trashContent","trashCbk","trash_promise","$authorize","maxVersions","$all","versions","timestamp_display","selectedVersion","setPreview","restoreSelected","compilePosterUrl","thumbnail","pad4","s","DEFAULT_THUMBNAIL","MAX_THUMBNAIL","uploadedImage","mode","inProgress","poster","defaultUrl","thumbnailIndex","currentThumbnail","nextThumb","prevThumb","defaultThumb","setPoster","setVideo","reencode","adApiConfig","baseUrl","responseError","rejection","something","animation","dir1","dir2","align","icon","addclass","RestangularProvider","bulbsApiConfig","setResponseExtractor","what","newResponse","previous","requestSuffix","FirebaseRefFactory","$firebase","rootRef","newRef","authDefer","firebase_token","auth","connectedRef","connected","$emit","callback","createArticle","articleRef","child","$asArray","addCurrentUserToActiveUsers","registeredDeferred","registeredPromise","simplifiedUser","userRef","registerCurrentUserActive","ref","$createVersion","articleData","createDefer","$createPromise","numVersions","every","versionData","valueOf","$retrieveArticle","retrieveDeferred","retrievePromise","Firebase","cache","restQ","propertiesToValues","existsCbk","elseCbk","errorCbk","resList","j","allFieldsMatch","datum","property","keyPrefixArticle","keyPrefix","createPromise","localStorage","setItem","JSON","stringify","keySplit","pickForRemoval","yesterday","subtract","keyTime","retrieveDefer","stored","parse","newCsrf","oldCsrf","jqueryCsrfSetup","LocalStorageBackup","firebaseAvailableDefer","$firebaseAvailable","_omitter","memoize","c","_deepScrub","transValue","isPlainObject","forOwn","scrubArticle","rawArticleData","$currentArticle","allDefer","allPromise","getNewVideoUploadCredentials","newVideoDeferred","newVideoUrl","uploadToS3","videoObject","s3deferred","formData","AWSAccessKeyId","acl","success_action_status","policy","signature","upload_endpoint","processData","xhr","ajaxSettings","addEventListener","percent","total","notify","encodeDeferred","_encodingVideos","clickDeferred","elem","uploadPercentComplete","chars","breakOnWord","substring","charAt","lastspace","lastIndexOf","words","inputWords","dateFilter","inDate","newdate","formattedDate"],"mappings":"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","file":"scripts.min.js"} \ No newline at end of file diff --git a/dist/scripts/templates.js b/dist/scripts/templates.js new file mode 100644 index 000000000..f6e39a33a --- /dev/null +++ b/dist/scripts/templates.js @@ -0,0 +1,7 @@ +angular.module("bulbsCmsApp").run(["$templateCache",function($templateCache){"use strict";$templateCache.put("/views/autocomplete.html",'
    '),$templateCache.put("/views/bettyimage.html","
    "),$templateCache.put("/views/cms-notification.html",'

    '),$templateCache.put("/views/cms-notifications.html",'

    CMS Alerts

    No CMS notifications yet, click "New CMS Alert" to add one!
    No CMS notifications yet, check back soon!
    '),$templateCache.put("/views/cms-notify-container.html",'
    New Update: {{ cmsNotification.title }} Learn More Dismiss
    '),$templateCache.put("/views/content-list-detail-panel.html",'
    Status
    {{ article.published | dateTimeFormat:"ddd[, ]M/D/YY[ at ]h:mm a z" }} Draft Scheduled for {{ article.published | dateTimeFormat:"ddd[, ]M/D/YY[ at ]h:mm a z" }}
    Authors
    {{ author.first_name }} {{ author.last_name }},
    Feature type
    {{ article.feature_type }}
    Tags
    {{ tag.name | truncateByWords:3 }},
    Article ID
    {{ article.id }}
    '),$templateCache.put("/views/contentedit.html",'
    {{field}}
    {{error}}

    Error: article has no content type.

    '),$templateCache.put("/views/contentlist.html",'

    No content here

    {{ article.feature_type }}
    By {{ article.authors[0].first_name | truncateByCharacters: 1 }} {{ article.authors[0].last_name }} and {{ article.authors.length - 1}} others · {{ article.published | dateTimeFormat:"ddd[, ]M/D/YY[ at ]h:mm a z" }} Draft Scheduled for {{ article.published | dateTimeFormat:"ddd[, ]M/D/YY[ at ]h:mm a z" }}
    '),$templateCache.put("/views/contributions.html",'
    {{ contribution.contributor.getFullName() }} 
    $ {{ contribution.roleObject.rates.flat_rate.rate }}
    $
    Force Payment Submission

    $ {{ contribution.featureTypeRate }}
    $
    Force Payment Submission

    Min.
    ${{ getHourlyPay(contribution) }}
    Force Payment Submission

    Force Payment Submission

    '),$templateCache.put("/views/create-content.html",''),$templateCache.put("/views/devicepreview.html",'
    No image selected.
    '),$templateCache.put("/views/encode-status.html",'
    • Video {{video.id}}
      {{video.job_status.progress | number:0}}%
      0%
    '),$templateCache.put("/views/logged-in-user.html",''),$templateCache.put("/views/mainvideo.html",'
    '), +$templateCache.put("/views/modals/400-modal.html",''),$templateCache.put("/views/modals/403-modal.html",''),$templateCache.put("/views/modals/changelog-modal.html",''),$templateCache.put("/views/modals/confirm-trash-modal.html",''),$templateCache.put("/views/modals/description-modal.html",''),$templateCache.put("/views/modals/last-modified-guard-modal.html",''),$templateCache.put("/views/modals/line-item-export-modal.html",''),$templateCache.put("/views/modals/login-modal.html",''),$templateCache.put("/views/modals/preview-link-modal.html",''),$templateCache.put("/views/modals/publish-date-modal.html",''),$templateCache.put("/views/modals/pubtime-validation-modal.html",''),$templateCache.put("/views/modals/report-email-modal.html",''),$templateCache.put("/views/modals/temporary-url-modal.html",''),$templateCache.put("/views/modals/thumbnail-modal.html",''),$templateCache.put("/views/modals/version-browser-modal.html",''),$templateCache.put("/views/modals/video-thumbnail-modal.html",''),$templateCache.put("/views/nav.html",''),$templateCache.put("/views/pzones.html",'

    Promotion Zone Editor

    '),$templateCache.put("/views/pzones/carousel.html","
    Looks like this P'Zone is outdated
    "),$templateCache.put("/views/pzones/feature_type.html","
    Coming soon!
    "),$templateCache.put("/views/pzones/list.html",'
    Content IDActions
    '),$templateCache.put("/views/pzones/oneup.html","
    Looks like this P'Zone is outdated
    "),$templateCache.put("/views/pzones/reviews.html",'

    The "reviews" promoted zone has no available options.

    '),$templateCache.put("/views/pzones/twoup.html","
    Looks like this P'Zone is outdated
    "),$templateCache.put("/views/reportbar.html",''),$templateCache.put("/views/reporting.html",'
    '),$templateCache.put("/views/rolefield.html",''),$templateCache.put("/views/save-button.html",""),$templateCache.put("/views/taglike-autocomplete-field.html",'
    • {{ display(object) }}
    '),$templateCache.put("/views/targeting-editor.html",'

    Targeting Editor

    '),$templateCache.put("/views/targeting.html",'
    KeyValue
    '),$templateCache.put("/views/textlike-autocomplete-field.html",'

    {{model}}
    '),$templateCache.put("/views/toolbar.html",''), +$templateCache.put("/views/video-field.html",'
    60% Complete
    '),$templateCache.put("/components/autocomplete-basic/autocomplete-basic.html",'
    {{ selectedValue }}
    No results found
    '),$templateCache.put("/components/betty-editable/betty-editable.html",'
    '),$templateCache.put("/components/breadcrumb/breadcrumb.html",' No links to display in breadcrumb! '),$templateCache.put("/components/bug-reporter/bug-reporter-button.html",'
    '),$templateCache.put("/components/campaign-autocomplete/campaign-autocomplete.html",''),$templateCache.put("/components/campaigns/campaigns-edit/campaigns-edit-sponsor-pixel/campaigns-edit-sponsor-pixel.html",'
    '),$templateCache.put("/components/campaigns/campaigns-edit/campaigns-edit.html",'

    Campaigns > New Campaign {{ model.campaignLabel }}

    Sponsor Pixels
    '),$templateCache.put("/components/campaigns/campaigns-list/campaigns-list-page.html","
    "),$templateCache.put("/components/confirmation-modal/confirmation-modal.html",''),$templateCache.put("/components/content-search/content-search.html",''),$templateCache.put("/components/custom-search/custom-search-content-item/custom-search-content-item.html",'
    Article ID: {{ model.id }} | {{ model.feature_type }}
    {{ model.title }}
    Published
    {{ model.published | date : \'M/d/yyyy, h:mm a\' }}
    '),$templateCache.put("/components/custom-search/custom-search-group/custom-search-group-condition/custom-search-group-condition.html",'
    {{ value.name }}
    '),$templateCache.put("/components/custom-search/custom-search-group/custom-search-group.html",'
    Total Results from this Group: {{ controllerService.groupsResultCountGet(groupIndex) }}
    '),$templateCache.put("/components/custom-search/custom-search.html",'

    Current List Based on Above Parameters Total Results in List: {{ customSearchService.content.count }}
    '),$templateCache.put("/components/date-time-modal/date-time-modal.html",''),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-error.html","{{ errorMessage }}"),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-boolean/dynamic-content-form-field-boolean.html",''),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-color/dynamic-content-form-field-color.html",' '),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-date-time/dynamic-content-form-field-date-time.html",'
    {{ ngModel[name] | dateTimeFormat }}
    '),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-image/dynamic-content-form-field-image.html",'
    '),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-input-errors/dynamic-content-form-field-input-errors.html",'
    • {{ schema.label }} must be an integer. {{ schema.label }} must be less than {{ schema.max_value }} {{ schema.label }} is {{ form[name].$viewValue.length - schema.max_length }} characters too long! {{ schema.label }} must be greater than {{ schema.min_value }}. {{ schema.label }} is required! {{ schema.label }} must be formatted as an rgb hex. eg: "#000000".
    '),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-input-label/dynamic-content-form-field-input-label.html",''),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-integer/dynamic-content-form-field-integer.html",''),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-invalid/dynamic-content-form-field-invalid.html",' "{{ schema.label || name }}" has an invalid field type "{{ schema.type }}"!'),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-list/dynamic-content-form-field-list.html",'
    '),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-object/dynamic-content-form-field-object.html",'
    Schema is malformed
    '),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-richtext/dynamic-content-form-field-richtext.html",'
    '),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-field/dynamic-content-form-field-text/dynamic-content-form-field-text.html",''),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-loaded.html",""),$templateCache.put("/components/dynamic-content/dynamic-content-form/dynamic-content-form-loading.html",' Loading dynamic content schema...'),$templateCache.put("/components/editor/editor-wrapper/editor-wrapper.html",'
    '), +$templateCache.put("/components/editors-pick/editors-pick.html","

    Editors Pick

    "),$templateCache.put("/components/evergreen-field/evergreen-field.html",'
    '),$templateCache.put("/components/filter-list-widget/filter-list-widget.html",'
    {{queryToLabelMappings[f.query]}}
    Clear filters
    "),$templateCache.put("/components/filter-widget/filter-widget.html",'
    {{queryToLabelMappings[f.query]}}
    Clear filters
    "),$templateCache.put("/components/generic-ajax-button/generic-ajax-button.html","'),$templateCache.put("/components/hide-from-rss-field/hide-from-rss-field.html",'
    '),$templateCache.put("/components/image-crop-modal/image-crop-modal.html",'
    '),$templateCache.put("/components/live-blog/live-blog-entries/live-blog-entries.html",'

    Entries

    {{ errorMessage }}
    No entries yet!
    1. Created by {{ entry.created_by | userNameDisplay }} @ {{ entry.created | dateTimeFormat }}. Last Updated by {{ entry.updated_by | userNameDisplay }} @ {{ entry.updated | dateTimeFormat }}.
    '),$templateCache.put("/components/notifications/notifications-edit/notifications-edit.html",'

    Notifications > New Notification {{ model.internalTitle }}

    '),$templateCache.put("/components/notifications/notifications-list/notifications-list-page.html","
    "),$templateCache.put("/components/polls/polls-edit/polls-edit.html",'

    Responses

    '),$templateCache.put("/components/polls/polls-list/polls-list-page.html","
    "),$templateCache.put("/components/promoted-content/promoted-content-article/promoted-content-article.html",'

    · No type set
    Not published

    '),$templateCache.put("/components/promoted-content/promoted-content-list/promoted-content-list.html",'

    Content List
    on {{ pzoneData.previewTime.toDate() | date: \'MMM d, yyyy @ h:mma\' }} Now

    Viewing a past list, past lists cannot be modified.
    Select an item below to finish your {{ pzoneData.action }} operation.
    {{ pzoneData.selectedPZone.zone_length - pzoneData.selectedPZone.content.length }} spots left
    • All {{ pzoneData.selectedPZone.zone_length }} spots filled, content below will not display
      {{ pzoneData.selectedPZone.zone_length - pzoneData.selectedPZone.content.length }} spots left
    '),$templateCache.put("/components/promoted-content/promoted-content-operations-list/promoted-content-operations-list.html",'

    Schedule {{ pzoneData.selectedPZone.name }}

    From: to:
    {{ deleteStatus.message }} Dismiss
    • {{ group[0].format(groupDateFormat) }} ({{ group[1].length }} Events)
      • {{ operation.cleanType }} @ position {{operation.index + 1}}
      Loading operations...
      '),$templateCache.put("/components/promoted-content/promoted-content-pzone-select/promoted-content-pzone-select.html",'
      Loading {{ selectedPZoneName }}...
      '),$templateCache.put("/components/promoted-content/promoted-content-save/promoted-content-save.html",'
      {{ pzoneData.unsavedOperations.length }} Unsaved Operation Operations'),$templateCache.put("/components/promoted-content/promoted-content-search/promoted-content-search.html",'

      Search Content

      '),$templateCache.put("/components/promoted-content/promoted-content-time-picker/promoted-content-time-picker.html",'
      '),$templateCache.put("/components/promoted-content/promoted-content.html",'

      Promoted Content

      '), +$templateCache.put("/components/reporting/reporting-line-items-edit/reporting-line-items-edit.html",'

      Line Items > New Line Item Edit Line Item

      '),$templateCache.put("/components/reporting/reporting-line-items-list/reporting-line-items-list.html",'
      '),$templateCache.put("/components/reporting/reporting-rate-overrides-edit/reporting-rate-overrides-edit.html",'

      Edit Rate Override

      $
      '),$templateCache.put("/components/reporting/reporting-rate-overrides-list/reporting-rate-overrides-list.html",'
      '),$templateCache.put("/components/reporting/reporting-roles-edit/reporting-roles-edit.html",'

      Roles > New Role {{ model.name }}

      Feature TypesRates
      {{ rate.featureType }}
      '),$templateCache.put("/components/reporting/reporting-roles-list/reporting-roles-list.html",'
      '),$templateCache.put("/components/sections/sections-edit/sections-edit.html",'

      Sections > New Section {{ model.name }}

      {{ LIST_URL }}
      '),$templateCache.put("/components/sections/sections-list/sections-list-page.html",'
      '),$templateCache.put("/components/send-to-editor-modal/send-to-editor-modal.html",'
      '),$templateCache.put("/components/special-coverage/special-coverage-edit/special-coverage-edit.html",'

      Special Coverage Lists > New Special Coverage {{ model.name }}

      {{ LIST_URL }}
      3x1 Image, 900px wide

      Content List

      Video Playlist

      Super Feature List

      '),$templateCache.put("/components/special-coverage/special-coverage-list/special-coverage-list-page.html","
      "),$templateCache.put("/components/static-image/static-image.html",'
      No image selected.'),$templateCache.put("/components/status-filter/status-filter.html",''),$templateCache.put("/components/super-features/super-features-edit/super-features-edit.html",'
      • ({{ content.id }}) {{ content.title }}
      Please save article to see dynamic content tools!
      '),$templateCache.put("/components/super-features/super-features-list/super-features-list.html",'
      '),$templateCache.put("/components/super-features/super-features-relations/super-features-relations.html",'

      Child Pages

      {{ errorMessage }}
      No child pages yet!
      1. {{ relation.published | dateTimeFormat }} Status: {{ relation.status }} EDIT
      '),$templateCache.put("/components/super-features/super-features-tab/super-features-tab-item/super-features-tab-item.html","
      Super Feature ID: {{ superFeature.id }}
      {{ superFeature.title }}
      Published
      {{ superFeature.published | dateTimeFormat }}
      "),$templateCache.put("/components/super-features/super-features-tab/super-features-tab.html",'
      Current List Items in List: {{ superFeatures.length }}
      '),$templateCache.put("/components/template-type-field/template-type-field.html",'
      '),$templateCache.put("/components/title-modal/title-modal.html",'
      '),$templateCache.put("/components/top-bar/top-bar-base.html",''),$templateCache.put("/shared/copy-button/copy-button.html",' '),$templateCache.put("/shared/list-page/list-page.html",'

      {{ namePlural }}

      {{ field.title }}
      No {{ namePlural }}, click "Add {{ name }}" below to add the first {{ name }}!
      '), +$templateCache.put("/shared/non-restmod-list-page/non-restmod-list-page.html",'

      {{ modelNamePlural() }}

      {{ field.title }}
      No {{ modelNamePlural() }}, click "Add {{ modelName() }}" below to add the first {{ modelName() }}!
      '),$templateCache.put("/shared/video-list/video-list-video/video-list-video.html","
      Video ID: {{ model.id }} | {{ model.channel.name }}
      {{ model.title }}
      Published
      {{ (model.published | date_string_to_moment).format('M/D/YYYY, h:mma') }}
      "),$templateCache.put("/shared/video-list/video-list.html",'
      Current List Items in List: {{ videos.length }}
      There are no videos in this list, search for a video to add to playlist.
      ')}]); +//# sourceMappingURL=templates.js.1477511784472.map \ No newline at end of file diff --git a/dist/scripts/templates.js.1477511784472.map b/dist/scripts/templates.js.1477511784472.map new file mode 100644 index 000000000..526746891 --- /dev/null +++ b/dist/scripts/templates.js.1477511784472.map @@ -0,0 +1 @@ +{"version":3,"sources":["../../.tmp/concat/scripts/templates.js"],"names":["angular","module","run","$templateCache","put"],"mappings":"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","file":"templates.js"} \ No newline at end of file diff --git a/dist/scripts/vendor.js b/dist/scripts/vendor.js new file mode 100644 index 000000000..a09ecc0fb --- /dev/null +++ b/dist/scripts/vendor.js @@ -0,0 +1,118300 @@ +/*! + * jQuery JavaScript Library v2.1.4 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-04-28T16:01Z + */ + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +// + +var arr = []; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + // Use the correct document accordingly with window argument (sandbox) + document = window.document, + + version = "2.1.4", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: arr.sort, + splice: arr.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // Skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // Extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray, + + isWindow: function( obj ) { + return obj != null && obj === obj.window; + }, + + isNumeric: function( obj ) { + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + return !jQuery.isArray( obj ) && (obj - parseFloat( obj ) + 1) >= 0; + }, + + isPlainObject: function( obj ) { + // Not plain objects: + // - Any object or value whose internal [[Class]] property is not "[object Object]" + // - DOM nodes + // - window + if ( jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + + // If the function hasn't returned already, we're confident that + // |obj| is a plain object, created by {} or constructed with new Object + return true; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + // Support: Android<4.0, iOS<6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; + }, + + // Evaluates a script in a global context + globalEval: function( code ) { + var script, + indirect = eval; + + code = jQuery.trim( code ); + + if ( code ) { + // If the code includes a valid, prologue position + // strict mode pragma, execute code by injecting a + // script tag into the document. + if ( code.indexOf("use strict") === 1 ) { + script = document.createElement("script"); + script.text = code; + document.head.appendChild( script ).parentNode.removeChild( script ); + } else { + // Otherwise, avoid the DOM node creation, insertion + // and removal by using an indirect global eval + indirect( code ); + } + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Support: IE9-11+ + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + // args is for internal usage only + each: function( obj, callback, args ) { + var value, + i = 0, + length = obj.length, + isArray = isArraylike( obj ); + + if ( args ) { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.apply( obj[ i ], args ); + + if ( value === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } else { + for ( i in obj ) { + value = callback.call( obj[ i ], i, obj[ i ] ); + + if ( value === false ) { + break; + } + } + } + } + + return obj; + }, + + // Support: Android<4.1 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArraylike( Object(arr) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + return arr == null ? -1 : indexOf.call( arr, elem, i ); + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, + i = 0, + length = elems.length, + isArray = isArraylike( elems ), + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: Date.now, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +function isArraylike( obj ) { + + // Support: iOS 8.2 (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + if ( obj.nodeType === 1 && length ) { + return true; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.2.0-pre + * http://sizzlejs.com/ + * + * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2014-12-16 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // http://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + // http://www.w3.org/TR/css3-syntax/#characters + characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Loosely modeled on CSS identifier characters + // An unquoted value should be a CSS identifier http://www.w3.org/TR/css3-selectors/#attribute-selectors + // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = characterEncoding.replace( "w", "w#" ), + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + characterEncoding + ")" ), + "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), + "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var match, elem, m, nodeType, + // QSA vars + i, groups, old, nid, newContext, newSelector; + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + + context = context || document; + results = results || []; + nodeType = context.nodeType; + + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + if ( !seed && documentIsHTML ) { + + // Try to shortcut find operations when possible (e.g., not under DocumentFragment) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + // Speed-up: Sizzle("#ID") + if ( (m = match[1]) ) { + if ( nodeType === 9 ) { + elem = context.getElementById( m ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document (jQuery #6963) + if ( elem && elem.parentNode ) { + // Handle the case where IE, Opera, and Webkit return items + // by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + } else { + // Context is not a document + if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && + contains( context, elem ) && elem.id === m ) { + results.push( elem ); + return results; + } + } + + // Speed-up: Sizzle("TAG") + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Speed-up: Sizzle(".CLASS") + } else if ( (m = match[3]) && support.getElementsByClassName ) { + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // QSA path + if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + nid = old = expando; + newContext = context; + newSelector = nodeType !== 1 && selector; + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + groups = tokenize( selector ); + + if ( (old = context.getAttribute("id")) ) { + nid = old.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", nid ); + } + nid = "[id='" + nid + "'] "; + + i = groups.length; + while ( i-- ) { + groups[i] = nid + toSelector( groups[i] ); + } + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; + newSelector = groups.join(","); + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch(qsaError) { + } finally { + if ( !old ) { + context.removeAttribute("id"); + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {Function(string, Object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = attrs.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, parent, + doc = node ? node.ownerDocument || node : preferredDoc; + + // If no document and documentElement is available, return + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Set our document + document = doc; + docElem = doc.documentElement; + parent = doc.defaultView; + + // Support: IE>8 + // If iframe document is assigned to "document" variable and if iframe has been reloaded, + // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 + // IE6-8 do not support the defaultView property so parent will be undefined + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", unloadHandler, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Support tests + ---------------------------------------------------------------------- */ + documentIsHTML = !isXML( doc ); + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( doc.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !doc.getElementsByName || !doc.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var m = context.getElementById( id ); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + docElem.appendChild( div ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.2+, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.7+ + if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = doc.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully does not implement inclusive descendent + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === doc ? -1 : + b === doc ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return doc; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, outerCache, node, diff, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + // Seek `elem` from a previously-cached index + outerCache = parent[ expando ] || (parent[ expando ] = {}); + cache = outerCache[ type ] || []; + nodeIndex = cache[0] === dirruns && cache[1]; + diff = cache[0] === dirruns && cache[2]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + // Use previously-cached element index if available + } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { + diff = cache[1]; + + // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...) + } else { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { + // Cache the index of each encountered element + if ( useCache ) { + (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context !== document && context; + } + + // Add elements passing elementMatchers directly to results + // Keep `i` a string if there are no elements so `matchedCount` will be "00" below + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context, xml ) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // Apply set filters to unmatched elements + matchedCount += i; + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is no seed and only one group + if ( match.length === 1 ) { + + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.pseudos; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + len = this.length, + ret = [], + self = this; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter(function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow(this, selector || [], false) ); + }, + not: function( selector ) { + return this.pushStack( winnow(this, selector || [], true) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +}); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // Option to run scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Support: Blackberry 4.6 + // gEBID returns nodes no longer in the document (#6963) + if ( elem && elem.parentNode ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // Methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.extend({ + dir: function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( (elem = elem[ dir ]) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } +}); + +jQuery.fn.extend({ + has: function( target ) { + var targets = jQuery( target, this ), + l = targets.length; + + return this.filter(function() { + var i = 0; + for ( ; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // Determine the position of an element within the set + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // Index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter(selector) + ); + } +}); + +function sibling( cur, dir ) { + while ( (cur = cur[dir]) && cur.nodeType !== 1 ) {} + return cur; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var matched = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + matched = jQuery.filter( selector, matched ); + } + + if ( this.length > 1 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +var rnotwhite = (/\S+/g); + + + +// String to Object options format cache +var optionsCache = {}; + +// Convert String-formatted options into Object-formatted ones and store in cache +function createOptions( options ) { + var object = optionsCache[ options ] = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + }); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + ( optionsCache[ options ] || createOptions( options ) ) : + jQuery.extend( {}, options ); + + var // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = !options.once && [], + // Fire callbacks + fire = function( data ) { + memory = options.memory && data; + fired = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + firing = true; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) { + memory = false; // To prevent further calls using add + break; + } + } + firing = false; + if ( list ) { + if ( stack ) { + if ( stack.length ) { + fire( stack.shift() ); + } + } else if ( memory ) { + list = []; + } else { + self.disable(); + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + // First, we save the current length + var start = list.length; + (function add( args ) { + jQuery.each( args, function( _, arg ) { + var type = jQuery.type( arg ); + if ( type === "function" ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && type !== "string" ) { + // Inspect recursively + add( arg ); + } + }); + })( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away + } else if ( memory ) { + firingStart = start; + fire( memory ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + // Handle firing indexes + if ( firing ) { + if ( index <= firingLength ) { + firingLength--; + } + if ( index <= firingIndex ) { + firingIndex--; + } + } + } + }); + } + return this; + }, + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); + }, + // Remove all callbacks from the list + empty: function() { + list = []; + firingLength = 0; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( list && ( !fired || stack ) ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + if ( firing ) { + stack.push( args ); + } else { + fire( args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend({ + + Deferred: function( func ) { + var tuples = [ + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ], + [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ], + [ "notify", "progress", jQuery.Callbacks("memory") ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred(function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[1] ](function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .done( newDefer.resolve ) + .fail( newDefer.reject ) + .progress( newDefer.notify ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + } + }); + }); + fns = null; + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[1] ] = list.add; + + // Handle state + if ( stateString ) { + list.add(function() { + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[0] ] = function() { + deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[0] + "With" ] = list.fireWith; + }); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // Add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ) + .progress( updateFunc( i, progressContexts, progressValues ) ); + } else { + --remaining; + } + } + } + + // If we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +}); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); + +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + jQuery.ready(); +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // We once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < len; i++ ) { + fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; + + +function Data() { + // Support: Android<4, + // Old WebKit does not have Object.preventExtensions/freeze method, + // return new empty object instead with no [[set]] accessor + Object.defineProperty( this.cache = {}, 0, { + get: function() { + return {}; + } + }); + + this.expando = jQuery.expando + Data.uid++; +} + +Data.uid = 1; +Data.accepts = jQuery.acceptData; + +Data.prototype = { + key: function( owner ) { + // We can accept data for non-element nodes in modern browsers, + // but we should not, see #8335. + // Always return the key for a frozen object. + if ( !Data.accepts( owner ) ) { + return 0; + } + + var descriptor = {}, + // Check if the owner object already has a cache key + unlock = owner[ this.expando ]; + + // If not, create one + if ( !unlock ) { + unlock = Data.uid++; + + // Secure it in a non-enumerable, non-writable property + try { + descriptor[ this.expando ] = { value: unlock }; + Object.defineProperties( owner, descriptor ); + + // Support: Android<4 + // Fallback to a less secure definition + } catch ( e ) { + descriptor[ this.expando ] = unlock; + jQuery.extend( owner, descriptor ); + } + } + + // Ensure the cache object + if ( !this.cache[ unlock ] ) { + this.cache[ unlock ] = {}; + } + + return unlock; + }, + set: function( owner, data, value ) { + var prop, + // There may be an unlock assigned to this node, + // if there is no entry for this "owner", create one inline + // and set the unlock as though an owner entry had always existed + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + // Handle: [ owner, key, value ] args + if ( typeof data === "string" ) { + cache[ data ] = value; + + // Handle: [ owner, { properties } ] args + } else { + // Fresh assignments by object are shallow copied + if ( jQuery.isEmptyObject( cache ) ) { + jQuery.extend( this.cache[ unlock ], data ); + // Otherwise, copy the properties one-by-one to the cache object + } else { + for ( prop in data ) { + cache[ prop ] = data[ prop ]; + } + } + } + return cache; + }, + get: function( owner, key ) { + // Either a valid cache is found, or will be created. + // New caches will be created and the unlock returned, + // allowing direct access to the newly created + // empty data object. A valid owner object must be provided. + var cache = this.cache[ this.key( owner ) ]; + + return key === undefined ? + cache : cache[ key ]; + }, + access: function( owner, key, value ) { + var stored; + // In cases where either: + // + // 1. No key was specified + // 2. A string key was specified, but no value provided + // + // Take the "read" path and allow the get method to determine + // which value to return, respectively either: + // + // 1. The entire cache object + // 2. The data stored at the key + // + if ( key === undefined || + ((key && typeof key === "string") && value === undefined) ) { + + stored = this.get( owner, key ); + + return stored !== undefined ? + stored : this.get( owner, jQuery.camelCase(key) ); + } + + // [*]When the key is not a string, or both a key and value + // are specified, set or extend (existing objects) with either: + // + // 1. An object of properties + // 2. A key and value + // + this.set( owner, key, value ); + + // Since the "set" path can have two possible entry points + // return the expected data based on which path was taken[*] + return value !== undefined ? value : key; + }, + remove: function( owner, key ) { + var i, name, camel, + unlock = this.key( owner ), + cache = this.cache[ unlock ]; + + if ( key === undefined ) { + this.cache[ unlock ] = {}; + + } else { + // Support array or space separated string of keys + if ( jQuery.isArray( key ) ) { + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = key.concat( key.map( jQuery.camelCase ) ); + } else { + camel = jQuery.camelCase( key ); + // Try the string as a key before any manipulation + if ( key in cache ) { + name = [ key, camel ]; + } else { + // If a key with the spaces exists, use it. + // Otherwise, create an array by matching non-whitespace + name = camel; + name = name in cache ? + [ name ] : ( name.match( rnotwhite ) || [] ); + } + } + + i = name.length; + while ( i-- ) { + delete cache[ name[ i ] ]; + } + } + }, + hasData: function( owner ) { + return !jQuery.isEmptyObject( + this.cache[ owner[ this.expando ] ] || {} + ); + }, + discard: function( owner ) { + if ( owner[ this.expando ] ) { + delete this.cache[ owner[ this.expando ] ]; + } + } +}; +var data_priv = new Data(); + +var data_user = new Data(); + + + +// Implementation Summary +// +// 1. Enforce API surface and semantic compatibility with 1.9.x branch +// 2. Improve the module's maintainability by reducing the storage +// paths to a single mechanism. +// 3. Use the same single mechanism to support "private" and "user" data. +// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) +// 5. Avoid exposing implementation details on user objects (eg. expando properties) +// 6. Provide a clear path for implementation upgrade to WeakMap in 2014 + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + +jQuery.extend({ + hasData: function( elem ) { + return data_user.hasData( elem ) || data_priv.hasData( elem ); + }, + + data: function( elem, name, data ) { + return data_user.access( elem, name, data ); + }, + + removeData: function( elem, name ) { + data_user.remove( elem, name ); + }, + + // TODO: Now that all calls to _data and _removeData have been replaced + // with direct calls to data_priv methods, these can be deprecated. + _data: function( elem, name, data ) { + return data_priv.access( elem, name, data ); + }, + + _removeData: function( elem, name ) { + data_priv.remove( elem, name ); + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = data_user.get( elem ); + + if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + data_priv.set( elem, "hasDataAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + data_user.set( this, key ); + }); + } + + return access( this, function( value ) { + var data, + camelKey = jQuery.camelCase( key ); + + // The calling jQuery object (element matches) is not empty + // (and therefore has an element appears at this[ 0 ]) and the + // `value` parameter was not undefined. An empty jQuery object + // will result in `undefined` for elem = this[ 0 ] which will + // throw an exception if an attempt to read a data cache is made. + if ( elem && value === undefined ) { + // Attempt to get data from the cache + // with the key as-is + data = data_user.get( elem, key ); + if ( data !== undefined ) { + return data; + } + + // Attempt to get data from the cache + // with the key camelized + data = data_user.get( elem, camelKey ); + if ( data !== undefined ) { + return data; + } + + // Attempt to "discover" the data in + // HTML5 custom data-* attrs + data = dataAttr( elem, camelKey, undefined ); + if ( data !== undefined ) { + return data; + } + + // We tried really hard, but the data doesn't exist. + return; + } + + // Set the data... + this.each(function() { + // First, attempt to store a copy or reference of any + // data that might've been store with a camelCased key. + var data = data_user.get( this, camelKey ); + + // For HTML5 data-* attribute interop, we have to + // store property names with dashes in a camelCase form. + // This might not apply to all properties...* + data_user.set( this, camelKey, value ); + + // *... In the case of properties that might _actually_ + // have dashes, we need to also store a copy of that + // unchanged property. + if ( key.indexOf("-") !== -1 && data !== undefined ) { + data_user.set( this, key, value ); + } + }); + }, null, value, arguments.length > 1, null, true ); + }, + + removeData: function( key ) { + return this.each(function() { + data_user.remove( this, key ); + }); + } +}); + + +jQuery.extend({ + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = data_priv.get( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = data_priv.access( elem, type, jQuery.makeArray(data) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // Clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // Not public - generate a queueHooks object, or return the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return data_priv.get( elem, key ) || data_priv.access( elem, key, { + empty: jQuery.Callbacks("once memory").add(function() { + data_priv.remove( elem, [ type + "queue", key ] ); + }) + }); + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + // Ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = data_priv.get( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +}); +var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); + }; + +var rcheckableType = (/^(?:checkbox|radio)$/i); + + + +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // Support: Safari<=5.1 + // Check state lost if the name is set (#11217) + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari<=5.1, Android<4.2 + // Older WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<=11+ + // Make sure textarea (and checkbox) defaultValue is properly cloned + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; + + + +support.focusinBubbles = "onfocusin" in window; + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + + var handleObjIn, eventHandle, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.get( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !(events = elemData.events) ) { + events = elemData.events = {}; + } + if ( !(eventHandle = elemData.handle) ) { + eventHandle = elemData.handle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; + }; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !(handlers = events[ type ]) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var j, origCount, tmp, + events, t, handleObj, + special, handlers, type, namespaces, origType, + elemData = data_priv.hasData( elem ) && data_priv.get( elem ); + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[t] ) || []; + type = origType = tmp[1]; + namespaces = ( tmp[2] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[2] && new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + data_priv.remove( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + + var i, cur, tmp, bubbleType, ontype, handle, special, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf(".") >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf(":") < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join("."); + event.namespace_re = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === (elem.ownerDocument || document) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( (cur = eventPath[i++]) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( data_priv.get( cur, "events" ) || {} )[ event.type ] && data_priv.get( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( eventPath.pop(), data ) === false) && + jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && jQuery.isFunction( elem[ type ] ) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( (event.result = ret) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && cur.nodeType && (!event.button || event.type !== "click") ) { + + for ( ; cur !== this; cur = cur.parentNode || this ) { + + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.disabled !== true || event.type !== "click" ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) >= 0 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, handlers: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) }); + } + + return handlerQueue; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: Cordova 2.5 (WebKit) (#13255) + // All events should have a target; Cordova deviceready doesn't + if ( !event.target ) { + event.target = document; + } + + // Support: Safari 6.0+, Chrome<28 + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + this.focus(); + return false; + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( this.type === "checkbox" && this.click && jQuery.nodeName( this, "input" ) ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } +}; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + // Support: Android<4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + + if ( e && e.preventDefault ) { + e.preventDefault(); + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( e && e.stopPropagation ) { + e.stopPropagation(); + } + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// Support: Chrome 15+ +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// Support: Firefox, Chrome, Safari +// Create "bubbling" focus and blur events +if ( !support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + var elem = this[0]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +}); + + +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, + rtagName = /<([\w:]+)/, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style|link)/i, + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /^$|\/(?:java|ecma)script/i, + rscriptTypeMasked = /^true\/(.*)/, + rcleanScript = /^\s*\s*$/g, + + // We have to close these tags to support XHTML (#13200) + wrapMap = { + + // Support: IE9 + option: [ 1, "" ], + + thead: [ 1, "", "
      " ], + col: [ 2, "", "
      " ], + tr: [ 2, "", "
      " ], + td: [ 3, "", "
      " ], + + _default: [ 0, "", "" ] + }; + +// Support: IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// Support: 1.x compatibility +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute("type"); + } + + return elem; +} + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + } + + // 2. Copy user data + if ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + data_user.set( dest, udataCur ); + } +} + +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + +// Fix IE bugs, see support tests +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var i, l, srcElements, destElements, + clone = elem.cloneNode( true ), + inPage = jQuery.contains( elem.ownerDocument, elem ); + + // Fix IE cloning issues + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + fixInput( srcElements[ i ], destElements[ i ] ); + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0, l = srcElements.length; i < l; i++ ) { + cloneCopyEvent( srcElements[ i ], destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + // Return the cloned set + return clone; + }, + + buildFragment: function( elems, context, scripts, selection ) { + var elem, tmp, tag, wrap, contains, j, + fragment = context.createDocumentFragment(), + nodes = [], + i = 0, + l = elems.length; + + for ( ; i < l; i++ ) { + elem = elems[ i ]; + + if ( elem || elem === 0 ) { + + // Add nodes directly + if ( jQuery.type( elem ) === "object" ) { + // Support: QtWebKit, PhantomJS + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); + + // Convert non-html into a text node + } else if ( !rhtml.test( elem ) ) { + nodes.push( context.createTextNode( elem ) ); + + // Convert html into DOM nodes + } else { + tmp = tmp || fragment.appendChild( context.createElement("div") ); + + // Deserialize a standard representation + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); + wrap = wrapMap[ tag ] || wrapMap._default; + tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; + + // Descend through wrappers to the right content + j = wrap[ 0 ]; + while ( j-- ) { + tmp = tmp.lastChild; + } + + // Support: QtWebKit, PhantomJS + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( nodes, tmp.childNodes ); + + // Remember the top-level container + tmp = fragment.firstChild; + + // Ensure the created nodes are orphaned (#12392) + tmp.textContent = ""; + } + } + } + + // Remove wrapper from fragment + fragment.textContent = ""; + + i = 0; + while ( (elem = nodes[ i++ ]) ) { + + // #4087 - If origin and destination elements are the same, and this is + // that element, do not do anything + if ( selection && jQuery.inArray( elem, selection ) !== -1 ) { + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( fragment.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( (elem = tmp[ j++ ]) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + return fragment; + }, + + cleanData: function( elems ) { + var data, elem, type, key, + special = jQuery.event.special, + i = 0; + + for ( ; (elem = elems[ i ]) !== undefined; i++ ) { + if ( jQuery.acceptData( elem ) ) { + key = elem[ data_priv.expando ]; + + if ( key && (data = data_priv.cache[ key ]) ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + if ( data_priv.cache[ key ] ) { + // Discard any remaining `private` data + delete data_priv.cache[ key ]; + } + } + } + // Discard any remaining `user` data + delete data_user.cache[ elem[ data_user.expando ] ]; + } + } +}); + +jQuery.fn.extend({ + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each(function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + }); + }, null, value, arguments.length ); + }, + + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, + + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, + + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); + } + + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); + } + elem.parentNode.removeChild( elem ); + } + } + + return this; + }, + + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); + }); + } + + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } + } + } + } + + return this; + } +}); + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optimization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "