diff --git a/src/Umbraco.Web.UI.Client/src/common/directives/hotkey.directive.js b/src/Umbraco.Web.UI.Client/src/common/directives/hotkey.directive.js new file mode 100644 index 0000000000..4406ad4edd --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/directives/hotkey.directive.js @@ -0,0 +1,28 @@ +/** +* @ngdoc directive +* @name umbraco.directives.directive:headline +**/ +angular.module("umbraco.directives") + .directive('hotkey', function ($window, keyboardService, $log) { + return function (scope, el, attrs) { + + var keyCombo = attrs["hotkey"]; + $log.log(keyCombo); + + keyboardService.bind(keyCombo, function() { + var element = $(el); + + $log.log(element); + + if(element.is("a,button,input[type='button'],input[type='submit']")){ + element.click(); + $log.log("click"); + + }else{ + element.focus(); + $log.log("focus"); + } + }); + + }; + }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/common/services/dialog.service.js b/src/Umbraco.Web.UI.Client/src/common/services/dialog.service.js index 353ca182db..b979b36632 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/dialog.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/dialog.service.js @@ -73,6 +73,7 @@ angular.module('umbraco.services') var $modal = $('
'); var id = templateUrl.replace('.html', '').replace('.aspx', '').replace(/[\/|\.|:\&\?\=]/g, "-") + '-' + scope.$id; + if(options.inline){ animationClass = ""; @@ -137,6 +138,8 @@ angular.module('umbraco.services') $compile($modal)(scope); }); + scope.dialogOptions = options; + //Scope to handle data from the modal form scope.dialogData = {}; scope.dialogData.selection = []; @@ -270,7 +273,7 @@ angular.module('umbraco.services') */ mediaPicker: function (options) { return openDialog({ - scope: options.scope, + scope: options.scope, callback: options.callback, template: 'views/common/dialogs/mediaPicker.html', show: true @@ -286,6 +289,7 @@ angular.module('umbraco.services') * Opens a content picker tree in a modal, the callback returns an array of selected documents * @param {Object} options content picker dialog options object * @param {$scope} options.scope dialog scope + * @param {$scope} options.multipicker should the picker return one or multiple items * @param {Function} options.callback callback function * @returns {Object} modal object */ @@ -293,6 +297,7 @@ angular.module('umbraco.services') return openDialog({ scope: options.scope, callback: options.callback, + multipicker: options.multipicker, template: 'views/common/dialogs/contentPicker.html', show: true }); diff --git a/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js b/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js new file mode 100644 index 0000000000..8328d3e476 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/common/services/keyboard.service.js @@ -0,0 +1,261 @@ +// This service was based on OpenJS library available in BSD License +// http://www.openjs.com/scripts/events/keyboard_shortcuts/index.php +angular.module('umbraco.services') +.factory('keyboardService', ['$window', '$timeout', function ($window, $timeout) { + var keyboardManagerService = {}; + var defaultOpt = { + 'type': 'keydown', + 'propagate': false, + 'inputDisabled': false, + 'target': $window.document, + 'keyCode': false + }; + + // Store all keyboard combination shortcuts + keyboardManagerService.keyboardEvent = {}; + + // Add a new keyboard combination shortcut + keyboardManagerService.bind = function (label, callback, opt) { + var fct, elt, code, k; + // Initialize opt object + opt = angular.extend({}, defaultOpt, opt); + label = label.toLowerCase(); + elt = opt.target; + if(typeof opt.target === 'string'){ + elt = document.getElementById(opt.target); + } + + fct = function (e) { + e = e || $window.event; + + // Disable event handler when focus input and textarea + if (opt['inputDisabled']) { + var elt; + if (e.target){ + elt = e.target; + }else if (e.srcElement){ + elt = e.srcElement; + } + + if (elt.nodeType === 3){elt = elt.parentNode;} + if (elt.tagName === 'INPUT' || elt.tagName === 'TEXTAREA'){return;} + } + + // Find out which key is pressed + if (e.keyCode){ + code = e.keyCode; + }else if (e.which){ + code = e.which; + } + + var character = String.fromCharCode(code).toLowerCase(); + + if (code === 188){character = ",";} // If the user presses , when the type is onkeydown + if (code === 190){character = ".";} // If the user presses , when the type is onkeydown + + var keys = label.split("+"); + // Key Pressed - counts the number of valid keypresses - if it is same as the number of keys, the shortcut function is invoked + var kp = 0; + // Work around for stupid Shift key bug created by using lowercase - as a result the shift+num combination was broken + var shift_nums = { + "`":"~", + "1":"!", + "2":"@", + "3":"#", + "4":"$", + "5":"%", + "6":"^", + "7":"&", + "8":"*", + "9":"(", + "0":")", + "-":"_", + "=":"+", + ";":":", + "'":"\"", + ",":"<", + ".":">", + "/":"?", + "\\":"|" + }; + // Special Keys - and their codes + var special_keys = { + 'esc':27, + 'escape':27, + 'tab':9, + 'space':32, + 'return':13, + 'enter':13, + 'backspace':8, + + 'scrolllock':145, + 'scroll_lock':145, + 'scroll':145, + 'capslock':20, + 'caps_lock':20, + 'caps':20, + 'numlock':144, + 'num_lock':144, + 'num':144, + + 'pause':19, + 'break':19, + + 'insert':45, + 'home':36, + 'delete':46, + 'end':35, + + 'pageup':33, + 'page_up':33, + 'pu':33, + + 'pagedown':34, + 'page_down':34, + 'pd':34, + + 'left':37, + 'up':38, + 'right':39, + 'down':40, + + 'f1':112, + 'f2':113, + 'f3':114, + 'f4':115, + 'f5':116, + 'f6':117, + 'f7':118, + 'f8':119, + 'f9':120, + 'f10':121, + 'f11':122, + 'f12':123 + }; + // Some modifiers key + var modifiers = { + shift: { + wanted: false, + pressed: e.shiftKey ? true : false + }, + ctrl : { + wanted: false, + pressed: e.ctrlKey ? true : false + }, + alt : { + wanted: false, + pressed: e.altKey ? true : false + }, + meta : { //Meta is Mac specific + wanted: false, + pressed: e.metaKey ? true : false + } + }; + // Foreach keys in label (split on +) + var l = keys.length; + for (var i = 0; i < l; i++) { + + var k=keys[i]; + switch (k) { + case 'ctrl': + case 'control': + kp++; + modifiers.ctrl.wanted = true; + break; + case 'shift': + case 'alt': + case 'meta': + kp++; + modifiers[k].wanted = true; + break; + } + + if (k.length > 1) { // If it is a special key + if(special_keys[k] === code){ + kp++; + } + + } else if (opt['keyCode']) { // If a specific key is set into the config + if (opt['keyCode'] === code) { + kp++; + } + + } else { // The special keys did not match + if(character === k) { + kp++; + }else { + if(shift_nums[character] && e.shiftKey) { // Stupid Shift key bug created by using lowercase + character = shift_nums[character]; + if(character === k){ + kp++; + } + } + } + } + + } //for end + + if(kp === keys.length && + modifiers.ctrl.pressed === modifiers.ctrl.wanted && + modifiers.shift.pressed === modifiers.shift.wanted && + modifiers.alt.pressed === modifiers.alt.wanted && + modifiers.meta.pressed === modifiers.meta.wanted) { + $timeout(function() { + callback(e); + }, 1); + + if(!opt['propagate']) { // Stop the event + // e.cancelBubble is supported by IE - this will kill the bubbling process. + e.cancelBubble = true; + e.returnValue = false; + + // e.stopPropagation works in Firefox. + if (e.stopPropagation) { + e.stopPropagation(); + e.preventDefault(); + } + return false; + } + } + + }; + // Store shortcut + keyboardManagerService.keyboardEvent[label] = { + 'callback': fct, + 'target': elt, + 'event': opt['type'] + }; + + //Attach the function with the event + if(elt.addEventListener){ + elt.addEventListener(opt['type'], fct, false); + }else if(elt.attachEvent){ + elt.attachEvent('on' + opt['type'], fct); + }else{ + elt['on' + opt['type']] = fct; + } + }; + // Remove the shortcut - just specify the shortcut and I will remove the binding + keyboardManagerService.unbind = function (label) { + label = label.toLowerCase(); + var binding = keyboardManagerService.keyboardEvent[label]; + delete(keyboardManagerService.keyboardEvent[label]); + + if(!binding){return;} + + var type = binding['event'], + elt = binding['target'], + callback = binding['callback']; + + if(elt.detachEvent){ + elt.detachEvent('on' + type, callback); + }else if(elt.removeEventListener){ + elt.removeEventListener(type, callback, false); + }else{ + elt['on'+type] = false; + } + }; + // + + return keyboardManagerService; +}]); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/belle.less b/src/Umbraco.Web.UI.Client/src/less/belle.less index 097e6ffeab..a33fea33b3 100644 --- a/src/Umbraco.Web.UI.Client/src/less/belle.less +++ b/src/Umbraco.Web.UI.Client/src/less/belle.less @@ -23,6 +23,7 @@ @import "../../lib/bootstrap/less/type.less"; @import "../../lib/bootstrap/less/code.less"; @import "tables.less"; +@import "colors.less"; // Components: common @import "../../lib/bootstrap/less/sprites.less"; diff --git a/src/Umbraco.Web.UI.Client/src/less/colors.less b/src/Umbraco.Web.UI.Client/src/less/colors.less new file mode 100644 index 0000000000..0b2d107c8f --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/less/colors.less @@ -0,0 +1 @@ +i.red{color: #b94a48;} \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/less/forms.less b/src/Umbraco.Web.UI.Client/src/less/forms.less index 03bed70197..f9b864b678 100644 --- a/src/Umbraco.Web.UI.Client/src/less/forms.less +++ b/src/Umbraco.Web.UI.Client/src/less/forms.less @@ -7,11 +7,16 @@ .hideLabel label { display: none; } - .hideLabel .controls-row { margin-left: 0px !Important; } +//utill styll to hide child untill hover +.hover-show{visibility: hidden;} +*:hover > .hover-show{visibility: visible;} + + + // GENERAL STYLES // -------------- @@ -45,6 +50,8 @@ legend { } } + + // Set font for forms label, input, diff --git a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/contentpicker.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/contentpicker.controller.js index 58705e35cc..9203a38e3c 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/dialogs/contentpicker.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/dialogs/contentpicker.controller.js @@ -1,15 +1,28 @@ //used for the media picker dialog angular.module("umbraco").controller("Umbraco.Dialogs.ContentPickerController", - function ($scope, eventsService) { + function ($scope, eventsService, $log) { + var dialogOptions = $scope.$parent.dialogOptions; $scope.dialogTreeEventHandler = $({}); + $log.log($scope); + $scope.dialogTreeEventHandler.bind("treeNodeSelect", function(ev, args){ args.event.preventDefault(); args.event.stopPropagation(); + eventsService.publish("Umbraco.Dialogs.ContentPickerController.Select", args, "hello").then(function(args){ - $(args.event.target.parentElement).find("i.umb-tree-icon").attr("class", "icon umb-tree-icon sprTree icon-check blue"); - $scope.select(args.node); - }); + if(dialogOptions && dialogOptions.multipicker){ + $(args.event.target.parentElement) + .find("i.umb-tree-icon") + .attr("class", "icon umb-tree-icon sprTree icon-check blue"); + + $scope.select(args.node); + }else{ + $scope.submit(args.node); + } + + }); + }); }); \ No newline at end of file diff --git a/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js b/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js index b3a873a658..6186cb7532 100644 --- a/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/common/navigation.controller.js @@ -9,13 +9,18 @@ * * @param {navigationService} navigationService A reference to the navigationService */ -function NavigationController($scope,$rootScope, $location, $log, navigationService, dialogService, historyService, sectionResource, angularHelper) { +function NavigationController($scope,$rootScope, $location, $log, navigationService, keyboardService, dialogService, historyService, sectionResource, angularHelper) { //Put the navigation service on this scope so we can use it's methods/properties in the view. // IMPORTANT: all properties assigned to this scope are generally available on the scope object on dialogs since // when we create a dialog we pass in this scope to be used for the dialog's scope instead of creating a new one. $scope.nav = navigationService; + //trigger search with a hotkey: + keyboardService.bind("ctrl+shift+s", function(){ + $scope.nav.showTree($scope.ui.currentSection); + }); + //the tree event handler i used to subscribe to the main tree click events $scope.treeEventHandler = $({}); diff --git a/src/Umbraco.Web.UI.Client/src/views/content/create.html b/src/Umbraco.Web.UI.Client/src/views/content/create.html index 0c7d43da3d..0ddd9dbc82 100644 --- a/src/Umbraco.Web.UI.Client/src/views/content/create.html +++ b/src/Umbraco.Web.UI.Client/src/views/content/create.html @@ -7,7 +7,7 @@