UNPKG

kdf

Version:

KD: a non-document focused UI Framework for web applications.

278 lines (194 loc) 7.37 kB
### todo: - make addLayer implementation more clear, by default adding a layer should set a listener for next ReceivedClickElsewhere and remove the layer automatically 2012/5/21 Sinan ### KDController = require './controller.coffee' module.exports = class KDWindowController extends KDController @keyViewHistory = [] superKey = if navigator.userAgent.indexOf("Mac OS X") is -1 then "ctrl" else "command" addListener = (eventName, listener, capturePhase = yes) -> window.addEventListener eventName, listener, capturePhase constructor:(options,data)-> @windowResizeListeners = {} @keyEventsToBeListened = ['keydown', 'keyup', 'keypress'] @currentCombos = {} @keyView = null @dragView = null @layers = [] @unloadListeners = {} @focusListeners = [] @focused = yes @bindEvents() super options, data addLayer: (layer)-> unless layer in @layers # log "layer added", layer @layers.push layer layer.on 'KDObjectWillBeDestroyed', => @removeLayer layer removeLayer: (layer)-> if layer in @layers # log "layer removed", layer index = @layers.indexOf(layer) @layers.splice index, 1 bindEvents:-> for eventName in @keyEventsToBeListened addEventListener eventName, @bound 'key' addEventListener 'resize', @bound 'notifyWindowResizeListeners' document.addEventListener 'scroll', do => timer = null {body} = document KD.utils.throttle (event)=> @emit "ScrollHappened", event , 50 , no addListener "dragenter", (event)=> unless @dragInAction @emit 'DragEnterOnWindow', event @setDragInAction yes addListener "dragleave", (event)=> unless 0 < event.clientX < @winWidth and 0 < event.clientY < @winHeight @emit 'DragExitOnWindow', event @setDragInAction no addListener "drop", (event)=> @emit 'DragExitOnWindow', event @emit 'DropOnWindow', event @setDragInAction no layers = @layers addListener 'mousedown', (e)=> lastLayer = layers.last if lastLayer and $(e.target).closest(lastLayer?.$()).length is 0 lastLayer.emit 'ReceivedClickElsewhere', e @removeLayer lastLayer addListener 'mouseup', (e)=> @unsetDragView e if @dragView @emit 'ReceivedMouseUpElsewhere', e addListener 'mousemove', (e)=> @redirectMouseMoveEvent e if @dragView # internal links (including "#") should prevent default, so we don't end # up with duplicate entries in history: e.g. /Activity and /Activity# # also so that we don't redirect the browser (addListener 'click', (e)-> nearestLink = KD.utils.getNearestElementByTagName e.target, 'a' if nearestLink?.target?.length is 0 # links with a target attribute should work as normal. href = nearestLink.getAttribute "href" isHttp = href?.indexOf("http") is 0 isMail = href?.indexOf("mailto") is 0 if isHttp nearestLink.target = "_blank" else if not isMail e.preventDefault() if href and not /^#/.test href KD.getSingleton("router").handleRoute href , no) addListener 'beforeunload', @bound 'beforeUnload' window.onfocus = (event) => @focusChange event, yes window.onblur = (event) => @focusChange event, no addUnloadListener:(key, listener)-> @unloadListeners[key] or= [] @unloadListeners[key].push listener clearUnloadListeners: (key)-> if key then @unloadListeners[key] = [] else @unloadListeners = {} isFocused: -> @focused addFocusListener: (listener)-> @focusListeners.push listener focusChange: (event, state)-> return unless event @focused = state listener state, event for listener in @focusListeners beforeUnload:(event)-> return unless event # all the listeners make their checks if it is safe or not to reload the page # they either return true or false if any of them returns false we intercept reload for own key, listeners of @unloadListeners for listener in listeners when listener() is off message = unless key is 'window' then " on #{key}" else '' return "Please make sure that you saved all your work#{message}." setDragInAction:(@dragInAction = no)-> document.body.classList[if @dragInAction then 'add' else 'remove'] 'dragInAction' setMainView:(@mainView)-> getMainView:(view)-> @mainView revertKeyView:(view)-> unless view warn 'view, which shouldn\'t be the keyview anymore, must be passed as a parameter!' return if view is @keyView and @keyView isnt @oldKeyView @setKeyView @oldKeyView superizeCombos = (combos)-> safeCombos = {} for own combo, cb of combos if /\bsuper(\+|\s)/.test combo combo = combo.replace /super/g, superKey safeCombos[combo] = cb return safeCombos viewHasKeyCombos:(view)-> return unless view o = view.getOptions() combos = {} for e in @keyEventsToBeListened if "object" is typeof o[e] for own combo, cb of o[e] combos[combo] = cb return if Object.keys(combos).length > 0 then combos else no registerKeyCombos:(view)-> combos = @viewHasKeyCombos view if combos? @comboMap = new KDKeyboardMap { combos } KDKeyboardListener.current().addComboMap @comboMap unregisterKeyCombos:-> KDKeyboardListener.current().removeComboMap @comboMap @keyView.unsetClass "mousetrap" if @keyView setKeyView:(keyView)-> keyView?.activateKeyView?() return if keyView is @keyView # unless keyView # log keyView, "keyView" if keyView @unregisterKeyCombos() @oldKeyView = @keyView @keyView = keyView @registerKeyCombos keyView @constructor.keyViewHistory.push keyView keyView?.activateKeyView?() @emit 'WindowChangeKeyView', keyView setDragView:(dragView)-> @setDragInAction yes @dragView = dragView unsetDragView:(e)-> @setDragInAction no @dragView.emit "DragFinished", e @dragView = null redirectMouseMoveEvent:(event)-> view = @dragView {pageX, pageY} = event {initial} = view.dragState.position initialX = initial.x initialY = initial.y delta = x : pageX - initialX y : pageY - initialY view.drag event, delta getKeyView:-> @keyView key:(event)-> # log event.type, @keyView.constructor.name, @keyView.getOptions().name # if Object.keys(@currentCombos).length > 0 # return yes # else @emit event.type, event @keyView?.handleEvent event registerWindowResizeListener:(instance)-> @windowResizeListeners[instance.id] = instance instance.on "KDObjectWillBeDestroyed", => @windowResizeListeners[instance.id] = null unregisterWindowResizeListener:(instance)-> @windowResizeListeners[instance.id] = null notifyWindowResizeListeners: (event)-> event or= type : "resize" for own key, inst of @windowResizeListeners when inst?._windowDidResize inst._windowDidResize event do -> KD = require './kd.coffee' KD.registerSingleton "windowController", new KDWindowController