diff --git a/.github/V8_GETTING_STARTED.md b/.github/V8_GETTING_STARTED.md index 62b376b0e7..87ddbb166a 100644 --- a/.github/V8_GETTING_STARTED.md +++ b/.github/V8_GETTING_STARTED.md @@ -33,5 +33,5 @@ We recommend running the site with the Visual Studio since you'll be able to rem We are keeping track of [known issues and limitations here](http://issues.umbraco.org/issue/U4-11279). These line items will eventually be turned into actual tasks to be worked on. Feel free to help us keep this list updated if you find issues and even help fix some of these items. If there is a particular item you'd like to help fix please mention this on the task and we'll create a sub task for the item to continue discussion there. -There's [a list of tasks for v8 that haven't been completed](https://issues.umbraco.org/issues?q=&project=U4&tagValue=&release=8.0.0&issueType=&resolvedState=open&search=search). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done. +There's [a list of tasks for v8 that haven't been completed](hhttps://github.com/umbraco/Umbraco-CMS/labels/release%2F8.0.0). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done. diff --git a/build/NuSpecs/UmbracoCms.Core.nuspec b/build/NuSpecs/UmbracoCms.Core.nuspec index daa0018668..dd565aa1d4 100644 --- a/build/NuSpecs/UmbracoCms.Core.nuspec +++ b/build/NuSpecs/UmbracoCms.Core.nuspec @@ -36,7 +36,7 @@ - + diff --git a/src/Umbraco.Core/Composing/TypeFinder.cs b/src/Umbraco.Core/Composing/TypeFinder.cs index a42b84e0c5..9604f599cf 100644 --- a/src/Umbraco.Core/Composing/TypeFinder.cs +++ b/src/Umbraco.Core/Composing/TypeFinder.cs @@ -210,53 +210,55 @@ namespace Umbraco.Core.Composing /// NOTE the comma vs period... comma delimits the name in an Assembly FullName property so if it ends with comma then its an exact name match /// NOTE this means that "foo." will NOT exclude "foo.dll" but only "foo.*.dll" /// - internal static readonly string[] KnownAssemblyExclusionFilter = new[] - { - "mscorlib,", - "System.", - "Antlr3.", - "Autofac.", - "Autofac,", - "Castle.", - "ClientDependency.", - "DataAnnotationsExtensions.", - "DataAnnotationsExtensions,", - "Dynamic,", - "HtmlDiff,", - "Iesi.Collections,", - "Microsoft.", - "Newtonsoft.", - "NHibernate.", - "NHibernate,", - "NuGet.", - "RouteDebugger,", - "SqlCE4Umbraco,", - "umbraco.datalayer,", - "umbraco.interfaces,", - //"umbraco.providers,", - //"Umbraco.Web.UI,", - "umbraco.webservices", - "Lucene.", - "Examine,", - "Examine.", - "ServiceStack.", - "MySql.", - "HtmlAgilityPack.", - "TidyNet.", - "ICSharpCode.", - "CookComputing.", - "AutoMapper,", - "AutoMapper.", - "AzureDirectory,", - "itextsharp,", - "UrlRewritingNet.", - "HtmlAgilityPack,", - "MiniProfiler,", - "Moq,", - "nunit.framework,", - "TidyNet,", - "WebDriver," - }; + internal static readonly string[] KnownAssemblyExclusionFilter = { + "Antlr3.", + "AutoMapper,", + "AutoMapper.", + "Autofac,", // DI + "Autofac.", + "AzureDirectory,", + "Castle.", // DI, tests + "ClientDependency.", + "CookComputing.", + "CSharpTest.", // BTree for NuCache + "DataAnnotationsExtensions,", + "DataAnnotationsExtensions.", + "Dynamic,", + "Examine,", + "Examine.", + "HtmlAgilityPack,", + "HtmlAgilityPack.", + "HtmlDiff,", + "ICSharpCode.", + "Iesi.Collections,", // used by NHibernate + "LightInject.", // DI + "LightInject,", + "Lucene.", + "Markdown,", + "Microsoft.", + "MiniProfiler,", + "Moq,", + "MySql.", + "NHibernate,", + "NHibernate.", + "Newtonsoft.", + "NPoco,", + "NuGet.", + "RouteDebugger,", + "Semver.", + "Serilog.", + "Serilog,", + "ServiceStack.", + "SqlCE4Umbraco,", + "Superpower,", // used by Serilog + "System.", + "TidyNet,", + "TidyNet.", + "WebDriver,", + "itextsharp,", + "mscorlib,", + "nunit.framework,", + }; /// /// Finds any classes derived from the type T that contain the attribute TAttribute diff --git a/src/Umbraco.Core/Composing/TypeLoader.cs b/src/Umbraco.Core/Composing/TypeLoader.cs index 3e7baf66b8..491ccb0c96 100644 --- a/src/Umbraco.Core/Composing/TypeLoader.cs +++ b/src/Umbraco.Core/Composing/TypeLoader.cs @@ -530,6 +530,8 @@ namespace Umbraco.Core.Composing // if not caching, or not IDiscoverable, directly get types if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false) { + _logger.Logger.Debug("Running a full, non-cached, scan for type {TypeName} (slow).", typeof(T).FullName); + return GetTypesInternal( typeof (T), null, () => TypeFinder.FindClassesOfType(specificAssemblies ?? AssembliesToScan), @@ -572,6 +574,8 @@ namespace Umbraco.Core.Composing // if not caching, or not IDiscoverable, directly get types if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false) { + _logger.Logger.Debug("Running a full, non-cached, scan for type {TypeName} / attribute {AttributeName} (slow).", typeof(T).FullName, typeof(TAttribute).FullName); + return GetTypesInternal( typeof (T), typeof (TAttribute), () => TypeFinder.FindClassesOfTypeWithAttribute(specificAssemblies ?? AssembliesToScan), @@ -611,6 +615,11 @@ namespace Umbraco.Core.Composing // do not cache anything from specific assemblies cache &= specificAssemblies == null; + if (cache == false) + { + _logger.Logger.Debug("Running a full, non-cached, scan for types / attribute {AttributeName} (slow).", typeof(TAttribute).FullName); + } + return GetTypesInternal( typeof (object), typeof (TAttribute), () => TypeFinder.FindClassesWithAttribute(specificAssemblies ?? AssembliesToScan), diff --git a/src/Umbraco.Core/Persistence/Repositories/IDocumentBlueprintRepository.cs b/src/Umbraco.Core/Persistence/Repositories/IDocumentBlueprintRepository.cs index c52601d629..0148a882fd 100644 --- a/src/Umbraco.Core/Persistence/Repositories/IDocumentBlueprintRepository.cs +++ b/src/Umbraco.Core/Persistence/Repositories/IDocumentBlueprintRepository.cs @@ -1,5 +1,5 @@ namespace Umbraco.Core.Persistence.Repositories { - interface IDocumentBlueprintRepository : IDocumentRepository + public interface IDocumentBlueprintRepository : IDocumentRepository { } } diff --git a/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs index b2bf99bdc6..55ca199121 100644 --- a/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs +++ b/src/Umbraco.Core/PropertyEditors/SliderConfiguration.cs @@ -8,9 +8,6 @@ [ConfigurationField("enableRange", "Enable range", "boolean")] public bool EnableRange { get; set; } - [ConfigurationField("orientation", "Orientation", "views/propertyeditors/slider/orientation.prevalues.html")] - public string Orientation { get; set; } - [ConfigurationField("initVal1", "Initial value", "number")] public int InitialValue { get; set; } @@ -25,38 +22,5 @@ [ConfigurationField("step", "Step increments", "number")] public int StepIncrements { get; set; } - - [ConfigurationField("precision", "Precision", "number", Description = "The number of digits shown after the decimal. Defaults to the number of digits after the decimal of step value.")] - public int Precision { get; set; } - - [ConfigurationField("handle", "Handle", "views/propertyeditors/slider/handle.prevalues.html", Description = "Handle shape. Default is 'round\'")] - public string Handle { get; set; } - - [ConfigurationField("tooltip", "Tooltip", "views/propertyeditors/slider/tooltip.prevalues.html", Description = "Whether to show the tooltip on drag, hide the tooltip, or always show the tooltip. Accepts: 'show', 'hide', or 'always'")] - public string Tooltip { get; set; } - - [ConfigurationField("tooltipSplit", "Tooltip split", "boolean", Description = "If false show one tootip if true show two tooltips one for each handler")] - public bool TooltipSplit { get; set; } // fixme bool? - - [ConfigurationField("tooltipFormat", "Tooltip format", "textstring", Description = "The value wanted to be displayed in the tooltip. Use {0} and {1} for current values - {1} is only for range slider and if not using tooltip split.")] - public string TooltipFormat { get; set; } - - [ConfigurationField("tooltipPosition", "Tooltip position", "textstring", Description = "Position of tooltip, relative to slider. Accepts 'top'/'bottom' for horizontal sliders and 'left'/'right' for vertically orientated sliders. Default positions are 'top' for horizontal and 'right' for vertical slider.")] - public string TooltipPosition { get; set; } - - [ConfigurationField("reversed", "Reversed", "boolean", Description = "Whether or not the slider should be reversed")] - public bool Reversed { get; set; } // fixme bool? - - [ConfigurationField("ticks", "Ticks", "textstring", Description = "Comma-separated values. Used to define the values of ticks. Tick marks are indicators to denote special values in the range. This option overwrites min and max options.")] - public string Ticks { get; set; } - - [ConfigurationField("ticksPositions", "Ticks positions", "textstring", Description = "Comma-separated values. Defines the positions of the tick values in percentages. The first value should always be 0, the last value should always be 100 percent.")] - public string TicksPositions { get; set; } - - [ConfigurationField("ticksLabels", "Ticks labels", "textstring", Description = "Comma-separated values. Defines the labels below the tick marks. Accepts HTML input.")] - public string TicksLabels { get; set; } - - [ConfigurationField("ticksSnapBounds", "Ticks snap bounds", "number", Description = "Used to define the snap bounds of a tick. Snaps to the tick if value is within these bounds.")] - public int TicksSnapBounds { get; set; } } } diff --git a/src/Umbraco.Core/Services/Implement/ContentService.cs b/src/Umbraco.Core/Services/Implement/ContentService.cs index dece3ed3ba..c5ad233a7a 100644 --- a/src/Umbraco.Core/Services/Implement/ContentService.cs +++ b/src/Umbraco.Core/Services/Implement/ContentService.cs @@ -19,7 +19,7 @@ namespace Umbraco.Core.Services.Implement /// /// Implements the content service. /// - internal class ContentService : RepositoryService, IContentService + public class ContentService : RepositoryService, IContentService { private readonly IDocumentRepository _documentRepository; private readonly IEntityRepository _entityRepository; diff --git a/src/Umbraco.Core/Services/MoveOperationStatusType.cs b/src/Umbraco.Core/Services/MoveOperationStatusType.cs index 95ccce93ca..b4b4c2b42e 100644 --- a/src/Umbraco.Core/Services/MoveOperationStatusType.cs +++ b/src/Umbraco.Core/Services/MoveOperationStatusType.cs @@ -7,7 +7,7 @@ /// /// Anything less than 10 = Success! /// - public enum MoveOperationStatusType + public enum MoveOperationStatusType : byte { /// /// The move was successful. diff --git a/src/Umbraco.Core/Umbraco.Core.csproj b/src/Umbraco.Core/Umbraco.Core.csproj index 4823811566..ecc8e719b4 100755 --- a/src/Umbraco.Core/Umbraco.Core.csproj +++ b/src/Umbraco.Core/Umbraco.Core.csproj @@ -89,7 +89,7 @@ 1.0.0 - 2.1.2 + 2.2.2 4.0.0 diff --git a/src/Umbraco.Web.UI.Client/gulpfile.js b/src/Umbraco.Web.UI.Client/gulpfile.js index cd7f6f4bef..580acae6ca 100644 --- a/src/Umbraco.Web.UI.Client/gulpfile.js +++ b/src/Umbraco.Web.UI.Client/gulpfile.js @@ -331,6 +331,14 @@ gulp.task('dependencies', function () { "src": ["./node_modules/ng-file-upload/dist/ng-file-upload.min.js"], "base": "./node_modules/ng-file-upload/dist" }, + { + "name": "nouislider", + "src": [ + "./node_modules/nouislider/distribute/nouislider.min.js", + "./node_modules/nouislider/distribute/nouislider.min.css" + ], + "base": "./node_modules/nouislider/distribute" + }, { "name": "signalr", "src": ["./node_modules/signalr/jquery.signalR.js"], diff --git a/src/Umbraco.Web.UI.Client/lib/datetimepicker/bootstrap-datetimepicker.js b/src/Umbraco.Web.UI.Client/lib/datetimepicker/bootstrap-datetimepicker.js deleted file mode 100644 index 1ec2e9374d..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/datetimepicker/bootstrap-datetimepicker.js +++ /dev/null @@ -1,1436 +0,0 @@ -/* -//! version : 3.1.3 -========================================================= -bootstrap-datetimepicker.js -https://github.com/Eonasdan/bootstrap-datetimepicker -========================================================= -The MIT License (MIT) - -Copyright (c) 2014 Jonathan Peterson - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ -;(function (root, factory) { - 'use strict'; - if (typeof define === 'function' && define.amd) { - // AMD is used - Register as an anonymous module. - define(['jquery', 'moment'], factory); - } else if (typeof exports === 'object') { - factory(require('jquery'), require('moment')); - } - else { - // Neither AMD or CommonJS used. Use global variables. - if (!jQuery) { - throw new Error('bootstrap-datetimepicker requires jQuery to be loaded first'); - } - if (!moment) { - throw new Error('bootstrap-datetimepicker requires moment.js to be loaded first'); - } - factory(root.jQuery, moment); - } -}(this, function ($, moment) { - 'use strict'; - if (typeof moment === 'undefined') { - throw new Error('momentjs is required'); - } - - var dpgId = 0, - - DateTimePicker = function (element, options) { - var defaults = $.fn.datetimepicker.defaults, - - icons = { - time: 'glyphicon glyphicon-time', - date: 'glyphicon glyphicon-calendar', - up: 'glyphicon glyphicon-chevron-up', - down: 'glyphicon glyphicon-chevron-down' - }, - - picker = this, - errored = false, - dDate, - - init = function () { - var icon = false, localeData, rInterval; - picker.options = $.extend({}, defaults, options); - picker.options.icons = $.extend({}, icons, picker.options.icons); - - picker.element = $(element); - - dataToOptions(); - - if (!(picker.options.pickTime || picker.options.pickDate)) { - throw new Error('Must choose at least one picker'); - } - - picker.id = dpgId++; - moment.locale(picker.options.language); - picker.date = moment(); - picker.unset = false; - picker.isInput = picker.element.is('input'); - picker.component = false; - - if (picker.element.hasClass('input-group')) { - if (picker.element.find('.datepickerbutton').size() === 0) {//in case there is more then one 'input-group-addon' Issue #48 - picker.component = picker.element.find('[class^="input-group-"]'); - } - else { - picker.component = picker.element.find('.datepickerbutton'); - } - } - picker.format = picker.options.format; - - localeData = moment().localeData(); - - if (!picker.format) { - picker.format = (picker.options.pickDate ? localeData.longDateFormat('L') : ''); - if (picker.options.pickDate && picker.options.pickTime) { - picker.format += ' '; - } - picker.format += (picker.options.pickTime ? localeData.longDateFormat('LT') : ''); - if (picker.options.useSeconds) { - if (localeData.longDateFormat('LT').indexOf(' A') !== -1) { - picker.format = picker.format.split(' A')[0] + ':ss A'; - } - else { - picker.format += ':ss'; - } - } - } - picker.use24hours = (picker.format.toLowerCase().indexOf('a') < 0 && picker.format.indexOf('h') < 0); - - if (picker.component) { - icon = picker.component.find('span'); - } - - if (picker.options.pickTime) { - if (icon) { - icon.addClass(picker.options.icons.time); - } - } - if (picker.options.pickDate) { - if (icon) { - icon.removeClass(picker.options.icons.time); - icon.addClass(picker.options.icons.date); - } - } - - picker.options.widgetParent = - typeof picker.options.widgetParent === 'string' && picker.options.widgetParent || - picker.element.parents().filter(function () { - return 'scroll' === $(this).css('overflow-y'); - }).get(0) || - 'body'; - - picker.widget = $(getTemplate()).appendTo(picker.options.widgetParent); - - picker.minViewMode = picker.options.minViewMode || 0; - if (typeof picker.minViewMode === 'string') { - switch (picker.minViewMode) { - case 'months': - picker.minViewMode = 1; - break; - case 'years': - picker.minViewMode = 2; - break; - default: - picker.minViewMode = 0; - break; - } - } - picker.viewMode = picker.options.viewMode || 0; - if (typeof picker.viewMode === 'string') { - switch (picker.viewMode) { - case 'months': - picker.viewMode = 1; - break; - case 'years': - picker.viewMode = 2; - break; - default: - picker.viewMode = 0; - break; - } - } - - picker.viewMode = Math.max(picker.viewMode, picker.minViewMode); - - picker.options.disabledDates = indexGivenDates(picker.options.disabledDates); - picker.options.enabledDates = indexGivenDates(picker.options.enabledDates); - - picker.startViewMode = picker.viewMode; - picker.setMinDate(picker.options.minDate); - picker.setMaxDate(picker.options.maxDate); - fillDow(); - fillMonths(); - fillHours(); - fillMinutes(); - fillSeconds(); - update(); - showMode(); - if (!getPickerInput().prop('disabled')) { - attachDatePickerEvents(); - } - if (picker.options.defaultDate !== '' && getPickerInput().val() === '') { - picker.setValue(picker.options.defaultDate); - } - if (picker.options.minuteStepping !== 1) { - rInterval = picker.options.minuteStepping; - picker.date.minutes((Math.round(picker.date.minutes() / rInterval) * rInterval) % 60).seconds(0); - } - }, - - getPickerInput = function () { - var input; - - if (picker.isInput) { - return picker.element; - } - input = picker.element.find('.datepickerinput'); - if (input.size() === 0) { - input = picker.element.find('input'); - } - else if (!input.is('input')) { - throw new Error('CSS class "datepickerinput" cannot be applied to non input element'); - } - return input; - }, - - dataToOptions = function () { - var eData; - if (picker.element.is('input')) { - eData = picker.element.data(); - } - else { - eData = picker.element.find('input').data(); - } - if (eData.dateFormat !== undefined) { - picker.options.format = eData.dateFormat; - } - if (eData.datePickdate !== undefined) { - picker.options.pickDate = eData.datePickdate; - } - if (eData.datePicktime !== undefined) { - picker.options.pickTime = eData.datePicktime; - } - if (eData.dateUseminutes !== undefined) { - picker.options.useMinutes = eData.dateUseminutes; - } - if (eData.dateUseseconds !== undefined) { - picker.options.useSeconds = eData.dateUseseconds; - } - if (eData.dateUsecurrent !== undefined) { - picker.options.useCurrent = eData.dateUsecurrent; - } - if (eData.calendarWeeks !== undefined) { - picker.options.calendarWeeks = eData.calendarWeeks; - } - if (eData.dateMinutestepping !== undefined) { - picker.options.minuteStepping = eData.dateMinutestepping; - } - if (eData.dateMindate !== undefined) { - picker.options.minDate = eData.dateMindate; - } - if (eData.dateMaxdate !== undefined) { - picker.options.maxDate = eData.dateMaxdate; - } - if (eData.dateShowtoday !== undefined) { - picker.options.showToday = eData.dateShowtoday; - } - if (eData.dateCollapse !== undefined) { - picker.options.collapse = eData.dateCollapse; - } - if (eData.dateLanguage !== undefined) { - picker.options.language = eData.dateLanguage; - } - if (eData.dateDefaultdate !== undefined) { - picker.options.defaultDate = eData.dateDefaultdate; - } - if (eData.dateDisableddates !== undefined) { - picker.options.disabledDates = eData.dateDisableddates; - } - if (eData.dateEnableddates !== undefined) { - picker.options.enabledDates = eData.dateEnableddates; - } - if (eData.dateIcons !== undefined) { - picker.options.icons = eData.dateIcons; - } - if (eData.dateUsestrict !== undefined) { - picker.options.useStrict = eData.dateUsestrict; - } - if (eData.dateDirection !== undefined) { - picker.options.direction = eData.dateDirection; - } - if (eData.dateSidebyside !== undefined) { - picker.options.sideBySide = eData.dateSidebyside; - } - if (eData.dateDaysofweekdisabled !== undefined) { - picker.options.daysOfWeekDisabled = eData.dateDaysofweekdisabled; - } - }, - - place = function () { - var position = 'absolute', - offset = picker.component ? picker.component.offset() : picker.element.offset(), - $window = $(window), - placePosition; - - picker.width = picker.component ? picker.component.outerWidth() : picker.element.outerWidth(); - offset.top = offset.top + picker.element.outerHeight(); - - if (picker.options.direction === 'up') { - placePosition = 'top'; - } else if (picker.options.direction === 'bottom') { - placePosition = 'bottom'; - } else if (picker.options.direction === 'auto') { - if (offset.top + picker.widget.height() > $window.height() + $window.scrollTop() && picker.widget.height() + picker.element.outerHeight() < offset.top) { - placePosition = 'top'; - } else { - placePosition = 'bottom'; - } - } - if (placePosition === 'top') { - offset.bottom = $window.height() - offset.top + picker.element.outerHeight() + 3; - picker.widget.addClass('top').removeClass('bottom'); - } else { - offset.top += 1; - picker.widget.addClass('bottom').removeClass('top'); - } - - if (picker.options.width !== undefined) { - picker.widget.width(picker.options.width); - } - - if (picker.options.orientation === 'left') { - picker.widget.addClass('left-oriented'); - offset.left = offset.left - picker.widget.width() + 20; - } - - if (isInFixed()) { - position = 'fixed'; - offset.top -= $window.scrollTop(); - offset.left -= $window.scrollLeft(); - } - - if ($window.width() < offset.left + picker.widget.outerWidth()) { - offset.right = $window.width() - offset.left - picker.width; - offset.left = 'auto'; - picker.widget.addClass('pull-right'); - } else { - offset.right = 'auto'; - picker.widget.removeClass('pull-right'); - } - - if (placePosition === 'top') { - picker.widget.css({ - position: position, - bottom: offset.bottom, - top: 'auto', - left: offset.left, - right: offset.right - }); - } else { - picker.widget.css({ - position: position, - top: offset.top, - bottom: 'auto', - left: offset.left, - right: offset.right - }); - } - }, - - notifyChange = function (oldDate, eventType) { - if (moment(picker.date).isSame(moment(oldDate)) && !errored) { - return; - } - errored = false; - picker.element.trigger({ - type: 'dp.change', - date: moment(picker.date), - oldDate: moment(oldDate) - }); - - if (eventType !== 'change') { - picker.element.change(); - } - - ////////MODIFICATION FOR COMPATIBILITY WITH OLD DATEPICKER - //The old datepicker doesn't handle timezones occrectly. - //This forces the new datepicker to mimick the incorrect values from the old datepicker - //To do this it formats the datetime to a string *without* timezone info and then parses that as UTC (even though it is not), then adds the timezone afterwards. - picker.element.trigger({ - type: 'changeDate', - date: moment.utc(picker.date.format("YYYY-MM-DD HH:mm:ss")).local().toDate(), - localDate: moment(picker.date).toDate() - }); - ///////END MODIFICATION FOR COMPATIBILTIY - }, - - notifyError = function (date) { - errored = true; - picker.element.trigger({ - type: 'dp.error', - date: moment(date, picker.format, picker.options.useStrict) - }); - }, - - update = function (newDate) { - moment.locale(picker.options.language); - var dateStr = newDate; - if (!dateStr) { - dateStr = getPickerInput().val(); - if (dateStr) { - picker.date = moment(dateStr, picker.format, picker.options.useStrict); - } - if (!picker.date) { - picker.date = moment(); - } - } - picker.viewDate = moment(picker.date).startOf('month'); - fillDate(); - fillTime(); - }, - - fillDow = function () { - moment.locale(picker.options.language); - var html = $(''), weekdaysMin = moment.weekdaysMin(), i; - if (picker.options.calendarWeeks === true) { - html.append('#'); - } - if (moment().localeData()._week.dow === 0) { // starts on Sunday - for (i = 0; i < 7; i++) { - html.append('' + weekdaysMin[i] + ''); - } - } else { - for (i = 1; i < 8; i++) { - if (i === 7) { - html.append('' + weekdaysMin[0] + ''); - } else { - html.append('' + weekdaysMin[i] + ''); - } - } - } - picker.widget.find('.datepicker-days thead').append(html); - }, - - fillMonths = function () { - moment.locale(picker.options.language); - var html = '', i, monthsShort = moment.monthsShort(); - for (i = 0; i < 12; i++) { - html += '' + monthsShort[i] + ''; - } - picker.widget.find('.datepicker-months td').append(html); - }, - - fillDate = function () { - if (!picker.options.pickDate) { - return; - } - moment.locale(picker.options.language); - var year = picker.viewDate.year(), - month = picker.viewDate.month(), - startYear = picker.options.minDate.year(), - startMonth = picker.options.minDate.month(), - endYear = picker.options.maxDate.year(), - endMonth = picker.options.maxDate.month(), - currentDate, - prevMonth, nextMonth, html = [], row, clsName, i, days, yearCont, currentYear, months = moment.months(); - - picker.widget.find('.datepicker-days').find('.disabled').removeClass('disabled'); - picker.widget.find('.datepicker-months').find('.disabled').removeClass('disabled'); - picker.widget.find('.datepicker-years').find('.disabled').removeClass('disabled'); - - picker.widget.find('.datepicker-days th:eq(1)').text( - months[month] + ' ' + year); - - prevMonth = moment(picker.viewDate, picker.format, picker.options.useStrict).subtract(1, 'months'); - days = prevMonth.daysInMonth(); - prevMonth.date(days).startOf('week'); - if ((year === startYear && month <= startMonth) || year < startYear) { - picker.widget.find('.datepicker-days th:eq(0)').addClass('disabled'); - } - if ((year === endYear && month >= endMonth) || year > endYear) { - picker.widget.find('.datepicker-days th:eq(2)').addClass('disabled'); - } - - nextMonth = moment(prevMonth).add(42, 'd'); - while (prevMonth.isBefore(nextMonth)) { - if (prevMonth.weekday() === moment().startOf('week').weekday()) { - row = $(''); - html.push(row); - if (picker.options.calendarWeeks === true) { - row.append('' + prevMonth.week() + ''); - } - } - clsName = ''; - if (prevMonth.year() < year || (prevMonth.year() === year && prevMonth.month() < month)) { - clsName += ' old'; - } else if (prevMonth.year() > year || (prevMonth.year() === year && prevMonth.month() > month)) { - clsName += ' new'; - } - if (prevMonth.isSame(moment({y: picker.date.year(), M: picker.date.month(), d: picker.date.date()}))) { - clsName += ' active'; - } - if (isInDisableDates(prevMonth, 'day') || !isInEnableDates(prevMonth)) { - clsName += ' disabled'; - } - if (picker.options.showToday === true) { - if (prevMonth.isSame(moment(), 'day')) { - clsName += ' today'; - } - } - if (picker.options.daysOfWeekDisabled) { - for (i = 0; i < picker.options.daysOfWeekDisabled.length; i++) { - if (prevMonth.day() === picker.options.daysOfWeekDisabled[i]) { - clsName += ' disabled'; - break; - } - } - } - row.append('' + prevMonth.date() + ''); - - currentDate = prevMonth.date(); - prevMonth.add(1, 'd'); - - if (currentDate === prevMonth.date()) { - prevMonth.add(1, 'd'); - } - } - picker.widget.find('.datepicker-days tbody').empty().append(html); - currentYear = picker.date.year(); - months = picker.widget.find('.datepicker-months').find('th:eq(1)').text(year).end().find('span').removeClass('active'); - if (currentYear === year) { - months.eq(picker.date.month()).addClass('active'); - } - if (year - 1 < startYear) { - picker.widget.find('.datepicker-months th:eq(0)').addClass('disabled'); - } - if (year + 1 > endYear) { - picker.widget.find('.datepicker-months th:eq(2)').addClass('disabled'); - } - for (i = 0; i < 12; i++) { - if ((year === startYear && startMonth > i) || (year < startYear)) { - $(months[i]).addClass('disabled'); - } else if ((year === endYear && endMonth < i) || (year > endYear)) { - $(months[i]).addClass('disabled'); - } - } - - html = ''; - year = parseInt(year / 10, 10) * 10; - yearCont = picker.widget.find('.datepicker-years').find( - 'th:eq(1)').text(year + '-' + (year + 9)).parents('table').find('td'); - picker.widget.find('.datepicker-years').find('th').removeClass('disabled'); - if (startYear > year) { - picker.widget.find('.datepicker-years').find('th:eq(0)').addClass('disabled'); - } - if (endYear < year + 9) { - picker.widget.find('.datepicker-years').find('th:eq(2)').addClass('disabled'); - } - year -= 1; - for (i = -1; i < 11; i++) { - html += '' + year + ''; - year += 1; - } - yearCont.html(html); - }, - - fillHours = function () { - moment.locale(picker.options.language); - var table = picker.widget.find('.timepicker .timepicker-hours table'), html = '', current, i, j; - table.parent().hide(); - if (picker.use24hours) { - current = 0; - for (i = 0; i < 6; i += 1) { - html += ''; - for (j = 0; j < 4; j += 1) { - html += '' + padLeft(current.toString()) + ''; - current++; - } - html += ''; - } - } - else { - current = 1; - for (i = 0; i < 3; i += 1) { - html += ''; - for (j = 0; j < 4; j += 1) { - html += '' + padLeft(current.toString()) + ''; - current++; - } - html += ''; - } - } - table.html(html); - }, - - fillMinutes = function () { - var table = picker.widget.find('.timepicker .timepicker-minutes table'), html = '', current = 0, i, j, step = picker.options.minuteStepping; - table.parent().hide(); - if (step === 1) { - step = 5; - } - for (i = 0; i < Math.ceil(60 / step / 4) ; i++) { - html += ''; - for (j = 0; j < 4; j += 1) { - if (current < 60) { - html += '' + padLeft(current.toString()) + ''; - current += step; - } else { - html += ''; - } - } - html += ''; - } - table.html(html); - }, - - fillSeconds = function () { - var table = picker.widget.find('.timepicker .timepicker-seconds table'), html = '', current = 0, i, j; - table.parent().hide(); - for (i = 0; i < 3; i++) { - html += ''; - for (j = 0; j < 4; j += 1) { - html += '' + padLeft(current.toString()) + ''; - current += 5; - } - html += ''; - } - table.html(html); - }, - - fillTime = function () { - if (!picker.date) { - return; - } - var timeComponents = picker.widget.find('.timepicker span[data-time-component]'), - hour = picker.date.hours(), - period = picker.date.format('A'); - if (!picker.use24hours) { - if (hour === 0) { - hour = 12; - } else if (hour !== 12) { - hour = hour % 12; - } - picker.widget.find('.timepicker [data-action=togglePeriod]').text(period); - } - timeComponents.filter('[data-time-component=hours]').text(padLeft(hour)); - timeComponents.filter('[data-time-component=minutes]').text(padLeft(picker.date.minutes())); - timeComponents.filter('[data-time-component=seconds]').text(padLeft(picker.date.second())); - }, - - click = function (e) { - e.stopPropagation(); - e.preventDefault(); - picker.unset = false; - var target = $(e.target).closest('span, td, th'), month, year, step, day, oldDate = moment(picker.date); - if (target.length === 1) { - if (!target.is('.disabled')) { - switch (target[0].nodeName.toLowerCase()) { - case 'th': - switch (target[0].className) { - case 'picker-switch': - showMode(1); - break; - case 'prev': - case 'next': - step = dpGlobal.modes[picker.viewMode].navStep; - if (target[0].className === 'prev') { - step = step * -1; - } - picker.viewDate.add(step, dpGlobal.modes[picker.viewMode].navFnc); - fillDate(); - break; - } - break; - case 'span': - if (target.is('.month')) { - month = target.parent().find('span').index(target); - picker.viewDate.month(month); - } else { - year = parseInt(target.text(), 10) || 0; - picker.viewDate.year(year); - } - if (picker.viewMode === picker.minViewMode) { - picker.date = moment({ - y: picker.viewDate.year(), - M: picker.viewDate.month(), - d: picker.viewDate.date(), - h: picker.date.hours(), - m: picker.date.minutes(), - s: picker.date.seconds() - }); - set(); - notifyChange(oldDate, e.type); - } - showMode(-1); - fillDate(); - break; - case 'td': - if (target.is('.day')) { - day = parseInt(target.text(), 10) || 1; - month = picker.viewDate.month(); - year = picker.viewDate.year(); - if (target.is('.old')) { - if (month === 0) { - month = 11; - year -= 1; - } else { - month -= 1; - } - } else if (target.is('.new')) { - if (month === 11) { - month = 0; - year += 1; - } else { - month += 1; - } - } - picker.date = moment({ - y: year, - M: month, - d: day, - h: picker.date.hours(), - m: picker.date.minutes(), - s: picker.date.seconds() - } - ); - picker.viewDate = moment({ - y: year, M: month, d: Math.min(28, day) - }); - fillDate(); - set(); - notifyChange(oldDate, e.type); - } - break; - } - } - } - }, - - actions = { - incrementHours: function () { - checkDate('add', 'hours', 1); - }, - - incrementMinutes: function () { - checkDate('add', 'minutes', picker.options.minuteStepping); - }, - - incrementSeconds: function () { - checkDate('add', 'seconds', 1); - }, - - decrementHours: function () { - checkDate('subtract', 'hours', 1); - }, - - decrementMinutes: function () { - checkDate('subtract', 'minutes', picker.options.minuteStepping); - }, - - decrementSeconds: function () { - checkDate('subtract', 'seconds', 1); - }, - - togglePeriod: function () { - var hour = picker.date.hours(); - if (hour >= 12) { - hour -= 12; - } else { - hour += 12; - } - picker.date.hours(hour); - }, - - showPicker: function () { - picker.widget.find('.timepicker > div:not(.timepicker-picker)').hide(); - picker.widget.find('.timepicker .timepicker-picker').show(); - }, - - showHours: function () { - picker.widget.find('.timepicker .timepicker-picker').hide(); - picker.widget.find('.timepicker .timepicker-hours').show(); - }, - - showMinutes: function () { - picker.widget.find('.timepicker .timepicker-picker').hide(); - picker.widget.find('.timepicker .timepicker-minutes').show(); - }, - - showSeconds: function () { - picker.widget.find('.timepicker .timepicker-picker').hide(); - picker.widget.find('.timepicker .timepicker-seconds').show(); - }, - - selectHour: function (e) { - var hour = parseInt($(e.target).text(), 10); - if (!picker.use24hours) { - if (picker.date.hours() >= 12) { - if (hour !== 12) { - hour += 12; - } - } else { - if (hour === 12) { - hour = 0; - } - } - } - picker.date.hours(hour); - actions.showPicker.call(picker); - }, - - selectMinute: function (e) { - picker.date.minutes(parseInt($(e.target).text(), 10)); - actions.showPicker.call(picker); - }, - - selectSecond: function (e) { - picker.date.seconds(parseInt($(e.target).text(), 10)); - actions.showPicker.call(picker); - } - }, - - doAction = function (e) { - var oldDate = moment(picker.date), - action = $(e.currentTarget).data('action'), - rv = actions[action].apply(picker, arguments); - stopEvent(e); - if (!picker.date) { - picker.date = moment({y: 1970}); - } - set(); - fillTime(); - notifyChange(oldDate, e.type); - return rv; - }, - - stopEvent = function (e) { - e.stopPropagation(); - e.preventDefault(); - }, - - keydown = function (e) { - if (e.keyCode === 27) { // allow escape to hide picker - picker.hide(); - } - }, - - change = function (e) { - moment.locale(picker.options.language); - var input = $(e.target), oldDate = moment(picker.date), newDate = moment(input.val(), picker.format, picker.options.useStrict); - if (newDate.isValid() && !isInDisableDates(newDate) && isInEnableDates(newDate)) { - update(); - picker.setValue(newDate); - notifyChange(oldDate, e.type); - set(); - } - else { - picker.viewDate = oldDate; - picker.unset = true; - notifyChange(oldDate, e.type); - notifyError(newDate); - } - }, - - showMode = function (dir) { - if (dir) { - picker.viewMode = Math.max(picker.minViewMode, Math.min(2, picker.viewMode + dir)); - } - picker.widget.find('.datepicker > div').hide().filter('.datepicker-' + dpGlobal.modes[picker.viewMode].clsName).show(); - }, - - attachDatePickerEvents = function () { - var $this, $parent, expanded, closed, collapseData; - picker.widget.on('click', '.datepicker *', $.proxy(click, this)); // this handles date picker clicks - picker.widget.on('click', '[data-action]', $.proxy(doAction, this)); // this handles time picker clicks - picker.widget.on('mousedown', $.proxy(stopEvent, this)); - picker.element.on('keydown', $.proxy(keydown, this)); - if (picker.options.pickDate && picker.options.pickTime) { - picker.widget.on('click.togglePicker', '.accordion-toggle', function (e) { - e.stopPropagation(); - $this = $(this); - $parent = $this.closest('ul'); - expanded = $parent.find('.in'); - closed = $parent.find('.collapse:not(.in)'); - - if (expanded && expanded.length) { - collapseData = expanded.data('collapse'); - if (collapseData && collapseData.transitioning) { - return; - } - expanded.collapse('hide'); - closed.collapse('show'); - $this.find('span').toggleClass(picker.options.icons.time + ' ' + picker.options.icons.date); - if (picker.component) { - picker.component.find('span').toggleClass(picker.options.icons.time + ' ' + picker.options.icons.date); - } - } - }); - } - if (picker.isInput) { - picker.element.on({ - 'click': $.proxy(picker.show, this), - 'focus': $.proxy(picker.show, this), - 'change': $.proxy(change, this), - 'blur': $.proxy(picker.hide, this) - }); - } else { - picker.element.on({ - 'change': $.proxy(change, this) - }, 'input'); - if (picker.component) { - picker.component.on('click', $.proxy(picker.show, this)); - picker.component.on('mousedown', $.proxy(stopEvent, this)); - } else { - picker.element.on('click', $.proxy(picker.show, this)); - } - } - }, - - attachDatePickerGlobalEvents = function () { - $(window).on( - 'resize.datetimepicker' + picker.id, $.proxy(place, this)); - if (!picker.isInput) { - $(document).on( - 'mousedown.datetimepicker' + picker.id, $.proxy(picker.hide, this)); - } - }, - - detachDatePickerEvents = function () { - picker.widget.off('click', '.datepicker *', picker.click); - picker.widget.off('click', '[data-action]'); - picker.widget.off('mousedown', picker.stopEvent); - if (picker.options.pickDate && picker.options.pickTime) { - picker.widget.off('click.togglePicker'); - } - if (picker.isInput) { - picker.element.off({ - 'focus': picker.show, - 'change': change, - 'click': picker.show, - 'blur' : picker.hide - }); - } else { - picker.element.off({ - 'change': change - }, 'input'); - if (picker.component) { - picker.component.off('click', picker.show); - picker.component.off('mousedown', picker.stopEvent); - } else { - picker.element.off('click', picker.show); - } - } - }, - - detachDatePickerGlobalEvents = function () { - $(window).off('resize.datetimepicker' + picker.id); - if (!picker.isInput) { - $(document).off('mousedown.datetimepicker' + picker.id); - } - }, - - isInFixed = function () { - if (picker.element) { - var parents = picker.element.parents(), inFixed = false, i; - for (i = 0; i < parents.length; i++) { - if ($(parents[i]).css('position') === 'fixed') { - inFixed = true; - break; - } - } - return inFixed; - } else { - return false; - } - }, - - set = function () { - moment.locale(picker.options.language); - var formatted = ''; - if (!picker.unset) { - formatted = moment(picker.date).format(picker.format); - } - getPickerInput().val(formatted); - picker.element.data('date', formatted); - if (!picker.options.pickTime) { - picker.hide(); - } - }, - - checkDate = function (direction, unit, amount) { - moment.locale(picker.options.language); - var newDate; - if (direction === 'add') { - newDate = moment(picker.date); - if (newDate.hours() === 23) { - newDate.add(amount, unit); - } - newDate.add(amount, unit); - } - else { - newDate = moment(picker.date).subtract(amount, unit); - } - if (isInDisableDates(moment(newDate.subtract(amount, unit))) || isInDisableDates(newDate)) { - notifyError(newDate.format(picker.format)); - return; - } - - if (direction === 'add') { - picker.date.add(amount, unit); - } - else { - picker.date.subtract(amount, unit); - } - picker.unset = false; - }, - - isInDisableDates = function (date, timeUnit) { - moment.locale(picker.options.language); - var maxDate = moment(picker.options.maxDate, picker.format, picker.options.useStrict), - minDate = moment(picker.options.minDate, picker.format, picker.options.useStrict); - - if (timeUnit) { - maxDate = maxDate.endOf(timeUnit); - minDate = minDate.startOf(timeUnit); - } - - if (date.isAfter(maxDate) || date.isBefore(minDate)) { - return true; - } - if (picker.options.disabledDates === false) { - return false; - } - return picker.options.disabledDates[date.format('YYYY-MM-DD')] === true; - }, - isInEnableDates = function (date) { - moment.locale(picker.options.language); - if (picker.options.enabledDates === false) { - return true; - } - return picker.options.enabledDates[date.format('YYYY-MM-DD')] === true; - }, - - indexGivenDates = function (givenDatesArray) { - // Store given enabledDates and disabledDates as keys. - // This way we can check their existence in O(1) time instead of looping through whole array. - // (for example: picker.options.enabledDates['2014-02-27'] === true) - var givenDatesIndexed = {}, givenDatesCount = 0, i; - for (i = 0; i < givenDatesArray.length; i++) { - if (moment.isMoment(givenDatesArray[i]) || givenDatesArray[i] instanceof Date) { - dDate = moment(givenDatesArray[i]); - } else { - dDate = moment(givenDatesArray[i], picker.format, picker.options.useStrict); - } - if (dDate.isValid()) { - givenDatesIndexed[dDate.format('YYYY-MM-DD')] = true; - givenDatesCount++; - } - } - if (givenDatesCount > 0) { - return givenDatesIndexed; - } - return false; - }, - - padLeft = function (string) { - string = string.toString(); - if (string.length >= 2) { - return string; - } - return '0' + string; - }, - - getTemplate = function () { - var - headTemplate = - '' + - '' + - '‹›' + - '' + - '', - contTemplate = - '', - template = '
' + - '' + headTemplate + '
' + - '
' + - '
' + - '' + headTemplate + contTemplate + '
' + - '
' + - '
' + - '' + headTemplate + contTemplate + '
' + - '
', - ret = ''; - if (picker.options.pickDate && picker.options.pickTime) { - ret = ''; - return ret; - } - if (picker.options.pickTime) { - return ( - '' - ); - } - return ( - '' - ); - }, - - dpGlobal = { - modes: [ - { - clsName: 'days', - navFnc: 'month', - navStep: 1 - }, - { - clsName: 'months', - navFnc: 'year', - navStep: 1 - }, - { - clsName: 'years', - navFnc: 'year', - navStep: 10 - } - ] - }, - - tpGlobal = { - hourTemplate: '', - minuteTemplate: '', - secondTemplate: '' - }; - - tpGlobal.getTemplate = function () { - return ( - '
' + - '' + - '' + - '' + - '' + - '' + - (picker.options.useSeconds ? - '' : '') + - (picker.use24hours ? '' : '') + - '' + - '' + - ' ' + - '' + - ' ' + - (picker.options.useSeconds ? - '' : '') + - (picker.use24hours ? '' : '' + - '') + - '' + - '' + - '' + - '' + - '' + - (picker.options.useSeconds ? - '' : '') + - (picker.use24hours ? '' : '') + - '' + - '
' + (picker.options.useMinutes ? '' : '') + '
' + tpGlobal.hourTemplate + ':' + (picker.options.useMinutes ? tpGlobal.minuteTemplate : '00') + ':' + tpGlobal.secondTemplate + '
' + (picker.options.useMinutes ? '' : '') + '
' + - '
' + - '
' + - '
' + - '
' + - '
' + - '
' + - '
' + - (picker.options.useSeconds ? - '
' : '') - ); - }; - - picker.destroy = function () { - detachDatePickerEvents(); - detachDatePickerGlobalEvents(); - picker.widget.remove(); - picker.element.removeData('DateTimePicker'); - if (picker.component) { - picker.component.removeData('DateTimePicker'); - } - }; - - picker.show = function (e) { - if (getPickerInput().prop('disabled')) { - return; - } - if (picker.options.useCurrent) { - if (getPickerInput().val() === '') { - if (picker.options.minuteStepping !== 1) { - var mDate = moment(), - rInterval = picker.options.minuteStepping; - mDate.minutes((Math.round(mDate.minutes() / rInterval) * rInterval) % 60).seconds(0); - picker.setValue(mDate.format(picker.format)); - } else { - picker.setValue(moment().format(picker.format)); - } - notifyChange('', e.type); - } - } - // if this is a click event on the input field and picker is already open don't hide it - if (e && e.type === 'click' && picker.isInput && picker.widget.hasClass('picker-open')) { - return; - } - if (picker.widget.hasClass('picker-open')) { - picker.widget.hide(); - picker.widget.removeClass('picker-open'); - } - else { - picker.widget.show(); - picker.widget.addClass('picker-open'); - } - picker.height = picker.component ? picker.component.outerHeight() : picker.element.outerHeight(); - place(); - picker.element.trigger({ - type: 'dp.show', - date: moment(picker.date) - }); - ////////MODIFICATION FOR COMPATIBILITY WITH OLD DATEPICKER - picker.element.trigger({ - type: 'show', - date: moment(picker.date).toDate(), - localDate: moment(picker.date).toDate() - }); - ///////END MODIFICATION FOR COMPATIBILTIY - attachDatePickerGlobalEvents(); - if (e) { - stopEvent(e); - } - }; - - picker.disable = function () { - var input = getPickerInput(); - if (input.prop('disabled')) { - return; - } - input.prop('disabled', true); - detachDatePickerEvents(); - }; - - picker.enable = function () { - var input = getPickerInput(); - if (!input.prop('disabled')) { - return; - } - input.prop('disabled', false); - attachDatePickerEvents(); - }; - - picker.hide = function () { - // Ignore event if in the middle of a picker transition - var collapse = picker.widget.find('.collapse'), i, collapseData; - for (i = 0; i < collapse.length; i++) { - collapseData = collapse.eq(i).data('collapse'); - if (collapseData && collapseData.transitioning) { - return; - } - } - picker.widget.hide(); - picker.widget.removeClass('picker-open'); - picker.viewMode = picker.startViewMode; - showMode(); - picker.element.trigger({ - type: 'dp.hide', - date: moment(picker.date) - }); - ////////MODIFICATION FOR COMPATIBILITY WITH OLD DATEPICKER - picker.element.trigger({ - type: 'hide', - date: moment(picker.date).toDate(), - localDate: moment(picker.date).toDate() - }); - ///////END MODIFICATION FOR COMPATIBILTIY - detachDatePickerGlobalEvents(); - }; - - picker.setValue = function (newDate) { - moment.locale(picker.options.language); - if (!newDate) { - picker.unset = true; - set(); - } else { - picker.unset = false; - } - if (!moment.isMoment(newDate)) { - newDate = (newDate instanceof Date) ? moment(newDate) : moment(newDate, picker.format, picker.options.useStrict); - } else { - newDate = newDate.locale(picker.options.language); - } - if (newDate.isValid()) { - picker.date = newDate; - set(); - picker.viewDate = moment({y: picker.date.year(), M: picker.date.month()}); - fillDate(); - fillTime(); - } - else { - notifyError(newDate); - } - }; - - picker.getDate = function () { - if (picker.unset) { - return null; - } - return moment(picker.date); - }; - - picker.setDate = function (date) { - var oldDate = moment(picker.date); - if (!date) { - picker.setValue(null); - } else { - picker.setValue(date); - } - notifyChange(oldDate, 'function'); - }; - - picker.setDisabledDates = function (dates) { - picker.options.disabledDates = indexGivenDates(dates); - if (picker.viewDate) { - update(); - } - }; - - picker.setEnabledDates = function (dates) { - picker.options.enabledDates = indexGivenDates(dates); - if (picker.viewDate) { - update(); - } - }; - - picker.setMaxDate = function (date) { - if (date === undefined) { - return; - } - if (moment.isMoment(date) || date instanceof Date) { - picker.options.maxDate = moment(date); - } else { - picker.options.maxDate = moment(date, picker.format, picker.options.useStrict); - } - if (picker.viewDate) { - update(); - } - }; - - picker.setMinDate = function (date) { - if (date === undefined) { - return; - } - if (moment.isMoment(date) || date instanceof Date) { - picker.options.minDate = moment(date); - } else { - picker.options.minDate = moment(date, picker.format, picker.options.useStrict); - } - if (picker.viewDate) { - update(); - } - }; - - init(); - }; - - $.fn.datetimepicker = function (options) { - - ////////MODIFICATION FOR COMPATIBILITY WITH OLD DATEPICKER - //store the orig method args - var methodArgs = arguments; - ///////END MODIFICATION FOR COMPATIBILTIY - - return this.each(function () { - var $this = $(this), - data = $this.data('DateTimePicker'); - if (!data) { - $this.data('DateTimePicker', new DateTimePicker(this, options)); - } - else { - ////////MODIFICATION FOR COMPATIBILITY WITH OLD DATEPICKER - //this is how the old api was accessed, now it is accessed directly by the - // .data("DateTimePicker") of the element that is assigned. - if (methodArgs.length == 0) { - return; - } - var method = data[methodArgs[0]]; - if (!(typeof method == "function")) { - return; - } - var args = []; - //remove first argument, note that arguments is not a true array! so we need to do this manually - for (var i = 1; i < methodArgs.length; i++) { - args.push(methodArgs[i]); - } - //call the method - method.apply(this, args); - - ///////END MODIFICATION FOR COMPATIBILTIY - } - }); - }; - - $.fn.datetimepicker.defaults = { - format: false, - pickDate: true, - pickTime: true, - useMinutes: true, - useSeconds: false, - useCurrent: true, - calendarWeeks: false, - minuteStepping: 1, - minDate: moment({y: 1900}), - maxDate: moment().add(100, 'y'), - showToday: true, - collapse: true, - language: moment.locale(), - defaultDate: '', - disabledDates: false, - enabledDates: false, - icons: {}, - useStrict: false, - direction: 'auto', - sideBySide: false, - daysOfWeekDisabled: [], - widgetParent: false - }; -})); diff --git a/src/Umbraco.Web.UI.Client/lib/datetimepicker/bootstrap-datetimepicker.min.css b/src/Umbraco.Web.UI.Client/lib/datetimepicker/bootstrap-datetimepicker.min.css deleted file mode 100644 index cf838a3c23..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/datetimepicker/bootstrap-datetimepicker.min.css +++ /dev/null @@ -1,5 +0,0 @@ -/*! - * Datetimepicker for Bootstrap v3 -//! version : 3.1.3 - * https://github.com/Eonasdan/bootstrap-datetimepicker/ - */.bootstrap-datetimepicker-widget{top:0;left:0;width:250px;padding:4px;margin-top:1px;z-index:99999!important;border-radius:4px}.bootstrap-datetimepicker-widget.timepicker-sbs{width:600px}.bootstrap-datetimepicker-widget.bottom:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-bottom:7px solid #ccc;border-bottom-color:rgba(0,0,0,.2);position:absolute;top:-7px;left:7px}.bootstrap-datetimepicker-widget.bottom:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-bottom:6px solid #fff;position:absolute;top:-6px;left:8px}.bootstrap-datetimepicker-widget.top:before{content:'';display:inline-block;border-left:7px solid transparent;border-right:7px solid transparent;border-top:7px solid #ccc;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:-7px;left:6px}.bootstrap-datetimepicker-widget.top:after{content:'';display:inline-block;border-left:6px solid transparent;border-right:6px solid transparent;border-top:6px solid #fff;position:absolute;bottom:-6px;left:7px}.bootstrap-datetimepicker-widget .dow{width:14.2857%}.bootstrap-datetimepicker-widget.pull-right:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.pull-right:after{left:auto;right:7px}.bootstrap-datetimepicker-widget>ul{list-style-type:none;margin:0}.bootstrap-datetimepicker-widget a[data-action]{padding:6px 0}.bootstrap-datetimepicker-widget a[data-action]:active{box-shadow:none}.bootstrap-datetimepicker-widget .timepicker-hour,.bootstrap-datetimepicker-widget .timepicker-minute,.bootstrap-datetimepicker-widget .timepicker-second{width:54px;font-weight:700;font-size:1.2em;margin:0}.bootstrap-datetimepicker-widget button[data-action]{padding:6px}.bootstrap-datetimepicker-widget table[data-hour-format="12"] .separator{width:4px;padding:0;margin:0}.bootstrap-datetimepicker-widget .datepicker>div{display:none}.bootstrap-datetimepicker-widget .picker-switch{text-align:center}.bootstrap-datetimepicker-widget table{width:100%;margin:0}.bootstrap-datetimepicker-widget td,.bootstrap-datetimepicker-widget th{text-align:center;border-radius:4px}.bootstrap-datetimepicker-widget td{height:54px;line-height:54px;width:54px}.bootstrap-datetimepicker-widget td.cw{font-size:10px;height:20px;line-height:20px;color:#777}.bootstrap-datetimepicker-widget td.day{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget td.day:hover,.bootstrap-datetimepicker-widget td.hour:hover,.bootstrap-datetimepicker-widget td.minute:hover,.bootstrap-datetimepicker-widget td.second:hover{background:#eee;cursor:pointer}.bootstrap-datetimepicker-widget td.old,.bootstrap-datetimepicker-widget td.new{color:#777}.bootstrap-datetimepicker-widget td.today{position:relative}.bootstrap-datetimepicker-widget td.today:before{content:'';display:inline-block;border-left:7px solid transparent;border-bottom:7px solid #428bca;border-top-color:rgba(0,0,0,.2);position:absolute;bottom:4px;right:4px}.bootstrap-datetimepicker-widget td.active,.bootstrap-datetimepicker-widget td.active:hover{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td.active.today:before{border-bottom-color:#fff}.bootstrap-datetimepicker-widget td.disabled,.bootstrap-datetimepicker-widget td.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget td span{display:inline-block;width:54px;height:54px;line-height:54px;margin:2px 1.5px;cursor:pointer;border-radius:4px}.bootstrap-datetimepicker-widget td span:hover{background:#eee}.bootstrap-datetimepicker-widget td span.active{background-color:#428bca;color:#fff;text-shadow:0 -1px 0 rgba(0,0,0,.25)}.bootstrap-datetimepicker-widget td span.old{color:#777}.bootstrap-datetimepicker-widget td span.disabled,.bootstrap-datetimepicker-widget td span.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget th{height:20px;line-height:20px;width:20px}.bootstrap-datetimepicker-widget th.picker-switch{width:145px}.bootstrap-datetimepicker-widget th.next,.bootstrap-datetimepicker-widget th.prev{font-size:21px}.bootstrap-datetimepicker-widget th.disabled,.bootstrap-datetimepicker-widget th.disabled:hover{background:0 0;color:#777;cursor:not-allowed}.bootstrap-datetimepicker-widget thead tr:first-child th{cursor:pointer}.bootstrap-datetimepicker-widget thead tr:first-child th:hover{background:#eee}.input-group.date .input-group-addon span{display:block;cursor:pointer;width:16px;height:16px}.bootstrap-datetimepicker-widget.left-oriented:before{left:auto;right:6px}.bootstrap-datetimepicker-widget.left-oriented:after{left:auto;right:7px}.bootstrap-datetimepicker-widget ul.list-unstyled li div.timepicker div.timepicker-picker table.table-condensed tbody>tr>td{padding:0!important}@media screen and (max-width:767px){.bootstrap-datetimepicker-widget.timepicker-sbs{width:283px}} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/slider/bootstrap-slider-custom.css b/src/Umbraco.Web.UI.Client/lib/slider/bootstrap-slider-custom.css deleted file mode 100644 index e7a395d458..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/slider/bootstrap-slider-custom.css +++ /dev/null @@ -1,79 +0,0 @@ -/* set default selection color */ -.slider-track .slider-selection { - background: #89cdef; -} - -/* custom styling for triangle */ -.slider.slider-horizontal .slider-tick.triangle, -.slider.slider-horizontal .slider-handle.triangle { - box-shadow: none; - border-width: 0 12px 15px 12px !important; - margin-left: -12px; - margin-top: -5px !important; -} - -.slider.slider-vertical .slider-tick.triangle, -.slider.slider-vertical .slider-handle.triangle { - box-shadow: none; - border-width: 12px 0 12px 15px !important; - margin-top: -12px; -} - -/* for custom handle remove box-shadow */ -.slider-track .slider-handle.custom { - box-shadow: none; - margin-left: -8px; -} - -/* for custom handle add text-shadow */ -.slider-track .slider-handle.custom::before { - text-shadow: 0 1px 2px rgba(0,0,0,.05); - color: #337ab7; - font-size: 26px; -} - -/* we make each tick a bit more clear */ -.slider .slider-tick { - background-image: -webkit-linear-gradient(top, #e6e6e6 0, #dedede 100%); - background-image: -o-linear-gradient(top, #e6e6e6 0, #dedede 100%); - background-image: linear-gradient(to bottom, #e6e6e6 0, #dedede 100%); -} - -.slider .slider-tick.in-selection { - background-image: -webkit-linear-gradient(top, #89cdef 0, #81bfde 100%); - background-image: -o-linear-gradient(top, #89cdef 0, #81bfde 100%); - background-image: linear-gradient(to bottom, #89cdef 0, #81bfde 100%); -} - -/* horizontal - triangle border-bottom color */ -.slider.slider-horizontal .slider-tick.triangle { - border-bottom-color: #dedede; -} -.slider.slider-horizontal .slider-handle.triangle { - border-bottom-color: #0480be; -} -.slider.slider-horizontal .slider-tick.triangle.in-selection { - border-bottom-color: #81bfde; -} - -/* vertical - triangle border-left color */ -.slider.slider-vertical .slider-tick.triangle { - border-left-color: #dedede; -} -.slider.slider-vertical .slider-handle.triangle { - border-left-color: #0480be; -} -.slider.slider-vertical .slider-tick.triangle.in-selection { - border-left-color: #81bfde; -} - -.slider.slider-horizontal { - width: 400px; - max-width: 100%; -} - -@media only screen and (max-width: 767px) { - .slider.slider-horizontal { - width: 100%; - } -} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/lib/slider/bootstrap-slider.css b/src/Umbraco.Web.UI.Client/lib/slider/bootstrap-slider.css deleted file mode 100644 index aa763b6bb6..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/slider/bootstrap-slider.css +++ /dev/null @@ -1,255 +0,0 @@ -/*! ======================================================= - VERSION 5.2.6 -========================================================= */ -/*! ========================================================= - * bootstrap-slider.js - * - * Maintainers: - * Kyle Kemp - * - Twitter: @seiyria - * - Github: seiyria - * Rohit Kalkur - * - Twitter: @Rovolutionary - * - Github: rovolution - * - * ========================================================= - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ========================================================= */ -.slider { - display: inline-block; - vertical-align: middle; - position: relative; -} -.slider.slider-horizontal { - width: 210px; - height: 20px; -} -.slider.slider-horizontal .slider-track { - height: 10px; - width: 100%; - margin-top: -5px; - top: 50%; - left: 0; -} -.slider.slider-horizontal .slider-selection, -.slider.slider-horizontal .slider-track-low, -.slider.slider-horizontal .slider-track-high { - height: 100%; - top: 0; - bottom: 0; -} -.slider.slider-horizontal .slider-tick, -.slider.slider-horizontal .slider-handle { - margin-left: -10px; - margin-top: -5px; -} -.slider.slider-horizontal .slider-tick.triangle, -.slider.slider-horizontal .slider-handle.triangle { - border-width: 0 10px 10px 10px; - width: 0; - height: 0; - border-bottom-color: #0480be; - margin-top: 0; -} -.slider.slider-horizontal .slider-tick-label-container { - white-space: nowrap; - margin-top: 20px; -} -.slider.slider-horizontal .slider-tick-label-container .slider-tick-label { - padding-top: 4px; - display: inline-block; - text-align: center; -} -.slider.slider-vertical { - height: 210px; - width: 20px; -} -.slider.slider-vertical .slider-track { - width: 10px; - height: 100%; - margin-left: -5px; - left: 50%; - top: 0; -} -.slider.slider-vertical .slider-selection { - width: 100%; - left: 0; - top: 0; - bottom: 0; -} -.slider.slider-vertical .slider-track-low, -.slider.slider-vertical .slider-track-high { - width: 100%; - left: 0; - right: 0; -} -.slider.slider-vertical .slider-tick, -.slider.slider-vertical .slider-handle { - margin-left: -5px; - margin-top: -10px; -} -.slider.slider-vertical .slider-tick.triangle, -.slider.slider-vertical .slider-handle.triangle { - border-width: 10px 0 10px 10px; - width: 1px; - height: 1px; - border-left-color: #0480be; - margin-left: 0; -} -.slider.slider-vertical .slider-tick-label-container { - white-space: nowrap; -} -.slider.slider-vertical .slider-tick-label-container .slider-tick-label { - padding-left: 4px; -} -.slider.slider-disabled .slider-handle { - background-image: -webkit-linear-gradient(top, #dfdfdf 0%, #bebebe 100%); - background-image: -o-linear-gradient(top, #dfdfdf 0%, #bebebe 100%); - background-image: linear-gradient(to bottom, #dfdfdf 0%, #bebebe 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdfdfdf', endColorstr='#ffbebebe', GradientType=0); -} -.slider.slider-disabled .slider-track { - background-image: -webkit-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%); - background-image: -o-linear-gradient(top, #e5e5e5 0%, #e9e9e9 100%); - background-image: linear-gradient(to bottom, #e5e5e5 0%, #e9e9e9 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe5e5e5', endColorstr='#ffe9e9e9', GradientType=0); - cursor: not-allowed; -} -.slider input { - display: none; -} -.slider .tooltip.top { - margin-top: -36px; -} -.slider .tooltip-inner { - white-space: nowrap; -} -.slider .hide { - display: none; -} -.slider-track { - position: absolute; - cursor: pointer; - background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%); - background-image: -o-linear-gradient(top, #f5f5f5 0%, #f9f9f9 100%); - background-image: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff9f9f9', GradientType=0); - -webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); - border-radius: 4px; -} -.slider-selection { - position: absolute; - background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); - background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); - background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - border-radius: 4px; -} -.slider-selection.tick-slider-selection { - background-image: -webkit-linear-gradient(top, #89cdef 0%, #81bfde 100%); - background-image: -o-linear-gradient(top, #89cdef 0%, #81bfde 100%); - background-image: linear-gradient(to bottom, #89cdef 0%, #81bfde 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef', endColorstr='#ff81bfde', GradientType=0); -} -.slider-track-low, -.slider-track-high { - position: absolute; - background: transparent; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - border-radius: 4px; -} -.slider-handle { - position: absolute; - width: 20px; - height: 20px; - background-color: #337ab7; - background-image: -webkit-linear-gradient(top, #149bdf 0%, #0480be 100%); - background-image: -o-linear-gradient(top, #149bdf 0%, #0480be 100%); - background-image: linear-gradient(to bottom, #149bdf 0%, #0480be 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff149bdf', endColorstr='#ff0480be', GradientType=0); - filter: none; - -webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); - border: 0px solid transparent; -} -.slider-handle.round { - border-radius: 50%; -} -.slider-handle.triangle { - background: transparent none; -} -.slider-handle.custom { - background: transparent none; -} -.slider-handle.custom::before { - line-height: 20px; - font-size: 20px; - content: '\2605'; - color: #726204; -} -.slider-tick { - position: absolute; - width: 20px; - height: 20px; - background-image: -webkit-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); - background-image: -o-linear-gradient(top, #f9f9f9 0%, #f5f5f5 100%); - background-image: linear-gradient(to bottom, #f9f9f9 0%, #f5f5f5 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff9f9f9', endColorstr='#fff5f5f5', GradientType=0); - -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.15); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; - filter: none; - opacity: 0.8; - border: 0px solid transparent; -} -.slider-tick.round { - border-radius: 50%; -} -.slider-tick.triangle { - background: transparent none; -} -.slider-tick.custom { - background: transparent none; -} -.slider-tick.custom::before { - line-height: 20px; - font-size: 20px; - content: '\2605'; - color: #726204; -} -.slider-tick.in-selection { - background-image: -webkit-linear-gradient(top, #89cdef 0%, #81bfde 100%); - background-image: -o-linear-gradient(top, #89cdef 0%, #81bfde 100%); - background-image: linear-gradient(to bottom, #89cdef 0%, #81bfde 100%); - background-repeat: repeat-x; - filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff89cdef', endColorstr='#ff81bfde', GradientType=0); - opacity: 1; -} diff --git a/src/Umbraco.Web.UI.Client/lib/slider/js/bootstrap-slider.js b/src/Umbraco.Web.UI.Client/lib/slider/js/bootstrap-slider.js deleted file mode 100644 index 3ae97f4fd6..0000000000 --- a/src/Umbraco.Web.UI.Client/lib/slider/js/bootstrap-slider.js +++ /dev/null @@ -1,1553 +0,0 @@ -/*! ======================================================= - VERSION 5.2.6 -========================================================= */ -/*! ========================================================= - * bootstrap-slider.js - * - * Maintainers: - * Kyle Kemp - * - Twitter: @seiyria - * - Github: seiyria - * Rohit Kalkur - * - Twitter: @Rovolutionary - * - Github: rovolution - * - * ========================================================= - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * ========================================================= */ - - -/** - * Bridget makes jQuery widgets - * v1.0.1 - * MIT license - */ - -(function(root, factory) { - if(typeof define === "function" && define.amd) { - define(["jquery"], factory); - } - else if(typeof module === "object" && module.exports) { - var jQuery; - try { - jQuery = require("jquery"); - } - catch (err) { - jQuery = null; - } - module.exports = factory(jQuery); - } - else { - root.Slider = factory(root.jQuery); - } -}(this, function($) { - // Reference to Slider constructor - var Slider; - - - (function( $ ) { - - 'use strict'; - - // -------------------------- utils -------------------------- // - - var slice = Array.prototype.slice; - - function noop() {} - - // -------------------------- definition -------------------------- // - - function defineBridget( $ ) { - - // bail if no jQuery - if ( !$ ) { - return; - } - - // -------------------------- addOptionMethod -------------------------- // - - /** - * adds option method -> $().plugin('option', {...}) - * @param {Function} PluginClass - constructor class - */ - function addOptionMethod( PluginClass ) { - // don't overwrite original option method - if ( PluginClass.prototype.option ) { - return; - } - - // option setter - PluginClass.prototype.option = function( opts ) { - // bail out if not an object - if ( !$.isPlainObject( opts ) ){ - return; - } - this.options = $.extend( true, this.options, opts ); - }; - } - - - // -------------------------- plugin bridge -------------------------- // - - // helper function for logging errors - // $.error breaks jQuery chaining - var logError = typeof console === 'undefined' ? noop : - function( message ) { - console.error( message ); - }; - - /** - * jQuery plugin bridge, access methods like $elem.plugin('method') - * @param {String} namespace - plugin name - * @param {Function} PluginClass - constructor class - */ - function bridge( namespace, PluginClass ) { - // add to jQuery fn namespace - $.fn[ namespace ] = function( options ) { - if ( typeof options === 'string' ) { - // call plugin method when first argument is a string - // get arguments for method - var args = slice.call( arguments, 1 ); - - for ( var i=0, len = this.length; i < len; i++ ) { - var elem = this[i]; - var instance = $.data( elem, namespace ); - if ( !instance ) { - logError( "cannot call methods on " + namespace + " prior to initialization; " + - "attempted to call '" + options + "'" ); - continue; - } - if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) { - logError( "no such method '" + options + "' for " + namespace + " instance" ); - continue; - } - - // trigger method with arguments - var returnValue = instance[ options ].apply( instance, args); - - // break look and return first value if provided - if ( returnValue !== undefined && returnValue !== instance) { - return returnValue; - } - } - // return this if no return value - return this; - } else { - var objects = this.map( function() { - var instance = $.data( this, namespace ); - if ( instance ) { - // apply options & init - instance.option( options ); - instance._init(); - } else { - // initialize new instance - instance = new PluginClass( this, options ); - $.data( this, namespace, instance ); - } - return $(this); - }); - - if(!objects || objects.length > 1) { - return objects; - } else { - return objects[0]; - } - } - }; - - } - - // -------------------------- bridget -------------------------- // - - /** - * converts a Prototypical class into a proper jQuery plugin - * the class must have a ._init method - * @param {String} namespace - plugin name, used in $().pluginName - * @param {Function} PluginClass - constructor class - */ - $.bridget = function( namespace, PluginClass ) { - addOptionMethod( PluginClass ); - bridge( namespace, PluginClass ); - }; - - return $.bridget; - - } - - // get jquery from browser global - defineBridget( $ ); - - })( $ ); - - - /************************************************* - - BOOTSTRAP-SLIDER SOURCE CODE - - **************************************************/ - - (function($) { - - var ErrorMsgs = { - formatInvalidInputErrorMsg : function(input) { - return "Invalid input value '" + input + "' passed in"; - }, - callingContextNotSliderInstance : "Calling context element does not have instance of Slider bound to it. Check your code to make sure the JQuery object returned from the call to the slider() initializer is calling the method" - }; - - var SliderScale = { - linear: { - toValue: function(percentage) { - var rawValue = percentage/100 * (this.options.max - this.options.min); - if (this.options.ticks_positions.length > 0) { - var minv, maxv, minp, maxp = 0; - for (var i = 0; i < this.options.ticks_positions.length; i++) { - if (percentage <= this.options.ticks_positions[i]) { - minv = (i > 0) ? this.options.ticks[i-1] : 0; - minp = (i > 0) ? this.options.ticks_positions[i-1] : 0; - maxv = this.options.ticks[i]; - maxp = this.options.ticks_positions[i]; - - break; - } - } - if (i > 0) { - var partialPercentage = (percentage - minp) / (maxp - minp); - rawValue = minv + partialPercentage * (maxv - minv); - } - } - - var value = this.options.min + Math.round(rawValue / this.options.step) * this.options.step; - if (value < this.options.min) { - return this.options.min; - } else if (value > this.options.max) { - return this.options.max; - } else { - return value; - } - }, - toPercentage: function(value) { - if (this.options.max === this.options.min) { - return 0; - } - - if (this.options.ticks_positions.length > 0) { - var minv, maxv, minp, maxp = 0; - for (var i = 0; i < this.options.ticks.length; i++) { - if (value <= this.options.ticks[i]) { - minv = (i > 0) ? this.options.ticks[i-1] : 0; - minp = (i > 0) ? this.options.ticks_positions[i-1] : 0; - maxv = this.options.ticks[i]; - maxp = this.options.ticks_positions[i]; - - break; - } - } - if (i > 0) { - var partialPercentage = (value - minv) / (maxv - minv); - return minp + partialPercentage * (maxp - minp); - } - } - - return 100 * (value - this.options.min) / (this.options.max - this.options.min); - } - }, - - logarithmic: { - /* Based on http://stackoverflow.com/questions/846221/logarithmic-slider */ - toValue: function(percentage) { - var min = (this.options.min === 0) ? 0 : Math.log(this.options.min); - var max = Math.log(this.options.max); - var value = Math.exp(min + (max - min) * percentage / 100); - value = this.options.min + Math.round((value - this.options.min) / this.options.step) * this.options.step; - /* Rounding to the nearest step could exceed the min or - * max, so clip to those values. */ - if (value < this.options.min) { - return this.options.min; - } else if (value > this.options.max) { - return this.options.max; - } else { - return value; - } - }, - toPercentage: function(value) { - if (this.options.max === this.options.min) { - return 0; - } else { - var max = Math.log(this.options.max); - var min = this.options.min === 0 ? 0 : Math.log(this.options.min); - var v = value === 0 ? 0 : Math.log(value); - return 100 * (v - min) / (max - min); - } - } - } - }; - - - /************************************************* - - CONSTRUCTOR - - **************************************************/ - Slider = function(element, options) { - createNewSlider.call(this, element, options); - return this; - }; - - function createNewSlider(element, options) { - - /* - The internal state object is used to store data about the current 'state' of slider. - - This includes values such as the `value`, `enabled`, etc... - */ - this._state = { - value: null, - enabled: null, - offset: null, - size: null, - percentage: null, - inDrag: false, - over: false - }; - - - if(typeof element === "string") { - this.element = document.querySelector(element); - } else if(element instanceof HTMLElement) { - this.element = element; - } - - /************************************************* - - Process Options - - **************************************************/ - options = options ? options : {}; - var optionTypes = Object.keys(this.defaultOptions); - - for(var i = 0; i < optionTypes.length; i++) { - var optName = optionTypes[i]; - - // First check if an option was passed in via the constructor - var val = options[optName]; - // If no data attrib, then check data atrributes - val = (typeof val !== 'undefined') ? val : getDataAttrib(this.element, optName); - // Finally, if nothing was specified, use the defaults - val = (val !== null) ? val : this.defaultOptions[optName]; - - // Set all options on the instance of the Slider - if(!this.options) { - this.options = {}; - } - this.options[optName] = val; - } - - /* - Validate `tooltip_position` against 'orientation` - - if `tooltip_position` is incompatible with orientation, swith it to a default compatible with specified `orientation` - -- default for "vertical" -> "right" - -- default for "horizontal" -> "left" - */ - if(this.options.orientation === "vertical" && (this.options.tooltip_position === "top" || this.options.tooltip_position === "bottom")) { - - this.options.tooltip_position = "right"; - - } - else if(this.options.orientation === "horizontal" && (this.options.tooltip_position === "left" || this.options.tooltip_position === "right")) { - - this.options.tooltip_position = "top"; - - } - - function getDataAttrib(element, optName) { - var dataName = "data-slider-" + optName.replace(/_/g, '-'); - var dataValString = element.getAttribute(dataName); - - try { - return JSON.parse(dataValString); - } - catch(err) { - return dataValString; - } - } - - /************************************************* - - Create Markup - - **************************************************/ - - var origWidth = this.element.style.width; - var updateSlider = false; - var parent = this.element.parentNode; - var sliderTrackSelection; - var sliderTrackLow, sliderTrackHigh; - var sliderMinHandle; - var sliderMaxHandle; - - if (this.sliderElem) { - updateSlider = true; - } else { - /* Create elements needed for slider */ - this.sliderElem = document.createElement("div"); - this.sliderElem.className = "slider"; - - /* Create slider track elements */ - var sliderTrack = document.createElement("div"); - sliderTrack.className = "slider-track"; - - sliderTrackLow = document.createElement("div"); - sliderTrackLow.className = "slider-track-low"; - - sliderTrackSelection = document.createElement("div"); - sliderTrackSelection.className = "slider-selection"; - - sliderTrackHigh = document.createElement("div"); - sliderTrackHigh.className = "slider-track-high"; - - sliderMinHandle = document.createElement("div"); - sliderMinHandle.className = "slider-handle min-slider-handle"; - - sliderMaxHandle = document.createElement("div"); - sliderMaxHandle.className = "slider-handle max-slider-handle"; - - sliderTrack.appendChild(sliderTrackLow); - sliderTrack.appendChild(sliderTrackSelection); - sliderTrack.appendChild(sliderTrackHigh); - - /* Create ticks */ - this.ticks = []; - if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) { - for (i = 0; i < this.options.ticks.length; i++) { - var tick = document.createElement('div'); - tick.className = 'slider-tick'; - - this.ticks.push(tick); - sliderTrack.appendChild(tick); - } - - sliderTrackSelection.className += " tick-slider-selection"; - } - - sliderTrack.appendChild(sliderMinHandle); - sliderTrack.appendChild(sliderMaxHandle); - - this.tickLabels = []; - if (Array.isArray(this.options.ticks_labels) && this.options.ticks_labels.length > 0) { - this.tickLabelContainer = document.createElement('div'); - this.tickLabelContainer.className = 'slider-tick-label-container'; - - for (i = 0; i < this.options.ticks_labels.length; i++) { - var label = document.createElement('div'); - var noTickPositionsSpecified = this.options.ticks_positions.length === 0; - var tickLabelsIndex = (this.options.reversed && noTickPositionsSpecified) ? (this.options.ticks_labels.length - (i + 1)) : i; - label.className = 'slider-tick-label'; - label.innerHTML = this.options.ticks_labels[tickLabelsIndex]; - - this.tickLabels.push(label); - this.tickLabelContainer.appendChild(label); - } - } - - - var createAndAppendTooltipSubElements = function(tooltipElem) { - var arrow = document.createElement("div"); - arrow.className = "tooltip-arrow"; - - var inner = document.createElement("div"); - inner.className = "tooltip-inner"; - - tooltipElem.appendChild(arrow); - tooltipElem.appendChild(inner); - - }; - - /* Create tooltip elements */ - var sliderTooltip = document.createElement("div"); - sliderTooltip.className = "tooltip tooltip-main"; - createAndAppendTooltipSubElements(sliderTooltip); - - var sliderTooltipMin = document.createElement("div"); - sliderTooltipMin.className = "tooltip tooltip-min"; - createAndAppendTooltipSubElements(sliderTooltipMin); - - var sliderTooltipMax = document.createElement("div"); - sliderTooltipMax.className = "tooltip tooltip-max"; - createAndAppendTooltipSubElements(sliderTooltipMax); - - - /* Append components to sliderElem */ - this.sliderElem.appendChild(sliderTrack); - this.sliderElem.appendChild(sliderTooltip); - this.sliderElem.appendChild(sliderTooltipMin); - this.sliderElem.appendChild(sliderTooltipMax); - - if (this.tickLabelContainer) { - this.sliderElem.appendChild(this.tickLabelContainer); - } - - /* Append slider element to parent container, right before the original element */ - parent.insertBefore(this.sliderElem, this.element); - - /* Hide original element */ - this.element.style.display = "none"; - } - /* If JQuery exists, cache JQ references */ - if($) { - this.$element = $(this.element); - this.$sliderElem = $(this.sliderElem); - } - - /************************************************* - - Setup - - **************************************************/ - this.eventToCallbackMap = {}; - this.sliderElem.id = this.options.id; - - this.touchCapable = 'ontouchstart' in window || (window.DocumentTouch && document instanceof window.DocumentTouch); - - this.tooltip = this.sliderElem.querySelector('.tooltip-main'); - this.tooltipInner = this.tooltip.querySelector('.tooltip-inner'); - - this.tooltip_min = this.sliderElem.querySelector('.tooltip-min'); - this.tooltipInner_min = this.tooltip_min.querySelector('.tooltip-inner'); - - this.tooltip_max = this.sliderElem.querySelector('.tooltip-max'); - this.tooltipInner_max= this.tooltip_max.querySelector('.tooltip-inner'); - - if (SliderScale[this.options.scale]) { - this.options.scale = SliderScale[this.options.scale]; - } - - if (updateSlider === true) { - // Reset classes - this._removeClass(this.sliderElem, 'slider-horizontal'); - this._removeClass(this.sliderElem, 'slider-vertical'); - this._removeClass(this.tooltip, 'hide'); - this._removeClass(this.tooltip_min, 'hide'); - this._removeClass(this.tooltip_max, 'hide'); - - // Undo existing inline styles for track - ["left", "top", "width", "height"].forEach(function(prop) { - this._removeProperty(this.trackLow, prop); - this._removeProperty(this.trackSelection, prop); - this._removeProperty(this.trackHigh, prop); - }, this); - - // Undo inline styles on handles - [this.handle1, this.handle2].forEach(function(handle) { - this._removeProperty(handle, 'left'); - this._removeProperty(handle, 'top'); - }, this); - - // Undo inline styles and classes on tooltips - [this.tooltip, this.tooltip_min, this.tooltip_max].forEach(function(tooltip) { - this._removeProperty(tooltip, 'left'); - this._removeProperty(tooltip, 'top'); - this._removeProperty(tooltip, 'margin-left'); - this._removeProperty(tooltip, 'margin-top'); - - this._removeClass(tooltip, 'right'); - this._removeClass(tooltip, 'top'); - }, this); - } - - if(this.options.orientation === 'vertical') { - this._addClass(this.sliderElem,'slider-vertical'); - this.stylePos = 'top'; - this.mousePos = 'pageY'; - this.sizePos = 'offsetHeight'; - } else { - this._addClass(this.sliderElem, 'slider-horizontal'); - this.sliderElem.style.width = origWidth; - this.options.orientation = 'horizontal'; - this.stylePos = 'left'; - this.mousePos = 'pageX'; - this.sizePos = 'offsetWidth'; - - } - this._setTooltipPosition(); - /* In case ticks are specified, overwrite the min and max bounds */ - if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) { - this.options.max = Math.max.apply(Math, this.options.ticks); - this.options.min = Math.min.apply(Math, this.options.ticks); - } - - if (Array.isArray(this.options.value)) { - this.options.range = true; - this._state.value = this.options.value; - } - else if (this.options.range) { - // User wants a range, but value is not an array - this._state.value = [this.options.value, this.options.max]; - } - else { - this._state.value = this.options.value; - } - - this.trackLow = sliderTrackLow || this.trackLow; - this.trackSelection = sliderTrackSelection || this.trackSelection; - this.trackHigh = sliderTrackHigh || this.trackHigh; - - if (this.options.selection === 'none') { - this._addClass(this.trackLow, 'hide'); - this._addClass(this.trackSelection, 'hide'); - this._addClass(this.trackHigh, 'hide'); - } - - this.handle1 = sliderMinHandle || this.handle1; - this.handle2 = sliderMaxHandle || this.handle2; - - if (updateSlider === true) { - // Reset classes - this._removeClass(this.handle1, 'round triangle'); - this._removeClass(this.handle2, 'round triangle hide'); - - for (i = 0; i < this.ticks.length; i++) { - this._removeClass(this.ticks[i], 'round triangle hide'); - } - } - - var availableHandleModifiers = ['round', 'triangle', 'custom']; - var isValidHandleType = availableHandleModifiers.indexOf(this.options.handle) !== -1; - if (isValidHandleType) { - this._addClass(this.handle1, this.options.handle); - this._addClass(this.handle2, this.options.handle); - - for (i = 0; i < this.ticks.length; i++) { - this._addClass(this.ticks[i], this.options.handle); - } - } - - this._state.offset = this._offset(this.sliderElem); - this._state.size = this.sliderElem[this.sizePos]; - this.setValue(this._state.value); - - /****************************************** - - Bind Event Listeners - - ******************************************/ - - // Bind keyboard handlers - this.handle1Keydown = this._keydown.bind(this, 0); - this.handle1.addEventListener("keydown", this.handle1Keydown, false); - - this.handle2Keydown = this._keydown.bind(this, 1); - this.handle2.addEventListener("keydown", this.handle2Keydown, false); - - this.mousedown = this._mousedown.bind(this); - if (this.touchCapable) { - // Bind touch handlers - this.sliderElem.addEventListener("touchstart", this.mousedown, false); - } - this.sliderElem.addEventListener("mousedown", this.mousedown, false); - - - // Bind tooltip-related handlers - if(this.options.tooltip === 'hide') { - this._addClass(this.tooltip, 'hide'); - this._addClass(this.tooltip_min, 'hide'); - this._addClass(this.tooltip_max, 'hide'); - } - else if(this.options.tooltip === 'always') { - this._showTooltip(); - this._alwaysShowTooltip = true; - } - else { - this.showTooltip = this._showTooltip.bind(this); - this.hideTooltip = this._hideTooltip.bind(this); - - this.sliderElem.addEventListener("mouseenter", this.showTooltip, false); - this.sliderElem.addEventListener("mouseleave", this.hideTooltip, false); - - this.handle1.addEventListener("focus", this.showTooltip, false); - this.handle1.addEventListener("blur", this.hideTooltip, false); - - this.handle2.addEventListener("focus", this.showTooltip, false); - this.handle2.addEventListener("blur", this.hideTooltip, false); - } - - if(this.options.enabled) { - this.enable(); - } else { - this.disable(); - } - } - - - - /************************************************* - - INSTANCE PROPERTIES/METHODS - - - Any methods bound to the prototype are considered - part of the plugin's `public` interface - - **************************************************/ - Slider.prototype = { - _init: function() {}, // NOTE: Must exist to support bridget - - constructor: Slider, - - defaultOptions: { - id: "", - min: 0, - max: 10, - step: 1, - precision: 0, - orientation: 'horizontal', - value: 5, - range: false, - selection: 'before', - tooltip: 'show', - tooltip_split: false, - handle: 'round', - reversed: false, - enabled: true, - formatter: function(val) { - if (Array.isArray(val)) { - return val[0] + " : " + val[1]; - } else { - return val; - } - }, - natural_arrow_keys: false, - ticks: [], - ticks_positions: [], - ticks_labels: [], - ticks_snap_bounds: 0, - scale: 'linear', - focus: false, - tooltip_position: null - }, - - getElement: function() { - return this.sliderElem; - }, - - getValue: function() { - if (this.options.range) { - return this._state.value; - } - else { - return this._state.value[0]; - } - }, - - setValue: function(val, triggerSlideEvent, triggerChangeEvent) { - if (!val) { - val = 0; - } - var oldValue = this.getValue(); - this._state.value = this._validateInputValue(val); - var applyPrecision = this._applyPrecision.bind(this); - - if (this.options.range) { - this._state.value[0] = applyPrecision(this._state.value[0]); - this._state.value[1] = applyPrecision(this._state.value[1]); - - this._state.value[0] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[0])); - this._state.value[1] = Math.max(this.options.min, Math.min(this.options.max, this._state.value[1])); - } - else { - this._state.value = applyPrecision(this._state.value); - this._state.value = [ Math.max(this.options.min, Math.min(this.options.max, this._state.value))]; - this._addClass(this.handle2, 'hide'); - if (this.options.selection === 'after') { - this._state.value[1] = this.options.max; - } else { - this._state.value[1] = this.options.min; - } - } - - if (this.options.max > this.options.min) { - this._state.percentage = [ - this._toPercentage(this._state.value[0]), - this._toPercentage(this._state.value[1]), - this.options.step * 100 / (this.options.max - this.options.min) - ]; - } else { - this._state.percentage = [0, 0, 100]; - } - - this._layout(); - var newValue = this.options.range ? this._state.value : this._state.value[0]; - - if(triggerSlideEvent === true) { - this._trigger('slide', newValue); - } - if( (oldValue !== newValue) && (triggerChangeEvent === true) ) { - this._trigger('change', { - oldValue: oldValue, - newValue: newValue - }); - } - this._setDataVal(newValue); - - return this; - }, - - destroy: function(){ - // Remove event handlers on slider elements - this._removeSliderEventHandlers(); - - // Remove the slider from the DOM - this.sliderElem.parentNode.removeChild(this.sliderElem); - /* Show original element */ - this.element.style.display = ""; - - // Clear out custom event bindings - this._cleanUpEventCallbacksMap(); - - // Remove data values - this.element.removeAttribute("data"); - - // Remove JQuery handlers/data - if($) { - this._unbindJQueryEventHandlers(); - this.$element.removeData('slider'); - } - }, - - disable: function() { - this._state.enabled = false; - this.handle1.removeAttribute("tabindex"); - this.handle2.removeAttribute("tabindex"); - this._addClass(this.sliderElem, 'slider-disabled'); - this._trigger('slideDisabled'); - - return this; - }, - - enable: function() { - this._state.enabled = true; - this.handle1.setAttribute("tabindex", 0); - this.handle2.setAttribute("tabindex", 0); - this._removeClass(this.sliderElem, 'slider-disabled'); - this._trigger('slideEnabled'); - - return this; - }, - - toggle: function() { - if(this._state.enabled) { - this.disable(); - } else { - this.enable(); - } - return this; - }, - - isEnabled: function() { - return this._state.enabled; - }, - - on: function(evt, callback) { - this._bindNonQueryEventHandler(evt, callback); - return this; - }, - - off: function(evt, callback) { - if($) { - this.$element.off(evt, callback); - this.$sliderElem.off(evt, callback); - } else { - this._unbindNonQueryEventHandler(evt, callback); - } - }, - - getAttribute: function(attribute) { - if(attribute) { - return this.options[attribute]; - } else { - return this.options; - } - }, - - setAttribute: function(attribute, value) { - this.options[attribute] = value; - return this; - }, - - refresh: function() { - this._removeSliderEventHandlers(); - createNewSlider.call(this, this.element, this.options); - if($) { - // Bind new instance of slider to the element - $.data(this.element, 'slider', this); - } - return this; - }, - - relayout: function() { - this._layout(); - return this; - }, - - /******************************+ - - HELPERS - - - Any method that is not part of the public interface. - - Place it underneath this comment block and write its signature like so: - - _fnName : function() {...} - - ********************************/ - _removeSliderEventHandlers: function() { - // Remove keydown event listeners - this.handle1.removeEventListener("keydown", this.handle1Keydown, false); - this.handle2.removeEventListener("keydown", this.handle2Keydown, false); - - if (this.showTooltip) { - this.handle1.removeEventListener("focus", this.showTooltip, false); - this.handle2.removeEventListener("focus", this.showTooltip, false); - } - if (this.hideTooltip) { - this.handle1.removeEventListener("blur", this.hideTooltip, false); - this.handle2.removeEventListener("blur", this.hideTooltip, false); - } - - // Remove event listeners from sliderElem - if (this.showTooltip) { - this.sliderElem.removeEventListener("mouseenter", this.showTooltip, false); - } - if (this.hideTooltip) { - this.sliderElem.removeEventListener("mouseleave", this.hideTooltip, false); - } - this.sliderElem.removeEventListener("touchstart", this.mousedown, false); - this.sliderElem.removeEventListener("mousedown", this.mousedown, false); - }, - _bindNonQueryEventHandler: function(evt, callback) { - if(this.eventToCallbackMap[evt] === undefined) { - this.eventToCallbackMap[evt] = []; - } - this.eventToCallbackMap[evt].push(callback); - }, - _unbindNonQueryEventHandler: function(evt, callback) { - var callbacks = this.eventToCallbackMap[evt]; - if(callbacks !== undefined) { - for (var i = 0; i < callbacks.length; i++) { - if (callbacks[i] === callback) { - callbacks.splice(i, 1); - break; - } - } - } - }, - _cleanUpEventCallbacksMap: function() { - var eventNames = Object.keys(this.eventToCallbackMap); - for(var i = 0; i < eventNames.length; i++) { - var eventName = eventNames[i]; - this.eventToCallbackMap[eventName] = null; - } - }, - _showTooltip: function() { - if (this.options.tooltip_split === false ){ - this._addClass(this.tooltip, 'in'); - this.tooltip_min.style.display = 'none'; - this.tooltip_max.style.display = 'none'; - } else { - this._addClass(this.tooltip_min, 'in'); - this._addClass(this.tooltip_max, 'in'); - this.tooltip.style.display = 'none'; - } - this._state.over = true; - }, - _hideTooltip: function() { - if (this._state.inDrag === false && this.alwaysShowTooltip !== true) { - this._removeClass(this.tooltip, 'in'); - this._removeClass(this.tooltip_min, 'in'); - this._removeClass(this.tooltip_max, 'in'); - } - this._state.over = false; - }, - _layout: function() { - var positionPercentages; - - if(this.options.reversed) { - positionPercentages = [ 100 - this._state.percentage[0], this.options.range ? 100 - this._state.percentage[1] : this._state.percentage[1]]; - } - else { - positionPercentages = [ this._state.percentage[0], this._state.percentage[1] ]; - } - - this.handle1.style[this.stylePos] = positionPercentages[0]+'%'; - this.handle2.style[this.stylePos] = positionPercentages[1]+'%'; - - /* Position ticks and labels */ - if (Array.isArray(this.options.ticks) && this.options.ticks.length > 0) { - - var styleSize = this.options.orientation === 'vertical' ? 'height' : 'width'; - var styleMargin = this.options.orientation === 'vertical' ? 'marginTop' : 'marginLeft'; - var labelSize = this._state.size / (this.options.ticks.length - 1); - - if (this.tickLabelContainer) { - var extraMargin = 0; - if (this.options.ticks_positions.length === 0) { - if (this.options.orientation !== 'vertical') { - this.tickLabelContainer.style[styleMargin] = -labelSize/2 + 'px'; - } - - extraMargin = this.tickLabelContainer.offsetHeight; - } else { - /* Chidren are position absolute, calculate height by finding the max offsetHeight of a child */ - for (i = 0 ; i < this.tickLabelContainer.childNodes.length; i++) { - if (this.tickLabelContainer.childNodes[i].offsetHeight > extraMargin) { - extraMargin = this.tickLabelContainer.childNodes[i].offsetHeight; - } - } - } - if (this.options.orientation === 'horizontal') { - this.sliderElem.style.marginBottom = extraMargin + 'px'; - } - } - for (var i = 0; i < this.options.ticks.length; i++) { - - var percentage = this.options.ticks_positions[i] || this._toPercentage(this.options.ticks[i]); - - if (this.options.reversed) { - percentage = 100 - percentage; - } - - this.ticks[i].style[this.stylePos] = percentage + '%'; - - /* Set class labels to denote whether ticks are in the selection */ - this._removeClass(this.ticks[i], 'in-selection'); - if (!this.options.range) { - if (this.options.selection === 'after' && percentage >= positionPercentages[0]){ - this._addClass(this.ticks[i], 'in-selection'); - } else if (this.options.selection === 'before' && percentage <= positionPercentages[0]) { - this._addClass(this.ticks[i], 'in-selection'); - } - } else if (percentage >= positionPercentages[0] && percentage <= positionPercentages[1]) { - this._addClass(this.ticks[i], 'in-selection'); - } - - if (this.tickLabels[i]) { - this.tickLabels[i].style[styleSize] = labelSize + 'px'; - - if (this.options.orientation !== 'vertical' && this.options.ticks_positions[i] !== undefined) { - this.tickLabels[i].style.position = 'absolute'; - this.tickLabels[i].style[this.stylePos] = percentage + '%'; - this.tickLabels[i].style[styleMargin] = -labelSize/2 + 'px'; - } else if (this.options.orientation === 'vertical') { - this.tickLabels[i].style['marginLeft'] = this.sliderElem.offsetWidth + 'px'; - this.tickLabelContainer.style['marginTop'] = this.sliderElem.offsetWidth / 2 * -1 + 'px'; - } - } - } - } - - var formattedTooltipVal; - - if (this.options.range) { - formattedTooltipVal = this.options.formatter(this._state.value); - this._setText(this.tooltipInner, formattedTooltipVal); - this.tooltip.style[this.stylePos] = (positionPercentages[1] + positionPercentages[0])/2 + '%'; - - if (this.options.orientation === 'vertical') { - this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px'); - } else { - this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px'); - } - - if (this.options.orientation === 'vertical') { - this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px'); - } else { - this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px'); - } - - var innerTooltipMinText = this.options.formatter(this._state.value[0]); - this._setText(this.tooltipInner_min, innerTooltipMinText); - - var innerTooltipMaxText = this.options.formatter(this._state.value[1]); - this._setText(this.tooltipInner_max, innerTooltipMaxText); - - this.tooltip_min.style[this.stylePos] = positionPercentages[0] + '%'; - - if (this.options.orientation === 'vertical') { - this._css(this.tooltip_min, 'margin-top', -this.tooltip_min.offsetHeight / 2 + 'px'); - } else { - this._css(this.tooltip_min, 'margin-left', -this.tooltip_min.offsetWidth / 2 + 'px'); - } - - this.tooltip_max.style[this.stylePos] = positionPercentages[1] + '%'; - - if (this.options.orientation === 'vertical') { - this._css(this.tooltip_max, 'margin-top', -this.tooltip_max.offsetHeight / 2 + 'px'); - } else { - this._css(this.tooltip_max, 'margin-left', -this.tooltip_max.offsetWidth / 2 + 'px'); - } - } else { - formattedTooltipVal = this.options.formatter(this._state.value[0]); - this._setText(this.tooltipInner, formattedTooltipVal); - - this.tooltip.style[this.stylePos] = positionPercentages[0] + '%'; - if (this.options.orientation === 'vertical') { - this._css(this.tooltip, 'margin-top', -this.tooltip.offsetHeight / 2 + 'px'); - } else { - this._css(this.tooltip, 'margin-left', -this.tooltip.offsetWidth / 2 + 'px'); - } - } - - if (this.options.orientation === 'vertical') { - this.trackLow.style.top = '0'; - this.trackLow.style.height = Math.min(positionPercentages[0], positionPercentages[1]) +'%'; - - this.trackSelection.style.top = Math.min(positionPercentages[0], positionPercentages[1]) +'%'; - this.trackSelection.style.height = Math.abs(positionPercentages[0] - positionPercentages[1]) +'%'; - - this.trackHigh.style.bottom = '0'; - this.trackHigh.style.height = (100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1])) +'%'; - } - else { - this.trackLow.style.left = '0'; - this.trackLow.style.width = Math.min(positionPercentages[0], positionPercentages[1]) +'%'; - - this.trackSelection.style.left = Math.min(positionPercentages[0], positionPercentages[1]) +'%'; - this.trackSelection.style.width = Math.abs(positionPercentages[0] - positionPercentages[1]) +'%'; - - this.trackHigh.style.right = '0'; - this.trackHigh.style.width = (100 - Math.min(positionPercentages[0], positionPercentages[1]) - Math.abs(positionPercentages[0] - positionPercentages[1])) +'%'; - - var offset_min = this.tooltip_min.getBoundingClientRect(); - var offset_max = this.tooltip_max.getBoundingClientRect(); - - if (offset_min.right > offset_max.left) { - this._removeClass(this.tooltip_max, 'top'); - this._addClass(this.tooltip_max, 'bottom'); - this.tooltip_max.style.top = 18 + 'px'; - } else { - this._removeClass(this.tooltip_max, 'bottom'); - this._addClass(this.tooltip_max, 'top'); - this.tooltip_max.style.top = this.tooltip_min.style.top; - } - } - }, - _removeProperty: function(element, prop) { - if (element.style.removeProperty) { - element.style.removeProperty(prop); - } else { - element.style.removeAttribute(prop); - } - }, - _mousedown: function(ev) { - if(!this._state.enabled) { - return false; - } - - this._state.offset = this._offset(this.sliderElem); - this._state.size = this.sliderElem[this.sizePos]; - - var percentage = this._getPercentage(ev); - - if (this.options.range) { - var diff1 = Math.abs(this._state.percentage[0] - percentage); - var diff2 = Math.abs(this._state.percentage[1] - percentage); - this._state.dragged = (diff1 < diff2) ? 0 : 1; - } else { - this._state.dragged = 0; - } - - this._state.percentage[this._state.dragged] = percentage; - this._layout(); - - if (this.touchCapable) { - document.removeEventListener("touchmove", this.mousemove, false); - document.removeEventListener("touchend", this.mouseup, false); - } - - if(this.mousemove){ - document.removeEventListener("mousemove", this.mousemove, false); - } - if(this.mouseup){ - document.removeEventListener("mouseup", this.mouseup, false); - } - - this.mousemove = this._mousemove.bind(this); - this.mouseup = this._mouseup.bind(this); - - if (this.touchCapable) { - // Touch: Bind touch events: - document.addEventListener("touchmove", this.mousemove, false); - document.addEventListener("touchend", this.mouseup, false); - } - // Bind mouse events: - document.addEventListener("mousemove", this.mousemove, false); - document.addEventListener("mouseup", this.mouseup, false); - - this._state.inDrag = true; - var newValue = this._calculateValue(); - - this._trigger('slideStart', newValue); - - this._setDataVal(newValue); - this.setValue(newValue, false, true); - - this._pauseEvent(ev); - - if (this.options.focus) { - this._triggerFocusOnHandle(this._state.dragged); - } - - return true; - }, - _triggerFocusOnHandle: function(handleIdx) { - if(handleIdx === 0) { - this.handle1.focus(); - } - if(handleIdx === 1) { - this.handle2.focus(); - } - }, - _keydown: function(handleIdx, ev) { - if(!this._state.enabled) { - return false; - } - - var dir; - switch (ev.keyCode) { - case 37: // left - case 40: // down - dir = -1; - break; - case 39: // right - case 38: // up - dir = 1; - break; - } - if (!dir) { - return; - } - - // use natural arrow keys instead of from min to max - if (this.options.natural_arrow_keys) { - var ifVerticalAndNotReversed = (this.options.orientation === 'vertical' && !this.options.reversed); - var ifHorizontalAndReversed = (this.options.orientation === 'horizontal' && this.options.reversed); - - if (ifVerticalAndNotReversed || ifHorizontalAndReversed) { - dir = -dir; - } - } - - var val = this._state.value[handleIdx] + dir * this.options.step; - if (this.options.range) { - val = [ (!handleIdx) ? val : this._state.value[0], - ( handleIdx) ? val : this._state.value[1]]; - } - - this._trigger('slideStart', val); - this._setDataVal(val); - this.setValue(val, true, true); - - this._setDataVal(val); - this._trigger('slideStop', val); - this._layout(); - - this._pauseEvent(ev); - - return false; - }, - _pauseEvent: function(ev) { - if(ev.stopPropagation) { - ev.stopPropagation(); - } - if(ev.preventDefault) { - ev.preventDefault(); - } - ev.cancelBubble=true; - ev.returnValue=false; - }, - _mousemove: function(ev) { - if(!this._state.enabled) { - return false; - } - - var percentage = this._getPercentage(ev); - this._adjustPercentageForRangeSliders(percentage); - this._state.percentage[this._state.dragged] = percentage; - this._layout(); - - var val = this._calculateValue(true); - this.setValue(val, true, true); - - return false; - }, - _adjustPercentageForRangeSliders: function(percentage) { - if (this.options.range) { - var precision = this._getNumDigitsAfterDecimalPlace(percentage); - precision = precision ? precision - 1 : 0; - var percentageWithAdjustedPrecision = this._applyToFixedAndParseFloat(percentage, precision); - if (this._state.dragged === 0 && this._applyToFixedAndParseFloat(this._state.percentage[1], precision) < percentageWithAdjustedPrecision) { - this._state.percentage[0] = this._state.percentage[1]; - this._state.dragged = 1; - } else if (this._state.dragged === 1 && this._applyToFixedAndParseFloat(this._state.percentage[0], precision) > percentageWithAdjustedPrecision) { - this._state.percentage[1] = this._state.percentage[0]; - this._state.dragged = 0; - } - } - }, - _mouseup: function() { - if(!this._state.enabled) { - return false; - } - if (this.touchCapable) { - // Touch: Unbind touch event handlers: - document.removeEventListener("touchmove", this.mousemove, false); - document.removeEventListener("touchend", this.mouseup, false); - } - // Unbind mouse event handlers: - document.removeEventListener("mousemove", this.mousemove, false); - document.removeEventListener("mouseup", this.mouseup, false); - - this._state.inDrag = false; - if (this._state.over === false) { - this._hideTooltip(); - } - var val = this._calculateValue(true); - - this._layout(); - this._setDataVal(val); - this._trigger('slideStop', val); - - return false; - }, - _calculateValue: function(snapToClosestTick) { - var val; - if (this.options.range) { - val = [this.options.min,this.options.max]; - if (this._state.percentage[0] !== 0){ - val[0] = this._toValue(this._state.percentage[0]); - val[0] = this._applyPrecision(val[0]); - } - if (this._state.percentage[1] !== 100){ - val[1] = this._toValue(this._state.percentage[1]); - val[1] = this._applyPrecision(val[1]); - } - } else { - val = this._toValue(this._state.percentage[0]); - val = parseFloat(val); - val = this._applyPrecision(val); - } - - if (snapToClosestTick) { - var min = [val, Infinity]; - for (var i = 0; i < this.options.ticks.length; i++) { - var diff = Math.abs(this.options.ticks[i] - val); - if (diff <= min[1]) { - min = [this.options.ticks[i], diff]; - } - } - if (min[1] <= this.options.ticks_snap_bounds) { - return min[0]; - } - } - - return val; - }, - _applyPrecision: function(val) { - var precision = this.options.precision || this._getNumDigitsAfterDecimalPlace(this.options.step); - return this._applyToFixedAndParseFloat(val, precision); - }, - _getNumDigitsAfterDecimalPlace: function(num) { - var match = (''+num).match(/(?:\.(\d+))?(?:[eE]([+-]?\d+))?$/); - if (!match) { return 0; } - return Math.max(0, (match[1] ? match[1].length : 0) - (match[2] ? +match[2] : 0)); - }, - _applyToFixedAndParseFloat: function(num, toFixedInput) { - var truncatedNum = num.toFixed(toFixedInput); - return parseFloat(truncatedNum); - }, - /* - Credits to Mike Samuel for the following method! - Source: http://stackoverflow.com/questions/10454518/javascript-how-to-retrieve-the-number-of-decimals-of-a-string-number - */ - _getPercentage: function(ev) { - if (this.touchCapable && (ev.type === 'touchstart' || ev.type === 'touchmove')) { - ev = ev.touches[0]; - } - - var eventPosition = ev[this.mousePos]; - var sliderOffset = this._state.offset[this.stylePos]; - var distanceToSlide = eventPosition - sliderOffset; - // Calculate what percent of the length the slider handle has slid - var percentage = (distanceToSlide / this._state.size) * 100; - percentage = Math.round(percentage / this._state.percentage[2]) * this._state.percentage[2]; - if (this.options.reversed) { - percentage = 100 - percentage; - } - - // Make sure the percent is within the bounds of the slider. - // 0% corresponds to the 'min' value of the slide - // 100% corresponds to the 'max' value of the slide - return Math.max(0, Math.min(100, percentage)); - }, - _validateInputValue: function(val) { - if (typeof val === 'number') { - return val; - } else if (Array.isArray(val)) { - this._validateArray(val); - return val; - } else { - throw new Error( ErrorMsgs.formatInvalidInputErrorMsg(val) ); - } - }, - _validateArray: function(val) { - for(var i = 0; i < val.length; i++) { - var input = val[i]; - if (typeof input !== 'number') { throw new Error( ErrorMsgs.formatInvalidInputErrorMsg(input) ); } - } - }, - _setDataVal: function(val) { - this.element.setAttribute('data-value', val); - this.element.setAttribute('value', val); - this.element.value = val; - }, - _trigger: function(evt, val) { - val = (val || val === 0) ? val : undefined; - - var callbackFnArray = this.eventToCallbackMap[evt]; - if(callbackFnArray && callbackFnArray.length) { - for(var i = 0; i < callbackFnArray.length; i++) { - var callbackFn = callbackFnArray[i]; - callbackFn(val); - } - } - - /* If JQuery exists, trigger JQuery events */ - if($) { - this._triggerJQueryEvent(evt, val); - } - }, - _triggerJQueryEvent: function(evt, val) { - var eventData = { - type: evt, - value: val - }; - this.$element.trigger(eventData); - this.$sliderElem.trigger(eventData); - }, - _unbindJQueryEventHandlers: function() { - this.$element.off(); - this.$sliderElem.off(); - }, - _setText: function(element, text) { - if(typeof element.innerText !== "undefined") { - element.innerText = text; - } else if(typeof element.textContent !== "undefined") { - element.textContent = text; - } - }, - _removeClass: function(element, classString) { - var classes = classString.split(" "); - var newClasses = element.className; - - for(var i = 0; i < classes.length; i++) { - var classTag = classes[i]; - var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)"); - newClasses = newClasses.replace(regex, " "); - } - - element.className = newClasses.trim(); - }, - _addClass: function(element, classString) { - var classes = classString.split(" "); - var newClasses = element.className; - - for(var i = 0; i < classes.length; i++) { - var classTag = classes[i]; - var regex = new RegExp("(?:\\s|^)" + classTag + "(?:\\s|$)"); - var ifClassExists = regex.test(newClasses); - - if(!ifClassExists) { - newClasses += " " + classTag; - } - } - - element.className = newClasses.trim(); - }, - _offsetLeft: function(obj){ - return obj.getBoundingClientRect().left; - }, - _offsetTop: function(obj){ - var offsetTop = obj.offsetTop; - while((obj = obj.offsetParent) && !isNaN(obj.offsetTop)){ - offsetTop += obj.offsetTop; - } - return offsetTop; - }, - _offset: function (obj) { - return { - left: this._offsetLeft(obj), - top: this._offsetTop(obj) - }; - }, - _css: function(elementRef, styleName, value) { - if ($) { - $.style(elementRef, styleName, value); - } else { - var style = styleName.replace(/^-ms-/, "ms-").replace(/-([\da-z])/gi, function (all, letter) { - return letter.toUpperCase(); - }); - elementRef.style[style] = value; - } - }, - _toValue: function(percentage) { - return this.options.scale.toValue.apply(this, [percentage]); - }, - _toPercentage: function(value) { - return this.options.scale.toPercentage.apply(this, [value]); - }, - _setTooltipPosition: function(){ - var tooltips = [this.tooltip, this.tooltip_min, this.tooltip_max]; - if (this.options.orientation === 'vertical'){ - var tooltipPos = this.options.tooltip_position || 'right'; - var oppositeSide = (tooltipPos === 'left') ? 'right' : 'left'; - tooltips.forEach(function(tooltip){ - this._addClass(tooltip, tooltipPos); - tooltip.style[oppositeSide] = '100%'; - }.bind(this)); - } else if(this.options.tooltip_position === 'bottom') { - tooltips.forEach(function(tooltip){ - this._addClass(tooltip, 'bottom'); - tooltip.style.top = 22 + 'px'; - }.bind(this)); - } else { - tooltips.forEach(function(tooltip){ - this._addClass(tooltip, 'top'); - tooltip.style.top = -this.tooltip.outerHeight - 14 + 'px'; - }.bind(this)); - } - } - }; - - /********************************* - - Attach to global namespace - - *********************************/ - if($) { - var namespace = $.fn.slider ? 'bootstrapSlider' : 'slider'; - $.bridget(namespace, Slider); - } - - })( $ ); - - return Slider; -})); diff --git a/src/Umbraco.Web.UI.Client/package.json b/src/Umbraco.Web.UI.Client/package.json index c70d3df8e4..63794446cd 100644 --- a/src/Umbraco.Web.UI.Client/package.json +++ b/src/Umbraco.Web.UI.Client/package.json @@ -32,6 +32,8 @@ "lazyload-js": "1.0.0", "moment": "2.10.6", "ng-file-upload": "12.2.13", + "nouislider": "12.1.0", + "npm": "^6.4.1", "signalr": "2.3.0", "tinymce": "4.8.3", "typeahead.js": "0.10.5", diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js index 1d52c4e451..923cac80f5 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/buttons/umbbutton.directive.js @@ -66,6 +66,7 @@ Use this directive to render an umbraco button. The directive can be used to gen @param {string=} size Set a button icon ("xs", "m", "l", "xl"). @param {boolean=} disabled Set to true to disable the button. @param {string=} addEllipsis Adds an ellipsis character (…) to the button label which means the button will open a dialog or prompt the user for more information. +@param {string=} showCaret Shows a caret on the right side of the button label **/ @@ -93,7 +94,8 @@ Use this directive to render an umbraco button. The directive can be used to gen disabled: " 0 ? true : false; + userService.getCurrentUser().then(function(user){ // only allow change of media type if user has access to the settings sections angular.forEach(user.sections, function(section){ - if(section.alias === "settings") { + if(section.alias === "settings" && !scope.isInfiniteMode) { scope.allowChangeDocumentType = true; scope.allowChangeTemplate = true; } diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js index b6af7a4ca6..22939c7f2c 100644 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/editor/umbeditormenu.directive.js @@ -1,50 +1,63 @@ -(function() { - 'use strict'; +(function () { + 'use strict'; - function EditorMenuDirective($injector, treeService, navigationService, umbModelMapper, appState) { + function EditorMenuDirective($injector, treeService, navigationService, umbModelMapper, appState) { - function link(scope, el, attr, ctrl) { + function link(scope, el, attr, ctrl) { - //adds a handler to the context menu item click, we need to handle this differently - //depending on what the menu item is supposed to do. - scope.executeMenuItem = function (action) { - navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection); - }; + scope.dropdown = { + isOpen: false + }; - //callback method to go and get the options async - scope.getOptions = function () { + function onInit() { - if (!scope.currentNode) { - return; - } + getOptions(); - //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu) - appState.setMenuState("currentNode", scope.currentNode); + } - if (!scope.actions) { - treeService.getMenu({ treeNode: scope.currentNode }) - .then(function (data) { - scope.actions = data.menuItems; - }); - } - }; + //adds a handler to the context menu item click, we need to handle this differently + //depending on what the menu item is supposed to do. + scope.executeMenuItem = function (action) { + navigationService.executeMenuAction(action, scope.currentNode, scope.currentSection); + scope.dropdown.isOpen = false; + }; - } + //callback method to go and get the options async + function getOptions() { - var directive = { - restrict: 'E', - replace: true, - templateUrl: 'views/components/editor/umb-editor-menu.html', - link: link, - scope: { - currentNode: "=", - currentSection: "@" - } - }; + if (!scope.currentNode) { + return; + } - return directive; - } + //when the options item is selected, we need to set the current menu item in appState (since this is synonymous with a menu) + appState.setMenuState("currentNode", scope.currentNode); - angular.module('umbraco.directives').directive('umbEditorMenu', EditorMenuDirective); + if (!scope.actions) { + treeService.getMenu({ treeNode: scope.currentNode }) + .then(function (data) { + scope.actions = data.menuItems; + }); + } + }; + + onInit(); + + } + + var directive = { + restrict: 'E', + replace: true, + templateUrl: 'views/components/editor/umb-editor-menu.html', + link: link, + scope: { + currentNode: "=", + currentSection: "@" + } + }; + + return directive; + } + + angular.module('umbraco.directives').directive('umbEditorMenu', EditorMenuDirective); })(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js deleted file mode 100644 index 8d2b83d065..0000000000 --- a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbdatetimepicker.directive.js +++ /dev/null @@ -1,186 +0,0 @@ -/** -@ngdoc directive -@name umbraco.directives.directive:umbDateTimePicker -@restrict E -@scope - -@description -Added in Umbraco version 7.6 -This directive is a wrapper of the bootstrap datetime picker version 3.1.3. Use it to render a date time picker. -For extra details about options and events take a look here: https://eonasdan.github.io/bootstrap-datetimepicker/ - -Use this directive to render a date time picker - -

Markup example

-
-	
- - - - -
-
- -

Controller example

-
-	(function () {
-		"use strict";
-
-		function Controller() {
-
-            var vm = this;
-
-            vm.date = "";
-
-            vm.config = {
-                pickDate: true,
-                pickTime: true,
-                useSeconds: true,
-                format: "YYYY-MM-DD HH:mm:ss",
-                icons: {
-                    time: "icon-time",
-                    date: "icon-calendar",
-                    up: "icon-chevron-up",
-                    down: "icon-chevron-down"
-                }
-            };
-
-            vm.datePickerChange = datePickerChange;
-            vm.datePickerError = datePickerError;
-
-            function datePickerChange(event) {
-                // handle change
-                if(event.date && event.date.isValid()) {
-                    var date = event.date.format(vm.datePickerConfig.format);
-                }
-            }
-
-            function datePickerError(event) {
-                // handle error
-            }
-
-        }
-
-		angular.module("umbraco").controller("My.Controller", Controller);
-
-	})();
-
- -@param {object} options (binding): Config object for the date picker. -@param {callback} onHide (callback): Hide callback. -@param {callback} onShow (callback): Show callback. -@param {callback} onChange (callback): Change callback. -@param {callback} onError (callback): Error callback. -@param {callback} onUpdate (callback): Update callback. -**/ - -(function () { - 'use strict'; - - function DateTimePickerDirective(assetsService) { - - function link(scope, element, attrs, ctrl) { - - scope.hasTranscludedContent = false; - - function onInit() { - - // check for transcluded content so we can hide the defualt markup - scope.hasTranscludedContent = element.find('.js-datePicker__transcluded-content')[0].children.length > 0; - - // load css file for the date picker - assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css', scope); - - // load the js file for the date picker - assetsService.loadJs('lib/datetimepicker/bootstrap-datetimepicker.js', scope).then(function () { - // init date picker - initDatePicker(); - }); - } - - function onHide(event) { - if (scope.onHide) { - scope.$apply(function(){ - // callback - scope.onHide({event: event}); - }); - } - } - - function onShow() { - if (scope.onShow) { - scope.$apply(function(){ - // callback - scope.onShow(); - }); - } - } - - function onChange(event) { - if (scope.onChange && event.date && event.date.isValid()) { - scope.$apply(function(){ - // callback - scope.onChange({event: event}); - }); - } - } - - function onError(event) { - if (scope.onError) { - scope.$apply(function(){ - // callback - scope.onError({event:event}); - }); - } - } - - function onUpdate(event) { - if (scope.onUpdate) { - scope.$apply(function(){ - // callback - scope.onUpdate({event: event}); - }); - } - } - - function initDatePicker() { - // Open the datepicker and add a changeDate eventlistener - element - .datetimepicker(scope.options) - .on("dp.hide", onHide) - .on("dp.show", onShow) - .on("dp.change", onChange) - .on("dp.error", onError) - .on("dp.update", onUpdate); - } - - onInit(); - - } - - var directive = { - restrict: 'E', - replace: true, - transclude: true, - templateUrl: 'views/components/umb-date-time-picker.html', - scope: { - options: "=", - onHide: "&", - onShow: "&", - onChange: "&", - onError: "&", - onUpdate: "&" - }, - link: link - }; - - return directive; - - } - - angular.module('umbraco.directives').directive('umbDateTimePicker', DateTimePickerDirective); - -})(); diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js new file mode 100644 index 0000000000..0003658600 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/components/umbrangeslider.directive.js @@ -0,0 +1,207 @@ +/** +@ngdoc directive +@name umbraco.directives.directive:umbRangeSlider +@restrict E +@scope + +@description +Added in Umbraco version 8.0 +This directive is a wrapper of the noUiSlider library. Use it to render a slider. +For extra details about options and events take a look here: https://refreshless.com/nouislider/ + +

Markup example

+
+	
+ + + + +
+
+ +

Controller example

+
+	(function () {
+		"use strict";
+
+		function Controller() {
+
+            var vm = this;
+
+            vm.value = [10];
+
+            vm.slideEnd = slideEnd;
+
+            function slideEnd(values) {
+            	// handle change
+            }
+
+        }
+
+		angular.module("umbraco").controller("My.Controller", Controller);
+
+	})();
+
+ +@param {object} ngModel (binding): Value for the slider. +@param {object} options (binding): Config object for the date picker. +@param {callback} onSetup (callback): onSetup gets triggered when the slider is initialized +@param {callback} onUpdate (callback): onUpdate fires every time the slider values are changed. +@param {callback} onSlide (callback): onSlide gets triggered when the handle is being dragged. +@param {callback} onSet (callback): onSet will trigger every time a slider stops changing. +@param {callback} onChange (callback): onChange fires when a user stops sliding, or when a slider value is changed by 'tap'. +@param {callback} onStart (callback): onStart fires when a handle is clicked (mousedown, or the equivalent touch events). +@param {callback} onEnd (callback): onEnd fires when a handle is released (mouseup etc), or when a slide is canceled due to other reasons. +**/ + + +(function() { + 'use strict'; + + var umbRangeSlider = { + template: '
', + controller: UmbRangeSliderController, + bindings: { + ngModel: '<', + options: '<', + onSetup: '&?', + onUpdate: '&?', + onSlide: '&?', + onSet: '&?', + onChange: '&?', + onStart: '&?', + onEnd: '&?' + } + }; + + function UmbRangeSliderController($element, $timeout, $scope, assetsService) { + + const ctrl = this; + let sliderInstance = null; + + ctrl.$onInit = function() { + + // load css file for the date picker + assetsService.loadCss('lib/nouislider/nouislider.min.css', $scope); + + // load the js file for the date picker + assetsService.loadJs('lib/nouislider/nouislider.min.js', $scope).then(function () { + // init date picker + grabElementAndRun(); + }); + + }; + + function grabElementAndRun() { + $timeout(function() { + const element = $element.find('.umb-range-slider')[0]; + setSlider(element); + }, 0, true); + } + + function setSlider(element) { + + sliderInstance = element; + + const defaultOptions = { + "start": [0], + "step": 1, + "range": { + "min": [0], + "max": [100] + } + }; + const options = ctrl.options ? ctrl.options : defaultOptions; + + // create new slider + noUiSlider.create(sliderInstance, options); + + if (ctrl.onSetup) { + ctrl.onSetup({ + slider: sliderInstance + }); + } + + // If has ngModel set the date + if (ctrl.ngModel) { + sliderInstance.noUiSlider.set(ctrl.ngModel); + } + + // destroy the slider instance when the dom element is removed + angular.element(element).on('$destroy', function() { + sliderInstance.noUiSlider.off(); + }); + + setUpCallbacks(); + + // Refresh the scope + $scope.$applyAsync(); + } + + function setUpCallbacks() { + if(sliderInstance) { + + // bind hook for update + if(ctrl.onUpdate) { + sliderInstance.noUiSlider.on('update', function (values, handle, unencoded, tap, positions) { + $timeout(function() { + ctrl.onUpdate({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions}); + }); + }); + } + + // bind hook for slide + if(ctrl.onSlide) { + sliderInstance.noUiSlider.on('slide', function (values, handle, unencoded, tap, positions) { + $timeout(function() { + ctrl.onSlide({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions}); + }); + }); + } + + // bind hook for set + if(ctrl.onSet) { + sliderInstance.noUiSlider.on('set', function (values, handle, unencoded, tap, positions) { + $timeout(function() { + ctrl.onSet({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions}); + }); + }); + } + + // bind hook for change + if(ctrl.onChange) { + sliderInstance.noUiSlider.on('change', function (values, handle, unencoded, tap, positions) { + $timeout(function() { + ctrl.onChange({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions}); + }); + }); + } + + // bind hook for start + if(ctrl.onStart) { + sliderInstance.noUiSlider.on('start', function (values, handle, unencoded, tap, positions) { + $timeout(function() { + ctrl.onStart({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions}); + }); + }); + } + + // bind hook for end + if(ctrl.onEnd) { + sliderInstance.noUiSlider.on('end', function (values, handle, unencoded, tap, positions) { + $timeout(function() { + ctrl.onEnd({values: values, handle: handle, unencoded: unencoded, tap: tap, positions: positions}); + }); + }); + } + + } + } + + } + + angular.module('umbraco.directives').component('umbRangeSlider', umbRangeSlider); + +})(); diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 5218a9b59c..5d00d6f749 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -152,6 +152,7 @@ @import "components/umb-number-badge.less"; @import "components/umb-progress-circle.less"; @import "components/umb-stylesheet.less"; +@import "components/umb-range-slider.less"; @import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less index d3f2fee5d5..7c45801f43 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/buttons/umb-button.less @@ -29,6 +29,11 @@ opacity: 0; } +.umb-button .umb-button__caret { + margin-top: 0; + margin-left: 5px; +} + .umb-button__progress { position: absolute; left: 50%; diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less index 8ead992625..151415f0db 100644 --- a/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-querybuilder.less @@ -39,4 +39,8 @@ .umb-querybuilder .query-items > * { flex: 0 1 auto; margin: 5px; -} \ No newline at end of file +} + +.umb-querybuilder .query-items .btn { + min-height: 2rem; +} diff --git a/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less new file mode 100644 index 0000000000..1c21c5a148 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/components/umb-range-slider.less @@ -0,0 +1,39 @@ + + +.umb-range-slider.noUi-target { + background: linear-gradient(to bottom, #f5f5f5 0%, #f9f9f9 100%); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); + border-radius: 20px; + height: 10px; + border: none; +} + +.umb-range-slider .noUi-handle { + border-radius: 100px; + border: none; + box-shadow: none; + width: 20px !important; + height: 20px !important; + background-color: @turquoise; +} + +.umb-range-slider .noUi-handle::before { + display: none; +} + +.umb-range-slider .noUi-handle::after { + display: none; +} + +.umb-range-slider .noUi-handle { + right: -10px !important; // half the handle width +} + +.umb-range-slider .noUi-marker-large.noUi-marker-horizontal { + height: 10px; +} + +.umb-range-slider .noUi-marker.noUi-marker-horizontal { + width: 1px; +} + diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.controller.js index 1beac99928..faca3b3fa0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.controller.js @@ -15,9 +15,7 @@ vm.conditions = []; vm.datePickerConfig = { - pickDate: true, - pickTime: false, - format: "YYYY-MM-DD" + dateFormat: "Y-m-d" }; vm.chooseSource = chooseSource; @@ -180,9 +178,10 @@ throttledFunc(); } - function datePickerChange(event, filter) { - if (event.date && event.date.isValid()) { - filter.constraintValue = event.date.format(vm.datePickerConfig.format); + function datePickerChange(date, filter) { + const momentDate = moment(date); + if (momentDate && momentDate.isValid()) { + filter.constraintValue = momentDate.format(vm.datePickerConfig.format); throttledFunc(); } } diff --git a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html index e7f0cd9a1b..4b6c6cc179 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html +++ b/src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/querybuilder/querybuilder.html @@ -23,26 +23,36 @@ I want from - - - {{vm.query.source.name}} - - + + + @@ -56,52 +66,62 @@ - - + + - - + + - - @@ -112,30 +132,38 @@ - @@ -148,9 +176,7 @@ -
-				{{model.result.queryExpression}}
-						
+
{{model.result.queryExpression}}
diff --git a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button-group.html b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button-group.html index 3810630fa9..054681d7f1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button-group.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button-group.html @@ -16,17 +16,20 @@ add-ellipsis={{defaultButton.addEllipsis}}> - + - + + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html index 68d4adef5a..03813c8518 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/buttons/umb-button.html @@ -11,6 +11,7 @@ {{vm.buttonLabel}} + @@ -18,6 +19,7 @@ {{vm.buttonLabel}} + @@ -25,6 +27,7 @@ {{vm.buttonLabel}} + diff --git a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-menu.html b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-menu.html index bf9c8fab8c..032e4cd6c3 100644 --- a/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-menu.html +++ b/src/Umbraco.Web.UI.Client/src/views/components/editor/umb-editor-menu.html @@ -1,22 +1,20 @@
- - - Actions - - + + - - + + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js b/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js index afab478fc4..a8f87ce2c9 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/content/content.rights.controller.js @@ -63,6 +63,8 @@ vm.labels.permissionsSetForGroup = value; }); setViewSate("managePermissions"); + // hide dropdown + vm.groupsDropdownOpen = false; } function assignGroupPermissions(group) { diff --git a/src/Umbraco.Web.UI.Client/src/views/content/rights.html b/src/Umbraco.Web.UI.Client/src/views/content/rights.html index afa932e606..35f3d34260 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/rights.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/rights.html @@ -27,20 +27,24 @@

diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js index 9bed59b0d7..704fccbde1 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js @@ -1,38 +1,81 @@ function dateTimePickerController($scope, notificationsService, assetsService, angularHelper, userService, $element, dateHelper) { - //setup the default config - var config = { - pickDate: true, - pickTime: true, - useSeconds: true, - format: "YYYY-MM-DD HH:mm:ss", - icons: { - time: "icon-time", - date: "icon-calendar", - up: "icon-chevron-up", - down: "icon-chevron-down" - } + let flatPickr = null; - }; + function onInit() { + + $scope.hasDatetimePickerValue = $scope.model.value ? true : false; + $scope.model.datetimePickerValue = null; + $scope.serverTime = null; + $scope.serverTimeNeedsOffsetting = false; + + // setup the default config + var config = { + pickDate: true, + pickTime: true, + useSeconds: true, + format: "YYYY-MM-DD HH:mm:ss", + icons: { + time: "icon-time", + date: "icon-calendar", + up: "icon-chevron-up", + down: "icon-chevron-down" + } + }; + + // map the user config + $scope.model.config = angular.extend(config, $scope.model.config); + + // ensure the format doesn't get overwritten with an empty string + if ($scope.model.config.format === "" || $scope.model.config.format === undefined || $scope.model.config.format === null) { + $scope.model.config.format = $scope.model.config.pickTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD"; + } + + // check whether a server time offset is needed + if (Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) { + // Will return something like 120 + var serverOffset = Umbraco.Sys.ServerVariables.application.serverTimeOffset; + + // Will return something like -120 + var localOffset = new Date().getTimezoneOffset(); + + // If these aren't equal then offsetting is needed + // note the minus in front of serverOffset needed + // because C# and javascript return the inverse offset + $scope.serverTimeNeedsOffsetting = (-serverOffset !== localOffset); + } + + const dateFormat = $scope.model.config.pickTime ? "Y-m-d H:i:S" : "Y-m-d"; + + // date picker config + $scope.datePickerConfig = { + enableTime: $scope.model.config.pickTime, + dateFormat: dateFormat, + time_24hr: true + }; + + setDatePickerVal(); - //map the user config - $scope.model.config = angular.extend(config, $scope.model.config); - //ensure the format doesn't get overwritten with an empty string - if ($scope.model.config.format === "" || $scope.model.config.format === undefined || $scope.model.config.format === null) { - $scope.model.config.format = $scope.model.config.pickTime ? "YYYY-MM-DD HH:mm:ss" : "YYYY-MM-DD"; } - $scope.hasDatetimePickerValue = $scope.model.value ? true : false; - $scope.datetimePickerValue = null; - - //hide picker if clicking on the document - $scope.hidePicker = function () { - //$element.find("div:first").datetimepicker("hide"); - // Sometimes the statement above fails and generates errors in the browser console. The following statements fix that. - var dtp = $element.find("div:first"); - if (dtp && dtp.datetimepicker) { - dtp.datetimepicker("hide"); + $scope.clearDate = function() { + $scope.hasDatetimePickerValue = false; + if($scope.model) { + $scope.model.datetimePickerValue = null; + $scope.model.value = null; } + if($scope.datePickerForm && $scope.datePickerForm.datepicker) { + $scope.datePickerForm.datepicker.$setValidity("pickerError", true); + } + } + + $scope.datePickerSetup = function(instance) { + flatPickr = instance; + }; + + $scope.datePickerChange = function(date) { + setDate(date); + setDatePickerVal(); }; //here we declare a special method which will be called whenever the value has changed from the server @@ -44,53 +87,45 @@ function dateTimePickerController($scope, notificationsService, assetsService, a var newDate = moment(newVal); if (newDate.isAfter(minDate)) { - applyDate({ date: moment(newVal) }); + setDate(newVal); } else { $scope.clearDate(); } } }; - - //handles the date changing via the date picker - function applyDate(e) { + + function setDate(date) { + const momentDate = moment(date); angularHelper.safeApply($scope, function() { // when a date is changed, update the model - if (e.date && e.date.isValid()) { + if (momentDate && momentDate.isValid()) { $scope.datePickerForm.datepicker.$setValidity("pickerError", true); $scope.hasDatetimePickerValue = true; - $scope.datetimePickerValue = e.date.format($scope.model.config.format); + $scope.model.datetimePickerValue = momentDate.format($scope.model.config.format); } else { $scope.hasDatetimePickerValue = false; - $scope.datetimePickerValue = null; - } - - setModelValue(); - - if (!$scope.model.config.pickTime) { - $element.find("div:first").datetimepicker("hide", 0); + $scope.model.datetimePickerValue = null; } + updateModelValue(date); }); } - //sets the scope model value accordingly - this is the value to be sent up to the server and depends on - // if the picker is configured to offset time. We always format the date/time in a specific format for sending - // to the server, this is different from the format used to display the date/time. - function setModelValue() { + function updateModelValue(date) { + const momentDate = moment(date); if ($scope.hasDatetimePickerValue) { - var elementData = $element.find("div:first").data().DateTimePicker; if ($scope.model.config.pickTime) { //check if we are supposed to offset the time if ($scope.model.value && Object.toBoolean($scope.model.config.offsetTime) && Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) { - $scope.model.value = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset); - $scope.serverTime = dateHelper.convertToServerStringTime(elementData.getDate(), Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z"); + $scope.model.value = dateHelper.convertToServerStringTime(momentDate, Umbraco.Sys.ServerVariables.application.serverTimeOffset); + $scope.serverTime = dateHelper.convertToServerStringTime(momentDate, Umbraco.Sys.ServerVariables.application.serverTimeOffset, "YYYY-MM-DD HH:mm:ss Z"); } else { - $scope.model.value = elementData.getDate().format("YYYY-MM-DD HH:mm:ss"); + $scope.model.value = momentDate.format("YYYY-MM-DD HH:mm:ss"); } } else { - $scope.model.value = elementData.getDate().format("YYYY-MM-DD"); + $scope.model.value = momentDate.format("YYYY-MM-DD"); } } else { @@ -99,7 +134,7 @@ function dateTimePickerController($scope, notificationsService, assetsService, a } /** Sets the value of the date picker control adn associated viewModel objects based on the model value */ - function setDatePickerVal(element) { + function setDatePickerVal() { if ($scope.model.value) { var dateVal; //check if we are supposed to offset the time @@ -112,98 +147,21 @@ function dateTimePickerController($scope, notificationsService, assetsService, a //create a normal moment , no offset required var dateVal = $scope.model.value ? moment($scope.model.value, "YYYY-MM-DD HH:mm:ss") : moment(); } - - element.datetimepicker("setValue", dateVal); - $scope.datetimePickerValue = dateVal.format($scope.model.config.format); + $scope.model.datetimePickerValue = dateVal.format($scope.model.config.format); } else { $scope.clearDate(); } } - $scope.clearDate = function() { - $scope.hasDatetimePickerValue = false; - $scope.datetimePickerValue = null; - $scope.model.value = null; - $scope.datePickerForm.datepicker.$setValidity("pickerError", true); - } - - $scope.serverTime = null; - $scope.serverTimeNeedsOffsetting = false; - if (Umbraco.Sys.ServerVariables.application.serverTimeOffset !== undefined) { - // Will return something like 120 - var serverOffset = Umbraco.Sys.ServerVariables.application.serverTimeOffset; - - // Will return something like -120 - var localOffset = new Date().getTimezoneOffset(); - - // If these aren't equal then offsetting is needed - // note the minus in front of serverOffset needed - // because C# and javascript return the inverse offset - $scope.serverTimeNeedsOffsetting = (-serverOffset !== localOffset); - } - - //get the current user to see if we can localize this picker - userService.getCurrentUser().then(function (user) { - - assetsService.loadCss('lib/datetimepicker/bootstrap-datetimepicker.min.css', $scope).then(function() { - - var filesToLoad = ["lib/datetimepicker/bootstrap-datetimepicker.js"]; - - - $scope.model.config.language = user.locale; - - - assetsService.load(filesToLoad, $scope).then( - function () { - //The Datepicker js and css files are available and all components are ready to use. - - // Get the id of the datepicker button that was clicked - var pickerId = $scope.model.alias; - - var element = $element.find("div:first"); - - // Create the datepicker and add a changeDate eventlistener - element - .datetimepicker(angular.extend({ useCurrent: true }, $scope.model.config)) - .on("dp.change", applyDate) - .on("dp.error", function(a, b, c) { - $scope.hasDatetimePickerValue = false; - $scope.datePickerForm.datepicker.$setValidity("pickerError", false); - }); - - $(document).bind("click", $scope.hidePicker); - - setDatePickerVal(element); - - element.find("input").bind("blur", function() { - //we need to force an apply here - $scope.$apply(); - }); - - $scope.$watch("model.value", function(newVal, oldVal) { - if (newVal !== oldVal) { - $scope.hasDatetimePickerValue = newVal ? true : false; - setDatePickerVal(element); - } - }); - - var unsubscribe = $scope.$on("formSubmitting", function (ev, args) { - setModelValue(); - }); - - //Ensure to remove the event handler when this instance is destroyted - $scope.$on('$destroy', function () { - element.find("input").unbind("blur"); - element.datetimepicker("destroy"); - unsubscribe(); - $(document).unbind("click", $scope.hidePicker); - }); - - }); - }); - + $scope.$watch("model.value", function(newVal, oldVal) { + if (newVal !== oldVal) { + $scope.hasDatetimePickerValue = newVal ? true : false; + setDatePickerVal(); + } }); + + onInit(); } diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html index 258d2040d5..47c9182650 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.html @@ -1,17 +1,30 @@
-
- +
- - - + + +
+ + + + +
+ +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/handle.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/handle.prevalues.html deleted file mode 100644 index 0821c5e757..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/handle.prevalues.html +++ /dev/null @@ -1,13 +0,0 @@ -
- - - - - Required - - -
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/orientation.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/orientation.prevalues.html deleted file mode 100644 index 05fb6756d1..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/orientation.prevalues.html +++ /dev/null @@ -1,12 +0,0 @@ -
- - - - - Required - - -
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js index 588e2d2b03..c8813ee4c0 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.controller.js @@ -1,248 +1,80 @@ -function sliderController($scope, $log, $element, assetsService, angularHelper) { +function sliderController($scope) { - var sliderRef = null; + let sliderRef = null; /** configure some defaults on init */ function configureDefaults() { - - if (!$scope.model.config.orientation) { - $scope.model.config.orientation = "horizontal"; - } - if (!$scope.model.config.enableRange) { - $scope.model.config.enableRange = false; - } - else { - $scope.model.config.enableRange = Object.toBoolean($scope.model.config.enableRange); - } - - if (!$scope.model.config.initVal1) { - $scope.model.config.initVal1 = 0; - } - else { - $scope.model.config.initVal1 = parseFloat($scope.model.config.initVal1); - } - if (!$scope.model.config.initVal2) { - $scope.model.config.initVal2 = 0; - } - else { - $scope.model.config.initVal2 = parseFloat($scope.model.config.initVal2); - } - if (!$scope.model.config.minVal) { - $scope.model.config.minVal = 0; - } - else { - $scope.model.config.minVal = parseFloat($scope.model.config.minVal); - } - if (!$scope.model.config.maxVal) { - $scope.model.config.maxVal = 100; - } - else { - $scope.model.config.maxVal = parseFloat($scope.model.config.maxVal); - } - if (!$scope.model.config.step) { - $scope.model.config.step = 1; - } - else { - $scope.model.config.step = parseFloat($scope.model.config.step); - } - - if (!$scope.model.config.handle) { - $scope.model.config.handle = "round"; - } - - if (!$scope.model.config.reversed) { - $scope.model.config.reversed = false; - } - else { - $scope.model.config.reversed = Object.toBoolean($scope.model.config.reversed); - } - - if (!$scope.model.config.tooltip) { - $scope.model.config.tooltip = "show"; - } - - if (!$scope.model.config.tooltipSplit) { - $scope.model.config.tooltipSplit = false; - } - else { - $scope.model.config.tooltipSplit = Object.toBoolean($scope.model.config.tooltipSplit); - } - - if ($scope.model.config.tooltipFormat) { - $scope.model.config.formatter = function (value) { - if (angular.isArray(value) && $scope.model.config.enableRange) { - return $scope.model.config.tooltipFormat.replace("{0}", value[0]).replace("{1}", value[1]); - } else { - return $scope.model.config.tooltipFormat.replace("{0}", value); - } - } - } - - if (!$scope.model.config.ticks) { - $scope.model.config.ticks = []; - } - else if (angular.isString($scope.model.config.ticks)) { - // returns comma-separated string to an array, e.g. [0, 100, 200, 300, 400] - $scope.model.config.ticks = _.map($scope.model.config.ticks.split(','), function (item) { - return parseInt(item.trim()); - }); - } - - if (!$scope.model.config.ticksPositions) { - $scope.model.config.ticksPositions = []; - } - else if (angular.isString($scope.model.config.ticksPositions)) { - // returns comma-separated string to an array, e.g. [0, 30, 60, 70, 90, 100] - $scope.model.config.ticksPositions = _.map($scope.model.config.ticksPositions.split(','), function (item) { - return parseInt(item.trim()); - }); - } - - if (!$scope.model.config.ticksLabels) { - $scope.model.config.ticksLabels = []; - } - else if (angular.isString($scope.model.config.ticksLabels)) { - // returns comma-separated string to an array, e.g. ['$0', '$100', '$200', '$300', '$400'] - $scope.model.config.ticksLabels = _.map($scope.model.config.ticksLabels.split(','), function (item) { - return item.trim(); - }); - } - - if (!$scope.model.config.ticksSnapBounds) { - $scope.model.config.ticksSnapBounds = 0; - } - else { - $scope.model.config.ticksSnapBounds = parseFloat($scope.model.config.ticksSnapBounds); - } + $scope.model.config.enableRange = $scope.model.config.enableRange ? Object.toBoolean($scope.model.config.enableRange) : false; + $scope.model.config.initVal1 = $scope.model.config.initVal1 ? parseFloat($scope.model.config.initVal1) : 0; + $scope.model.config.initVal2 = $scope.model.config.initVal2 ? parseFloat($scope.model.config.initVal2) : 0; + $scope.model.config.minVal = $scope.model.config.minVal ? parseFloat($scope.model.config.minVal) : 0; + $scope.model.config.maxVal = $scope.model.config.maxVal ? parseFloat($scope.model.config.maxVal) : 100; + $scope.model.config.step = $scope.model.config.step ? parseFloat($scope.model.config.step) : 1; } - function getValueForSlider(val) { - - if (!angular.isArray(val)) { - val = val.toString().split(","); - } - var val1 = val[0]; - var val2 = val.length > 1 ? val[1] : null; - - //configure the model value based on if range is enabled or not - if ($scope.model.config.enableRange == true) { - var i1 = parseFloat(val1); - var i2 = parseFloat(val2); - return [ - isNaN(i1) ? $scope.model.config.minVal : (i1 >= $scope.model.config.minVal ? i1 : $scope.model.config.minVal), - isNaN(i2) ? $scope.model.config.maxVal : (i2 >= i1 ? (i2 <= $scope.model.config.maxVal ? i2 : $scope.model.config.maxVal) : $scope.model.config.maxVal) - ]; - } - else { - return parseFloat(val1); - } + function setModelValue(values) { + $scope.model.value = values ? values.toString() : null; } - /** This creates the slider with the model values - it's called on startup and returns a reference to the slider object */ - function createSlider() { + $scope.setup = function(slider) { + sliderRef = slider; + }; - //the value that we'll give the slider - if it's a range, we store our value as a comma separated val but this slider expects an array - var sliderVal = null; - - //configure the model value based on if range is enabled or not - if ($scope.model.config.enableRange == true) { - //If no value saved yet - then use default value - //If it contains a single value - then also create a new array value - if (!$scope.model.value || $scope.model.value.indexOf(",") == -1) { - sliderVal = getValueForSlider([$scope.model.config.initVal1, $scope.model.config.initVal2]); - } - else { - //this will mean it's a delimited value stored in the db, convert it to an array - sliderVal = getValueForSlider($scope.model.value.split(',')); - } - } - else { - //If no value saved yet - then use default value - if ($scope.model.value) { - sliderVal = getValueForSlider($scope.model.value); - } - else { - sliderVal = getValueForSlider($scope.model.config.initVal1); - } - } - - //initiate slider, add event handler and get the instance reference (stored in data) - var slider = $element.find('.slider-item').bootstrapSlider({ - max: $scope.model.config.maxVal, - min: $scope.model.config.minVal, - orientation: $scope.model.config.orientation, - selection: $scope.model.config.reversed ? "after" : "before", - step: $scope.model.config.step, - precision: $scope.model.config.precision, - tooltip: $scope.model.config.tooltip, - tooltip_split: $scope.model.config.tooltipSplit, - tooltip_position: $scope.model.config.tooltipPosition, - handle: $scope.model.config.handle, - reversed: $scope.model.config.reversed, - ticks: $scope.model.config.ticks, - ticks_positions: $scope.model.config.ticksPositions, - ticks_labels: $scope.model.config.ticksLabels, - ticks_snap_bounds: $scope.model.config.ticksSnapBounds, - formatter: $scope.model.config.formatter, - range: $scope.model.config.enableRange, - //set the slider val - we cannot do this with data- attributes when using ranges - value: sliderVal - }); - - slider.on('slideStop', function (e) { - var value = e.value; - angularHelper.safeApply($scope, function () { - $scope.model.value = getModelValueFromSlider(value); - }); - }).data('slider'); - - return slider; - } - - function getModelValueFromSlider(sliderVal) { - //Get the value from the slider and format it correctly, if it is a range we want a comma delimited value - if ($scope.model.config.enableRange == true) { - return sliderVal.join(","); - } - else { - return sliderVal.toString(); - } - } + $scope.end = function(values) { + setModelValue(values); + }; function init() { + // convert to array + $scope.sliderValue = $scope.model.value ? $scope.model.value.split(',') : null; + configureDefaults(); - //tell the assetsService to load the bootstrap slider - //libs from the plugin folder - assetsService - .loadJs("lib/slider/js/bootstrap-slider.js") - .then(function () { + // format config to fit slider plugin + const start = $scope.model.config.enableRange ? [$scope.model.config.initVal1, $scope.model.config.initVal2] : [$scope.model.config.initVal1]; + const step = $scope.model.config.step; + const tooltips = $scope.model.config.enableRange ? [true, true] : [true]; + const min = $scope.model.config.minVal ? [$scope.model.config.minVal] : [$scope.model.config.minVal]; + const max = $scope.model.config.maxVal ? [$scope.model.config.maxVal] : [$scope.model.config.maxVal]; - var slider = createSlider(); - - // Initialize model value if not set - if (!$scope.model.value) { - var sliderVal = slider.bootstrapSlider('getValue'); - $scope.model.value = getModelValueFromSlider(sliderVal); + // setup default + $scope.sliderOptions = { + "start": start, + "step": step, + "tooltips": tooltips, + "format": { + to: function (value) { + return Math.round(value); + }, + from: function (value) { + return Math.round(value); } + }, + "range": { + "min": min, + "max": max + }, + "pips": { + mode: 'steps', + density: 100, + filter: filterPips + } + }; - //watch for the model value being changed and update the slider value when it does - $scope.$watch("model.value", function (newVal, oldVal) { - if (newVal != oldVal) { - var sliderVal = getModelValueFromSlider(slider.bootstrapSlider('getValue')); - if (newVal !== sliderVal) { - slider.bootstrapSlider('setValue', getValueForSlider(newVal)); - } - } - }); + function filterPips(value) { + // show a pip for min and maximum value + return value === $scope.model.config.minVal || value === $scope.model.config.maxVal ? 1 : -1; + } - }); - - //load the separate css for the editor to avoid it blocking our js loading - assetsService.loadCss("lib/slider/bootstrap-slider.css", $scope); - assetsService.loadCss("lib/slider/bootstrap-slider-custom.css", $scope); } + + $scope.$watch('model.value', function(newValue, oldValue){ + if(newValue && newValue !== oldValue) { + $scope.sliderValue = newValue.split(','); + sliderRef.noUiSlider.set($scope.sliderValue); + } + }) init(); diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html index 638ecead4b..c147b30b23 100644 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html +++ b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/slider.html @@ -1,5 +1,12 @@ 
- +
+ + +
diff --git a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/tooltip.prevalues.html b/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/tooltip.prevalues.html deleted file mode 100644 index 415a39d3bd..0000000000 --- a/src/Umbraco.Web.UI.Client/src/views/propertyeditors/slider/tooltip.prevalues.html +++ /dev/null @@ -1,13 +0,0 @@ -
- - - - - Required - - -
diff --git a/src/Umbraco.Web/Actions/ActionCollection.cs b/src/Umbraco.Web/Actions/ActionCollection.cs index 0e33f03103..89ac8a59f4 100644 --- a/src/Umbraco.Web/Actions/ActionCollection.cs +++ b/src/Umbraco.Web/Actions/ActionCollection.cs @@ -21,18 +21,20 @@ namespace Umbraco.Web.Actions internal IEnumerable GetByLetters(IEnumerable letters) { + var actions = this.ToArray(); // no worry: internally, it's already an array return letters .Where(x => x.Length == 1) - .Select(x => this.FirstOrDefault(y => y.Letter == x[0])) + .Select(x => actions.FirstOrDefault(y => y.Letter == x[0])) .WhereNotNull() .ToList(); } internal IReadOnlyList FromEntityPermission(EntityPermission entityPermission) { + var actions = this.ToArray(); // no worry: internally, it's already an array return entityPermission.AssignedPermissions .Where(x => x.Length == 1) - .SelectMany(x => this.Where(y => y.Letter == x[0])) + .SelectMany(x => actions.Where(y => y.Letter == x[0])) .WhereNotNull() .ToList(); } diff --git a/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs b/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs index 55fa1c2099..ec1a9210a7 100644 --- a/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs +++ b/src/Umbraco.Web/Actions/ActionCollectionBuilder.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using Umbraco.Core.Composing; - namespace Umbraco.Web.Actions { internal class ActionCollectionBuilder : LazyCollectionBuilderBase diff --git a/src/Umbraco.Web/Composing/Composers/ControllersComposer.cs b/src/Umbraco.Web/Composing/Composers/ControllersComposer.cs index 83a52456a1..0565e0d863 100644 --- a/src/Umbraco.Web/Composing/Composers/ControllersComposer.cs +++ b/src/Umbraco.Web/Composing/Composers/ControllersComposer.cs @@ -1,44 +1,84 @@ -using System.Reflection; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; using System.Web.Http.Controllers; using System.Web.Mvc; using Umbraco.Core.Components; using Umbraco.Core.Composing; +using Umbraco.Web.Mvc; +using Umbraco.Web.WebApi; namespace Umbraco.Web.Composing.Composers { internal static class ControllersComposer { /// - /// Registers all IControllers using the TypeLoader for scanning and caching found instances for the calling assembly + /// Registers Umbraco controllers. /// - public static Composition ComposeMvcControllers(this Composition composition, Assembly assembly) + public static Composition ComposeUmbracoControllers(this Composition composition, Assembly umbracoWebAssembly) { - //TODO: We've already scanned for UmbracoApiControllers and SurfaceControllers - should we scan again - // for all controllers? Seems like we should just do this once and then filter. That said here we are - // only scanning our own single assembly. Hrm. + // notes + // + // We scan and auto-registers: + // - every IController and IHttpController that *we* have in Umbraco.Web + // - PluginController and UmbracoApiController in every assembly + // + // We do NOT scan: + // - any IController or IHttpController (anything not PluginController nor UmbracoApiController), outside of Umbraco.Web + // which means that users HAVE to explicitly register their own non-Umbraco controllers + // + // This is because we try to achieve a balance between "simple" and "fast. Scanning for PluginController or + // UmbracoApiController is fast-ish because they both are IDiscoverable. Scanning for IController or IHttpController + // is a full, non-cached scan = expensive, we do it only for 1 assembly. + // + // TODO + // find a way to scan for IController *and* IHttpController in one single pass + // or, actually register them manually so don't require a full scan for these + // 5 are IController but not PluginController + // Umbraco.Web.Mvc.RenderMvcController + // Umbraco.Web.Install.Controllers.InstallController + // Umbraco.Web.Macros.PartialViewMacroController + // Umbraco.Web.Editors.PreviewController + // Umbraco.Web.Editors.BackOfficeController + // 9 are IHttpController but not UmbracoApiController + // Umbraco.Web.Controllers.UmbProfileController + // Umbraco.Web.Controllers.UmbLoginStatusController + // Umbraco.Web.Controllers.UmbRegisterController + // Umbraco.Web.Controllers.UmbLoginController + // Umbraco.Web.Mvc.RenderMvcController + // Umbraco.Web.Install.Controllers.InstallController + // Umbraco.Web.Macros.PartialViewMacroController + // Umbraco.Web.Editors.PreviewController + // Umbraco.Web.Editors.BackOfficeController - composition.RegisterControllers(assembly); + // scan and register every IController in Umbraco.Web + var umbracoWebControllers = composition.TypeLoader.GetTypes(specificAssemblies: new[] { umbracoWebAssembly }); + //foreach (var controller in umbracoWebControllers.Where(x => !typeof(PluginController).IsAssignableFrom(x))) + // Current.Logger.Debug(typeof(LightInjectExtensions), "IController NOT PluginController: " + controller.FullName); + composition.RegisterControllers(umbracoWebControllers); + + // scan and register every PluginController in everything (PluginController is IDiscoverable and IController) + var nonUmbracoWebPluginController = composition.TypeLoader.GetTypes().Where(x => x.Assembly != umbracoWebAssembly); + composition.RegisterControllers(nonUmbracoWebPluginController); + + // scan and register every IHttpController in Umbraco.Web + var umbracoWebHttpControllers = composition.TypeLoader.GetTypes(specificAssemblies: new[] { umbracoWebAssembly }); + //foreach (var controller in umbracoWebControllers.Where(x => !typeof(UmbracoApiController).IsAssignableFrom(x))) + // Current.Logger.Debug(typeof(LightInjectExtensions), "IHttpController NOT UmbracoApiController: " + controller.FullName); + composition.RegisterControllers(umbracoWebHttpControllers); + + // scan and register every UmbracoApiController in everything (UmbracoApiController is IDiscoverable and IHttpController) + var nonUmbracoWebApiControllers = composition.TypeLoader.GetTypes().Where(x => x.Assembly != umbracoWebAssembly); + composition.RegisterControllers(nonUmbracoWebApiControllers); + return composition; } - /// - /// Registers all IHttpController using the TypeLoader for scanning and caching found instances for the calling assembly - /// - public static Composition ComposeApiControllers(this Composition composition, Assembly assembly) + private static void RegisterControllers(this Composition composition, IEnumerable controllerTypes) { - //TODO: We've already scanned for UmbracoApiControllers and SurfaceControllers - should we scan again - // for all controllers? Seems like we should just do this once and then filter. That said here we are - // only scanning our own single assembly. Hrm. - - composition.RegisterControllers(assembly); - return composition; - } - - private static void RegisterControllers(this Composition composition, Assembly assembly) - { - var types = composition.TypeLoader.GetTypes(specificAssemblies: new[] { assembly }); - foreach (var type in types) - composition.Register(type, Lifetime.Request); + foreach (var controllerType in controllerTypes) + composition.Register(controllerType, Lifetime.Request); } } } diff --git a/src/Umbraco.Web/Editors/DictionaryController.cs b/src/Umbraco.Web/Editors/DictionaryController.cs index 7d846e68ec..cd3141c7b9 100644 --- a/src/Umbraco.Web/Editors/DictionaryController.cs +++ b/src/Umbraco.Web/Editors/DictionaryController.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Net; using System.Net.Http; using System.Web.Http; @@ -194,7 +195,7 @@ namespace Umbraco.Web.Editors const int level = 0; - foreach (var dictionaryItem in Services.LocalizationService.GetRootDictionaryItems()) + foreach (var dictionaryItem in Services.LocalizationService.GetRootDictionaryItems().OrderBy(ItemSort())) { var item = Mapper.Map(dictionaryItem); item.Level = 0; @@ -220,8 +221,7 @@ namespace Umbraco.Web.Editors /// private void GetChildItemsForList(IDictionaryItem dictionaryItem, int level, List list) { - foreach (var childItem in Services.LocalizationService.GetDictionaryItemChildren( - dictionaryItem.Key)) + foreach (var childItem in Services.LocalizationService.GetDictionaryItemChildren(dictionaryItem.Key).OrderBy(ItemSort())) { var item = Mapper.Map(childItem); item.Level = level; @@ -230,5 +230,7 @@ namespace Umbraco.Web.Editors GetChildItemsForList(childItem, level + 1, list); } } + + private Func ItemSort() => item => item.ItemKey; } } diff --git a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs index 1e5dd51c7d..601692b0e9 100644 --- a/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs +++ b/src/Umbraco.Web/Runtime/WebRuntimeComponent.cs @@ -121,8 +121,7 @@ namespace Umbraco.Web.Runtime composition.ConfigureForWeb(); composition - .ComposeMvcControllers(GetType().Assembly) - .ComposeApiControllers(GetType().Assembly); + .ComposeUmbracoControllers(GetType().Assembly); composition.WithCollectionBuilder() .Add(() => composition.TypeLoader.GetTypes()); // fixme which searchable trees?! diff --git a/src/Umbraco.Web/Trees/DictionaryTreeController.cs b/src/Umbraco.Web/Trees/DictionaryTreeController.cs index d0a7fce3ad..cac2e7f435 100644 --- a/src/Umbraco.Web/Trees/DictionaryTreeController.cs +++ b/src/Umbraco.Web/Trees/DictionaryTreeController.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Net.Http.Formatting; using Umbraco.Core; +using Umbraco.Core.Models; using Umbraco.Core.Services; using Umbraco.Web.Actions; @@ -52,10 +53,12 @@ namespace Umbraco.Web.Trees var nodes = new TreeNodeCollection(); + Func ItemSort() => item => item.ItemKey; + if (id == Constants.System.Root.ToInvariantString()) { nodes.AddRange( - Services.LocalizationService.GetRootDictionaryItems().Select( + Services.LocalizationService.GetRootDictionaryItems().OrderBy(ItemSort()).Select( x => CreateTreeNode( x.Id.ToInvariantString(), id, @@ -71,7 +74,7 @@ namespace Umbraco.Web.Trees if (parentDictionary == null) return nodes; - nodes.AddRange(Services.LocalizationService.GetDictionaryItemChildren(parentDictionary.Key).ToList().OrderByDescending(item => item.Key).Select( + nodes.AddRange(Services.LocalizationService.GetDictionaryItemChildren(parentDictionary.Key).ToList().OrderBy(ItemSort()).Select( x => CreateTreeNode( x.Id.ToInvariantString(), id,