Commit 3e87c7cb authored by Ujvari Gergely's avatar Ujvari Gergely

Manually merged with the upstream/develop branch

parents 852084c7 aa8dd549
This diff is collapsed.
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
(function() {
var pfx = ['ms', 'moz', 'webkit', 'o'];
for (var i = 0; i < pfx.length && !window.requestAnimationFrame ; i++) {
requestAnimationFrame = window[pfx[i]+'RequestAnimationFrame'];
cancelAnimationFrame = window[pfx[i]+'CancelRequestAnimationFrame'];
if (!cancelAnimationFrame) {
cancelAnimationFrame = window[pfx[i]+'CancelAnimationFrame'];
}
window.requestAnimationFrame = requestAnimationFrame;
window.cancelAnimationFrame = cancelAnimationFrame;
}
if (!window.requestAnimationFrame)
var lastTime = 0;
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}())
......@@ -22,6 +22,12 @@ class Annotator.Host extends Annotator
# timer used to throttle event frequency
updateTimer: null
# Drag state variables
drag:
delta: 0
last: null
tick: false
constructor: (element, options) ->
super
......@@ -63,8 +69,22 @@ class Annotator.Host extends Annotator
setupAnnotation: => this.setupAnnotation arguments...
onEditorHide: this.onEditorHide
onEditorSubmit: this.onEditorSubmit
showFrame: => @frame.removeClass('annotator-collapsed')
hideFrame: => @frame.addClass('annotator-collapsed')
showFrame: =>
@frame.css 'margin-left': "#{-1 * @frame.width()}px"
@frame.removeClass 'annotator-no-transition'
@frame.removeClass 'annotator-collapsed'
hideFrame: =>
@frame.css 'margin-left': ''
@frame.removeClass 'annotator-no-transition'
@frame.addClass 'annotator-collapsed'
dragFrame: (screenX) =>
if screenX > 0
if @drag.last?
@drag.delta += screenX - @drag.last
@drag.last = screenX
unless @drag.tick
@drag.tick = true
window.requestAnimationFrame this.dragRefresh
getHighlights: =>
highlights: $(@wrapper).find('.annotator-hl').map ->
offset: $(this).offset()
......@@ -150,6 +170,20 @@ class Annotator.Host extends Annotator
document.addEventListener 'touchstart', =>
touch = true
do update
document.addEventListener 'dragover', (event) =>
if @drag.last?
@drag.delta += event.screenX - @drag.last
@drag.last = event.screenX
unless @drag.tick
@drag.tick = true
window.requestAnimationFrame this.dragRefresh
document.addEventListener 'dragleave', (event) =>
if @drag.last?
@drag.delta += event.screenX - @drag.last
@drag.last = event.screenX
unless @drag.tick
@drag.tick = true
window.requestAnimationFrame this.dragRefresh
$(window).on 'resize scroll', update
$(document.body).on 'resize scroll', '*', util.debounce => @consumer.update()
super
......@@ -161,6 +195,21 @@ class Annotator.Host extends Annotator
_setupEditor: ->
true
dragRefresh: =>
d = @drag.delta
@drag.delta = 0
@drag.tick = false
m = parseInt (getComputedStyle @frame[0]).marginLeft
w = -1 * m
m += d
w -= d
@frame.addClass 'annotator-no-transition'
@frame.css
'margin-left': "#{m}px"
width: "#{w}px"
showEditor: (annotation) =>
stub =
ranges: annotation.ranges
......@@ -174,8 +223,8 @@ class Annotator.Host extends Annotator
@consumer.showEditor stub
checkForStartSelection: (event) =>
# Annotator chokes on this callback hen trying to access
# this.viewer.hide since there is no viewer. The `mouseIsDown` state
# Override to prevent Annotator choking when this ties to access the
# viewer but preserve the manipulation of the attribute `mouseIsDown` which
# is needed for preventing the sidebar from closing while annotating.
unless event and this.isAnnotator(event.target)
@mouseIsDown = true
angular.module 'h', [
'deform'
'h.controllers'
'h.directives'
'h.services'
]
class Hypothesis extends Annotator
# Annotator state variables.
this::bucket = -1 # * The index of the bucket shown in the summary view
this::detail = false # * Whether the viewer shows a summary or detail listing
this::hash = -1 # * cheap UUID :cake:
this::cache = {} # * object cache
this::visible = false # * Whether the sidebar is visible
this::unsaved_drafts = [] # * Unsaved drafts currenty open
# Plugin configuration
options:
Heatmap: {}
......@@ -15,10 +7,31 @@ class Hypothesis extends Annotator
showViewPermissionsCheckbox: false,
userString: (user) -> user.replace(/^acct:(.+)@(.+)$/, '$1 on $2')
constructor: (element, options) ->
# Internal state
bucket: -1 # * The index of the bucket shown in the summary view
detail: false # * Whether the viewer shows a summary or detail listing
hash: -1 # * cheap UUID :cake:
cache: {} # * object cache
visible: false # * Whether the sidebar is visible
unsaved_drafts: [] # * Unsaved drafts currenty open
this.$inject = ['$rootElement', '$scope', '$compile', '$http']
constructor: (@element, @scope, @compile, @http) ->
super @element, @options
# Load plugins
for own name, opts of @options
if not @plugins[name] and name of Annotator.Plugin
this.addPlugin(name, opts)
# Export public, bound functions on the scope
for own key of this
if typeof this[key] == 'function' and not key.match('^_')
@scope[key] = this[key]
# Establish cross-domain communication to the widget host
@provider = new easyXDM.Rpc
swf: options.swf
swf: @options.swf
onReady: this._initialize
,
local:
......@@ -44,11 +57,11 @@ class Hypothesis extends Annotator
if @plugins.Permissions.user?
this.showEditor annotation
else
showAuth true
@editor.hide()
if @unsaved_drafts.indexOf(@editor) > -1 then @unsaved_drafts.splice(@unsaved_drafts.indexOf(@editor),1)
this.show()
# This guy does stuff when you "back out" of the interface.
this.showAuth true
#this.show()
# This guy does stuff when you "back out" of the interface.
# (Currently triggered by a click on the source page.)
back: =>
# If it's in the detail view, loads the bucket back up.
......@@ -67,6 +80,7 @@ class Hypothesis extends Annotator
onEditorSubmit: {}
showFrame: {}
hideFrame: {}
dragFrame: {}
getHighlights: {}
setActiveHighlights: {}
getMaxBottom: {}
......@@ -78,12 +92,48 @@ class Hypothesis extends Annotator
@renderer.hooks.chain "postConversion", (text) ->
text.replace /<a href=/, "<a target=\"_blank\" href="
super
@scope.$watch 'personas', (newValue, oldValue) =>
if newValue?.length
@element.find('#persona')
.off('change').on('change', -> $(this).submit())
.off('click')
this.showAuth(false)
else
@scope.persona = null
@scope.token = null
@element.find('#persona').off('click').on('click', => this.showAuth())
@scope.$watch 'persona', (newValue, oldValue) =>
if oldValue? and not newValue?
@http.post 'logout', '',
withCredentials: true
.success (data) => @scope.reset()
@scope.$watch 'token', (newValue, oldValue) =>
if @plugins.Auth?
@plugins.Auth.token = newValue
@plugins.Auth.updateHeaders()
if newValue?
if not @plugins.Auth?
this.addPlugin 'Auth',
tokenUrl: @scope.tokenUrl
token: newValue
else
@plugins.Auth.setToken(newValue)
@plugins.Auth.withToken @plugins.Permissions._setAuthFromToken
else
@plugins.Permissions.setUser(null)
delete @plugins.Auth
# Load plugins
for own name, opts of @options
if not @plugins[name] and name of Annotator.Plugin
this.addPlugin(name, opts)
# Fetch the initial model from the server
@http.get 'model'
withCredentials: true
.success (data) =>
angular.extend @scope, data
this.reset()
this.showAuth false
this
......@@ -95,8 +145,8 @@ class Hypothesis extends Annotator
@viewer.show()
@provider.getMaxBottom (max) =>
$('#toolbar').css("top", "#{max}px")
$('#gutter').css("padding-top", "#{max}px")
@element.find('#toolbar').css("top", "#{max}px")
@element.find('#gutter').css("padding-top", "#{max}px")
@heatmap.BUCKET_THRESHOLD_PAD = (
max + @heatmap.constructor.prototype.BUCKET_THRESHOLD_PAD
)
......@@ -109,7 +159,7 @@ class Hypothesis extends Annotator
this.publish 'hostUpdated'
_setupWrapper: ->
@wrapper = $('#wrapper')
@wrapper = @element.find('#wrapper')
.on 'mousewheel', (event, delta) ->
# prevent overscroll from scrolling host frame
# http://stackoverflow.com/questions/5802467
......@@ -122,13 +172,30 @@ class Hypothesis extends Annotator
this
_setupDocumentEvents: ->
$('#toolbar .tri').click =>
@element.find('#toolbar .tri').click =>
if @visible
this.hide()
else
if @viewer.isShown() and @bucket == -1
this._fillDynamicBucket()
this.show()
el = document.createElementNS 'http://www.w3.org/1999/xhtml', 'canvas'
el.width = el.height = 1
@element.append el
handle = @element.find('#toolbar .tri')[0]
handle.addEventListener 'dragstart', (event) =>
event.dataTransfer.setData 'text/plain', ''
event.dataTransfer.setDragImage(el, 0, 0)
@provider.dragFrame event.screenX
handle.addEventListener 'dragend', (event) =>
@provider.dragFrame event.screenX
@element[0].addEventListener 'dragover', (event) =>
@provider.dragFrame event.screenX
@element[0].addEventListener 'dragleave', (event) =>
@provider.dragFrame event.screenX
this
_setupDynamicStyle: ->
......@@ -265,8 +332,8 @@ class Hypothesis extends Annotator
# Returns itself for chaining.
_setupEditor: ->
@editor = this._createEditor()
.on('hide', @provider.onEditorHide)
.on('save', @provider.onEditorSubmit)
.on('hide', => @provider.onEditorHide)
.on('save', => @provider.onEditorSubmit)
this
_createEditor: ->
......@@ -480,7 +547,7 @@ class Hypothesis extends Annotator
animate parent.classed('collapsed', !collapsed)
when '#reply'
unless @plugins.Permissions?.user
showAuth true
this.showAuth true
break
d3.event.preventDefault()
parent = d3.select(event.currentTarget)
......@@ -561,13 +628,16 @@ class Hypothesis extends Annotator
@visible = true
@provider.setActiveHighlights annotations
@provider.showFrame()
$("#toolbar").addClass "shown"
@element.find('#toolbar').addClass('shown')
.find('.tri').attr('draggable', true)
hide: =>
@lastWidth = window.innerWidth
@visible = false
@provider.setActiveHighlights []
@provider.hideFrame()
$("#toolbar").removeClass "shown"
@element.find('#toolbar').removeClass('shown')
.find('.tri').attr('draggable', false)
_canCloseUnsaved: ->
#See if there's an unsaved/uncancelled reply
......@@ -591,4 +661,49 @@ class Hypothesis extends Annotator
else
annotation.id
window.Hypothesis = Hypothesis
showAuth: (show=true) =>
$header = @element.find('header')
visible = !$header.hasClass('collapsed')
if (visible != show)
if (show)
$header.removeClass('collapsed')
else
$header.addClass('collapsed')
$header.one 'webkitTransitionEnd transitionend OTransitionEnd', =>
$header.find('.nav-tabs > li.active > a').trigger('shown')
submit: =>
controls = @element.find('.sheet .active form').formSerialize()
@http.post '', controls,
headers:
'Content-Type': 'application/x-www-form-urlencoded'
withCredentials: true
.success (data) =>
# Extend the scope with updated model data
angular.extend(@scope, data.model)
# Replace any forms which were re-rendered in this response
for oid of data.form
target = '#' + oid
$form = $(data.form[oid])
$form.replaceAll(target)
link = @compile $form
link @scope
deform.focusFirstInput target
reset: =>
angular.extend @scope,
username: null
password: null
email: null
code: null
personas: []
persona: null
token: null
angular.module('h.controllers', [])
.controller('Hypothesis', Hypothesis)
# Extend deform to allow targeting focusing of input when multiple forms
# are on the same page. See https://github.com/Pylons/deform/pull/128
angular.extend deform,
focusFirstInput: (el) ->
el = el || document.body
input = $(el).find(':input')
.filter('[id ^= deformField]')
.filter('[type != hidden]')
.first()
raw = input?.get(0)
if raw?.type in ['text', 'file', 'password', 'textarea']
if raw.className != "hasDatepicker" then input.focus()
# AngularJS directive that creates data bindings for named controls on forms
# with the 'deform' class and triggers deform callbacks during linking.
deformDirective = ->
compile: (tElement, tAttrs, transclude) ->
# Capture the initial values from the server-side render and set a model
# binding for all the named controls.
initValues = {}
$controls = tElement.find('input,select,textarea').filter('[name]')
.filter ->
# Ignore private members (like __formid__) and hidden fields
# (angular does not data-bind hidden fields)
not (this.name.match '^_' or this.type is hidden)
.each ->
initValues[this.name] =
if this.tagName == 'select'
options = []
selected = null
$(this)
.attr(
'data-ng-options',
"value for value in #{this.name}.values"
)
.find('option').filter('[value!=""]')
.each (i) ->
if this.selected then selected = i
options.push
label: this.label or this.innerText
value: this.value
.remove()
options: options
selected: selected
else
this.value
$(this).attr 'data-ng-model', "#{this.name}"
# Link function
(scope, iElement, iAttrs, controller) ->
deform.processCallbacks()
restrict: 'C'
require: 'form'
scope: false
angular.module('deform', []).config [
'$provide', '$compileProvider', '$filterProvider',
($provide, $compileProvider, $filterProvider) ->
# Process any pending callbacks from the initial load
deform.processCallbacks()
# Register the deform service and directive
$provide.value 'deform', deform
$compileProvider.directive 'deform', deformDirective
]
navTabsDirective = (deform) ->
link: (scope, iElement, iAttrs, controller) ->
iElement.find('a')
# Focus the first form element when showing a tab pane
.on 'shown', (e) ->
target = $(e.target).data('target')
deform.focusFirstInput(target)
# Always show the first pane to start
.first().tab('show')
restrict: 'C'
navTabsDirective.$inject = ['deform']
angular.module('h.directives', ['deform'])
.directive('navTabs', navTabsDirective)
angular.module('h.services', [])
......@@ -24,13 +24,13 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
//HIDDEN TABS////////////////////////////////
.nav a[data-target="#password-tab"],
.nav a[data-target="#reset-tab"] {
.nav a[data-target="#activate-tab"],
.nav a[data-target="#forgot-tab"] {
display: none;
}
.nav .active a[data-target="#password-tab"],
.nav .active a[data-target="#reset-tab"] {
.nav .active a[data-target="#activate-tab"],
.nav .active a[data-target="#forgot-tab"] {
display: initial;
}
......@@ -236,7 +236,7 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
float: left;
width: $baseLineHeight;
height: $baseLineHeight;
cursor: pointer;
cursor: w-resize;
}
&.shown {
......@@ -268,7 +268,7 @@ svg { -webkit-tap-highlight-color: rgba(255, 255, 255, 0); }
}
}
&.open .provider {
.dropdown.open .provider {
display: inline;
}
}
......
......@@ -108,10 +108,13 @@
&.annotator-collapsed {
margin-left: -$heatmap-width - 17px;
}
}
.annotator-no-transition {
@include transition(none !important);
}
/*
Mobile layout
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment