Commit 330e9b9e authored by Robert Knight's avatar Robert Knight

Convert app.coffee, widget-controller.coffee to JS and cleanup.

In preparation for upcoming changes as part of direct linking,
convert the entry point and top-level controllers for the app
to JS.

 * Use a CommonJS require to bring in the 'vendor/jwz' dependency.
parent 0a60f51d
......@@ -67,7 +67,7 @@ var appBundles = [{
// The sidebar application for displaying and editing annotations
name: 'app',
transforms: ['coffee'],
entry: './h/static/scripts/app.coffee',
entry: './h/static/scripts/app',
},{
// The Annotator library which provides annotation controls on
// the page and sets up the sidebar
......
require('./polyfills')
# Initialize Raven. This is required at the top of this file
# so that it happens early in the app's startup flow
settings = require('./settings')(document)
if settings.raven
raven = require('./raven')
raven.init(settings.raven)
angular = require('angular')
# autofill-event relies on the existence of window.angular so
# it must be require'd after angular is first require'd
require('autofill-event')
# Setup Angular integration for Raven
if settings.raven
raven.angularModule(angular)
else
angular.module('ngRaven', [])
streamer = require('./streamer')
resolve =
# Ensure that we have available a) the current authenticated userid, and b)
# the list of user groups.
sessionState: ['session', (session) -> session.load()]
store: ['store', (store) -> store.$promise]
streamer: streamer.connect
threading: [
'annotationMapper', 'drafts', 'threading'
(annotationMapper, drafts, threading) ->
# Unload all the annotations
annotationMapper.unloadAnnotations(threading.annotationList())
# Reset the threading root
threading.createIdTable([])
threading.root = mail.messageContainer()
# Reload all new, unsaved annotations
threading.thread(drafts.unsaved())
return threading
]
configureLocation = ['$locationProvider', ($locationProvider) ->
# Use HTML5 history
$locationProvider.html5Mode(true)
]
configureRoutes = ['$routeProvider', ($routeProvider) ->
$routeProvider.when '/a/:id',
controller: 'AnnotationViewerController'
templateUrl: 'viewer.html'
reloadOnSearch: false
resolve: resolve
$routeProvider.when '/viewer',
controller: 'WidgetController'
templateUrl: 'viewer.html'
reloadOnSearch: false
resolve: resolve
$routeProvider.when '/stream',
controller: 'StreamController'
templateUrl: 'viewer.html'
reloadOnSearch: false
resolve: resolve
$routeProvider.otherwise
redirectTo: '/viewer'
]
setupCrossFrame = ['crossframe', (crossframe) -> crossframe.connect()]
setupHttp = ['$http', ($http) ->
$http.defaults.headers.common['X-Client-Id'] = streamer.clientId
]
setupHost = ['host', (host) -> ]
module.exports = angular.module('h', [
# Angular addons which export the Angular module name
# via module.exports
require('angular-jwt')
require('angular-resource')
require('angular-route')
require('angular-sanitize')
require('angular-toastr')
# Angular addons which do not export the Angular module
# name via module.exports
['angulartics', require('angulartics')][0]
['angulartics.google.analytics', require('angulartics/src/angulartics-ga')][0]
['ngTagsInput', require('ng-tags-input')][0]
['ui.bootstrap', require('./vendor/ui-bootstrap-custom-tpls-0.13.4')][0]
# Local addons
'ngRaven'
])
.controller('AppController', require('./app-controller'))
.controller('AnnotationUIController', require('./annotation-ui-controller'))
.controller('AnnotationViewerController', require('./annotation-viewer-controller'))
.controller('StreamController', require('./stream-controller'))
.controller('WidgetController', require('./widget-controller'))
.directive('annotation', require('./directive/annotation').directive)
.directive('deepCount', require('./directive/deep-count'))
.directive('excerpt', require('./directive/excerpt').directive)
.directive('formInput', require('./directive/form-input'))
.directive('formValidate', require('./directive/form-validate'))
.directive('groupList', require('./directive/group-list').directive)
.directive('hAutofocus', require('./directive/h-autofocus'))
.directive('loginForm', require('./directive/login-form').directive)
.directive('markdown', require('./directive/markdown'))
.directive('simpleSearch', require('./directive/simple-search'))
.directive('statusButton', require('./directive/status-button'))
.directive('thread', require('./directive/thread'))
.directive('threadFilter', require('./directive/thread-filter'))
.directive('spinner', require('./directive/spinner'))
.directive('shareDialog', require('./directive/share-dialog'))
.directive('windowScroll', require('./directive/window-scroll'))
.directive('dropdownMenuBtn', require('./directive/dropdown-menu-btn'))
.directive('publishAnnotationBtn', require('./directive/publish-annotation-btn'))
.directive('searchStatusBar', require('./directive/search-status-bar'))
.directive('sidebarTutorial', require('./directive/sidebar-tutorial').directive)
.directive('signinControl', require('./directive/signin-control'))
.directive('sortDropdown', require('./directive/sort-dropdown'))
.directive('topBar', require('./directive/top-bar'))
.filter('converter', require('./filter/converter'))
.provider('identity', require('./identity'))
.service('annotationMapper', require('./annotation-mapper'))
.service('annotationUI', require('./annotation-ui'))
.service('auth', require('./auth'))
.service('bridge', require('./bridge'))
.service('crossframe', require('./cross-frame'))
.service('drafts', require('./drafts'))
.service('features', require('./features'))
.service('flash', require('./flash'))
.service('formRespond', require('./form-respond'))
.service('groups', require('./groups'))
.service('host', require('./host'))
.service('localStorage', require('./local-storage'))
.service('permissions', require('./permissions'))
.service('queryParser', require('./query-parser'))
.service('render', require('./render'))
.service('searchFilter', require('./search-filter'))
.service('session', require('./session'))
.service('streamFilter', require('./stream-filter'))
.service('tags', require('./tags'))
.service('threading', require('./threading'))
.service('unicode', require('./unicode'))
.service('viewFilter', require('./view-filter'))
.factory('store', require('./store'))
.value('AnnotationSync', require('./annotation-sync'))
.value('AnnotationUISync', require('./annotation-ui-sync'))
.value('Discovery', require('./discovery'))
.value('raven', require('./raven'))
.value('settings', settings)
.value('time', require('./time'))
.config(configureLocation)
.config(configureRoutes)
.run(setupCrossFrame)
.run(setupHttp)
.run(setupHost)
require('./config/module')
'use strict';
require('./polyfills');
// Initialize Raven. This is required at the top of this file
// so that it happens early in the app's startup flow
var settings = require('./settings')(document);
if (settings.raven) {
var raven = require('./raven');
raven.init(settings.raven);
}
var angular = require('angular');
// autofill-event relies on the existence of window.angular so
// it must be require'd after angular is first require'd
require('autofill-event');
// Setup Angular integration for Raven
if (settings.raven) {
raven.angularModule(angular);
} else {
angular.module('ngRaven', []);
}
var mail = require('./vendor/jwz');
var streamer = require('./streamer');
var resolve =
// Ensure that we have available a) the current authenticated userid, and b)
// the list of user groups.
{
sessionState: ['session', function (session) { return session.load(); }],
store: ['store', function (store) { return store.$promise; }],
streamer: streamer.connect,
threading: [
'annotationMapper', 'drafts', 'threading',
function (annotationMapper, drafts, threading) {
// Unload all the annotations
annotationMapper.unloadAnnotations(threading.annotationList());
// Reset the threading root
threading.createIdTable([]);
threading.root = mail.messageContainer();
// Reload all new, unsaved annotations
threading.thread(drafts.unsaved());
return threading;
}
]
};
// @ngInject
function configureLocation($locationProvider) {
// Use HTML5 history
return $locationProvider.html5Mode(true);
}
// @ngInject
function configureRoutes($routeProvider) {
$routeProvider.when('/a/:id',
{
controller: 'AnnotationViewerController',
templateUrl: 'viewer.html',
reloadOnSearch: false,
resolve: resolve
});
$routeProvider.when('/viewer',
{
controller: 'WidgetController',
templateUrl: 'viewer.html',
reloadOnSearch: false,
resolve: resolve
});
$routeProvider.when('/stream',
{
controller: 'StreamController',
templateUrl: 'viewer.html',
reloadOnSearch: false,
resolve: resolve
});
return $routeProvider.otherwise({
redirectTo: '/viewer'
});
}
// @ngInject
function setupCrossFrame(crossframe) {
return crossframe.connect();
}
// @ngInject
function setupHttp($http) {
$http.defaults.headers.common['X-Client-Id'] = streamer.clientId;
}
module.exports = angular.module('h', [
// Angular addons which export the Angular module name
// via module.exports
require('angular-jwt'),
require('angular-resource'),
require('angular-route'),
require('angular-sanitize'),
require('angular-toastr'),
// Angular addons which do not export the Angular module
// name via module.exports
['angulartics', require('angulartics')][0],
['angulartics.google.analytics', require('angulartics/src/angulartics-ga')][0],
['ngTagsInput', require('ng-tags-input')][0],
['ui.bootstrap', require('./vendor/ui-bootstrap-custom-tpls-0.13.4')][0],
// Local addons
'ngRaven'
])
.controller('AppController', require('./app-controller'))
.controller('AnnotationUIController', require('./annotation-ui-controller'))
.controller('AnnotationViewerController', require('./annotation-viewer-controller'))
.controller('StreamController', require('./stream-controller'))
.controller('WidgetController', require('./widget-controller'))
.directive('annotation', require('./directive/annotation').directive)
.directive('deepCount', require('./directive/deep-count'))
.directive('excerpt', require('./directive/excerpt').directive)
.directive('formInput', require('./directive/form-input'))
.directive('formValidate', require('./directive/form-validate'))
.directive('groupList', require('./directive/group-list').directive)
.directive('hAutofocus', require('./directive/h-autofocus'))
.directive('loginForm', require('./directive/login-form').directive)
.directive('markdown', require('./directive/markdown'))
.directive('simpleSearch', require('./directive/simple-search'))
.directive('statusButton', require('./directive/status-button'))
.directive('thread', require('./directive/thread'))
.directive('threadFilter', require('./directive/thread-filter'))
.directive('spinner', require('./directive/spinner'))
.directive('shareDialog', require('./directive/share-dialog'))
.directive('windowScroll', require('./directive/window-scroll'))
.directive('dropdownMenuBtn', require('./directive/dropdown-menu-btn'))
.directive('publishAnnotationBtn', require('./directive/publish-annotation-btn'))
.directive('searchStatusBar', require('./directive/search-status-bar'))
.directive('sidebarTutorial', require('./directive/sidebar-tutorial').directive)
.directive('signinControl', require('./directive/signin-control'))
.directive('sortDropdown', require('./directive/sort-dropdown'))
.directive('topBar', require('./directive/top-bar'))
.filter('converter', require('./filter/converter'))
.provider('identity', require('./identity'))
.service('annotationMapper', require('./annotation-mapper'))
.service('annotationUI', require('./annotation-ui'))
.service('auth', require('./auth'))
.service('bridge', require('./bridge'))
.service('crossframe', require('./cross-frame'))
.service('drafts', require('./drafts'))
.service('features', require('./features'))
.service('flash', require('./flash'))
.service('formRespond', require('./form-respond'))
.service('groups', require('./groups'))
.service('host', require('./host'))
.service('localStorage', require('./local-storage'))
.service('permissions', require('./permissions'))
.service('queryParser', require('./query-parser'))
.service('render', require('./render'))
.service('searchFilter', require('./search-filter'))
.service('session', require('./session'))
.service('streamFilter', require('./stream-filter'))
.service('tags', require('./tags'))
.service('threading', require('./threading'))
.service('unicode', require('./unicode'))
.service('viewFilter', require('./view-filter'))
.factory('store', require('./store'))
.value('AnnotationSync', require('./annotation-sync'))
.value('AnnotationUISync', require('./annotation-ui-sync'))
.value('Discovery', require('./discovery'))
.value('raven', require('./raven'))
.value('settings', settings)
.value('time', require('./time'))
.config(configureLocation)
.config(configureRoutes)
.run(setupCrossFrame)
.run(setupHttp);
require('./config/module');
{module, inject} = angular.mock
events = require('../events')
describe 'WidgetController', ->
$scope = null
$rootScope = null
fakeAnnotationMapper = null
fakeAnnotationUI = null
fakeAuth = null
fakeCrossFrame = null
fakeDrafts = null
fakeStore = null
fakeStreamer = null
fakeStreamFilter = null
fakeThreading = null
fakeGroups = null
lastSearchResult = null
sandbox = null
viewer = null
before ->
angular.module('h', [])
.controller('WidgetController', require('../widget-controller.coffee'))
beforeEach module('h')
beforeEach module ($provide) ->
sandbox = sinon.sandbox.create()
fakeAnnotationMapper = {
loadAnnotations: sandbox.spy()
unloadAnnotations: sandbox.spy()
}
fakeAnnotationUI = {
tool: 'comment'
clearSelectedAnnotations: sandbox.spy()
}
fakeAuth = {user: null}
fakeCrossFrame = {frames: []}
fakeDrafts = {
unsaved: sandbox.stub()
}
fakeStore = {
SearchResource:
get: (query, callback) ->
offset = query.offset or 0
limit = query.limit or 20
result =
total: 100
rows: [offset..offset+limit-1]
replies: []
callback result
}
fakeStreamer = {
setConfig: sandbox.spy()
}
fakeStreamFilter = {
resetFilter: sandbox.stub().returnsThis()
addClause: sandbox.stub().returnsThis()
getFilter: sandbox.stub().returns({})
}
fakeThreading = {
root: {},
thread: sandbox.stub()
}
fakeGroups = {
focused: -> {id: 'foo'}
}
$provide.value 'annotationMapper', fakeAnnotationMapper
$provide.value 'annotationUI', fakeAnnotationUI
$provide.value 'crossframe', fakeCrossFrame
$provide.value 'drafts', fakeDrafts
$provide.value 'store', fakeStore
$provide.value 'streamer', fakeStreamer
$provide.value 'streamFilter', fakeStreamFilter
$provide.value 'threading', fakeThreading
$provide.value 'groups', fakeGroups
return
beforeEach inject ($controller, _$rootScope_) ->
$rootScope = _$rootScope_
$scope = $rootScope.$new()
viewer = $controller 'WidgetController', {$scope}
afterEach ->
sandbox.restore()
describe 'loadAnnotations', ->
it 'loads all annotations for a frame', ->
viewer.chunkSize = 20
fakeCrossFrame.frames.push({uri: 'http://example.com'})
$scope.$digest()
loadSpy = fakeAnnotationMapper.loadAnnotations
assert.callCount(loadSpy, 5)
assert.calledWith(loadSpy, [0..19])
assert.calledWith(loadSpy, [20..39])
assert.calledWith(loadSpy, [40..59])
assert.calledWith(loadSpy, [60..79])
assert.calledWith(loadSpy, [80..99])
it 'passes _separate_replies: true to the search API', ->
fakeStore.SearchResource.get = sandbox.stub()
fakeCrossFrame.frames.push({uri: 'http://example.com'})
$scope.$digest()
assert.equal(
fakeStore.SearchResource.get.firstCall.args[0]._separate_replies, true)
it 'passes annotations and replies from search to loadAnnotations()', ->
fakeStore.SearchResource.get = (query, callback) ->
callback({
rows: ['annotation_1', 'annotation_2']
replies: ['reply_1', 'reply_2', 'reply_3']
})
fakeCrossFrame.frames.push({uri: 'http://example.com'})
$scope.$digest()
assert fakeAnnotationMapper.loadAnnotations.calledOnce
assert fakeAnnotationMapper.loadAnnotations.calledWith(
['annotation_1', 'annotation_2'], ['reply_1', 'reply_2', 'reply_3']
)
describe 'when the focused group changes', ->
it 'should load annotations for the new group', ->
fakeThreading.annotationList = sandbox.stub().returns([{id: '1'}])
fakeCrossFrame.frames.push({uri: 'http://example.com'})
searchResult = {total: 10, rows: [0..10], replies: []}
fakeStore.SearchResource.get = (query, callback) ->
callback(searchResult)
$scope.$broadcast(events.GROUP_FOCUSED)
assert.calledWith(fakeAnnotationMapper.unloadAnnotations,
[{id: '1'}])
$scope.$digest();
assert.calledWith(fakeAnnotationMapper.loadAnnotations,
searchResult.rows)
assert.calledWith(fakeThreading.thread, fakeDrafts.unsaved())
describe 'when a new annotation is created', ->
###*
# It should clear any selection that exists in the sidebar before
# creating a new annotation. Otherwise the new annotation with its
# form open for the user to type in won't be visible because it's
# not part of the selection.
###
it 'clears the selection', ->
$scope.clearSelection = sinon.stub()
$rootScope.$emit('beforeAnnotationCreated', {})
assert.called($scope.clearSelection)
it 'does not clear the selection if the new annotation is a highlight', ->
$scope.clearSelection = sinon.stub()
$rootScope.$emit('beforeAnnotationCreated', {$highlight: true})
assert.notCalled($scope.clearSelection)
it 'does not clear the selection if the new annotation is a reply', ->
$scope.clearSelection = sinon.stub()
$rootScope.$emit('beforeAnnotationCreated', {
references: ['parent-id']
})
assert.notCalled($scope.clearSelection)
'use strict';
var angular = require('angular');
var events = require('../events');
describe('WidgetController', function () {
var $scope = null;
var $rootScope = null;
var fakeAnnotationMapper = null;
var fakeAnnotationUI = null;
var fakeAuth = null;
var fakeCrossFrame = null;
var fakeDrafts = null;
var fakeStore = null;
var fakeStreamer = null;
var fakeStreamFilter = null;
var fakeThreading = null;
var fakeGroups = null;
var sandbox = null;
var viewer = null;
before(function () {
angular.module('h', [])
.controller('WidgetController', require('../widget-controller'));
});
beforeEach(angular.mock.module('h'));
beforeEach(angular.mock.module(function ($provide) {
sandbox = sinon.sandbox.create();
fakeAnnotationMapper = {
loadAnnotations: sandbox.spy(),
unloadAnnotations: sandbox.spy()
};
fakeAnnotationUI = {
tool: 'comment',
clearSelectedAnnotations: sandbox.spy()
};
fakeAuth = {user: null};
fakeCrossFrame = {frames: []};
fakeDrafts = {
unsaved: sandbox.stub()
};
fakeStore = {
SearchResource: {
get: function (query, callback) {
var offset = query.offset || 0;
var limit = query.limit || 20;
var result =
{
total: 100,
rows: ((function () {
var result1 = [];
var end = offset + limit - 1;
var i = offset;
if (offset <= end) {
while (i <= end) {
result1.push(i++);
}
} else {
while (i >= end) {
result1.push(i--);
}
}
return result1;
})()),
replies: []
};
return callback(result);
}
},
};
fakeStreamer = {
setConfig: sandbox.spy()
};
fakeStreamFilter = {
resetFilter: sandbox.stub().returnsThis(),
addClause: sandbox.stub().returnsThis(),
getFilter: sandbox.stub().returns({})
};
fakeThreading = {
root: {},
thread: sandbox.stub()
};
fakeGroups = {
focused: function () { return {id: 'foo'}; }
};
$provide.value('annotationMapper', fakeAnnotationMapper);
$provide.value('annotationUI', fakeAnnotationUI);
$provide.value('crossframe', fakeCrossFrame);
$provide.value('drafts', fakeDrafts);
$provide.value('store', fakeStore);
$provide.value('streamer', fakeStreamer);
$provide.value('streamFilter', fakeStreamFilter);
$provide.value('threading', fakeThreading);
$provide.value('groups', fakeGroups);
return;
}));
beforeEach(angular.mock.inject(function ($controller, _$rootScope_) {
$rootScope = _$rootScope_;
$scope = $rootScope.$new();
viewer = $controller('WidgetController', {$scope: $scope});
}));
afterEach(function () {
return sandbox.restore();
});
describe('loadAnnotations', function () {
it('loads all annotations for a frame', function () {
$scope.chunkSize = 20;
fakeCrossFrame.frames.push({uri: 'http://example.com'});
$scope.$digest();
var loadSpy = fakeAnnotationMapper.loadAnnotations;
assert.callCount(loadSpy, 5);
assert.calledWith(loadSpy, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]);
assert.calledWith(loadSpy, [20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39]);
assert.calledWith(loadSpy, [40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59]);
assert.calledWith(loadSpy, [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79]);
assert.calledWith(loadSpy, [80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]);
});
it('passes _separate_replies: true to the search API', function () {
fakeStore.SearchResource.get = sandbox.stub();
fakeCrossFrame.frames.push({uri: 'http://example.com'});
$scope.$digest();
assert.equal(
fakeStore.SearchResource.get.firstCall.args[0]._separate_replies, true);
});
return it('passes annotations and replies from search to loadAnnotations()', function () {
fakeStore.SearchResource.get = function (query, callback) {
return callback({
rows: ['annotation_1', 'annotation_2'],
replies: ['reply_1', 'reply_2', 'reply_3']
});
};
fakeCrossFrame.frames.push({uri: 'http://example.com'});
$scope.$digest();
assert(fakeAnnotationMapper.loadAnnotations.calledOnce);
assert(fakeAnnotationMapper.loadAnnotations.calledWith(
['annotation_1', 'annotation_2'], ['reply_1', 'reply_2', 'reply_3']
));
});
});
describe('when the focused group changes', function () {
return it('should load annotations for the new group', function () {
fakeThreading.annotationList = sandbox.stub().returns([{id: '1'}]);
fakeCrossFrame.frames.push({uri: 'http://example.com'});
var searchResult = {total: 10, rows: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], replies: []};
fakeStore.SearchResource.get = function (query, callback) {
return callback(searchResult);
};
$scope.$broadcast(events.GROUP_FOCUSED);
assert.calledWith(fakeAnnotationMapper.unloadAnnotations, [{id: '1'}]);
$scope.$digest();
assert.calledWith(fakeAnnotationMapper.loadAnnotations, searchResult.rows);
assert.calledWith(fakeThreading.thread, fakeDrafts.unsaved());
});
});
describe('when a new annotation is created', function () {
/**
* It should clear any selection that exists in the sidebar before
* creating a new annotation. Otherwise the new annotation with its
* form open for the user to type in won't be visible because it's
* not part of the selection.
*/
it('clears the selection', function () {
$scope.clearSelection = sinon.stub();
$rootScope.$emit('beforeAnnotationCreated', {});
assert.called($scope.clearSelection);
});
it('does not clear the selection if the new annotation is a highlight', function () {
$scope.clearSelection = sinon.stub();
$rootScope.$emit('beforeAnnotationCreated', {$highlight: true});
assert.notCalled($scope.clearSelection);
});
it('does not clear the selection if the new annotation is a reply', function () {
$scope.clearSelection = sinon.stub();
$rootScope.$emit('beforeAnnotationCreated', {
references: ['parent-id']
});
assert.notCalled($scope.clearSelection);
});
});
});
angular = require('angular')
events = require('./events')
module.exports = class WidgetController
this.$inject = [
'$scope', '$rootScope', 'annotationUI', 'crossframe', 'annotationMapper',
'drafts', 'groups', 'streamer', 'streamFilter', 'store', 'threading'
]
constructor: (
$scope, $rootScope, annotationUI, crossframe, annotationMapper,
drafts, groups, streamer, streamFilter, store, threading
) ->
$scope.threadRoot = threading.root
$scope.sortOptions = ['Newest', 'Oldest', 'Location']
@chunkSize = 200
loaded = []
_resetAnnotations = ->
# Unload all the annotations
annotationMapper.unloadAnnotations(threading.annotationList())
# Reload all the drafts
threading.thread(drafts.unsaved())
_loadAnnotationsFrom = (query, offset) =>
queryCore =
limit: @chunkSize
offset: offset
sort: 'created'
order: 'asc'
group: groups.focused().id
q = angular.extend(queryCore, query)
q._separate_replies = true
store.SearchResource.get q, (results) ->
total = results.total
offset += results.rows.length
if offset < total
_loadAnnotationsFrom query, offset
annotationMapper.loadAnnotations(results.rows, results.replies)
loadAnnotations = (frames) ->
for f in frames
if f.uri in loaded
continue
loaded.push(f.uri)
_loadAnnotationsFrom({uri: f.uri}, 0)
if loaded.length > 0
streamFilter.resetFilter().addClause('/uri', 'one_of', loaded)
streamer.setConfig('filter', {filter: streamFilter.getFilter()})
$scope.$on events.GROUP_FOCUSED, ->
_resetAnnotations(annotationMapper, drafts, threading)
loaded = []
loadAnnotations crossframe.frames
$scope.$watchCollection (-> crossframe.frames), loadAnnotations
$scope.focus = (annotation) ->
if angular.isObject annotation
highlights = [annotation.$$tag]
else
highlights = []
crossframe.call('focusAnnotations', highlights)
$scope.scrollTo = (annotation) ->
if angular.isObject annotation
crossframe.call('scrollToAnnotation', annotation.$$tag)
$scope.hasFocus = (annotation) ->
!!($scope.focusedAnnotations ? {})[annotation?.$$tag]
$rootScope.$on('beforeAnnotationCreated', (event, data) ->
if data.$highlight || (data.references && data.references.length > 0)
return
$scope.clearSelection()
)
'use strict';
var angular = require('angular');
var events = require('./events');
// @ngInject
module.exports = function WidgetController(
$scope, $rootScope, annotationUI, crossframe, annotationMapper,
drafts, groups, streamer, streamFilter, store, threading
) {
$scope.threadRoot = threading.root;
$scope.sortOptions = ['Newest', 'Oldest', 'Location'];
var DEFAULT_CHUNK_SIZE = 200;
var loaded = [];
var _resetAnnotations = function () {
// Unload all the annotations
annotationMapper.unloadAnnotations(threading.annotationList());
// Reload all the drafts
threading.thread(drafts.unsaved());
};
var _loadAnnotationsFrom = function (query, offset) {
var queryCore = {
limit: $scope.chunkSize || DEFAULT_CHUNK_SIZE,
offset: offset,
sort: 'created',
order: 'asc',
group: groups.focused().id
};
var q = angular.extend(queryCore, query);
q._separate_replies = true;
store.SearchResource.get(q, function (results) {
var total = results.total;
offset += results.rows.length;
if (offset < total) {
_loadAnnotationsFrom(query, offset);
}
annotationMapper.loadAnnotations(results.rows, results.replies);
});
};
var loadAnnotations = function (frames) {
for (var i = 0, f; i < frames.length; i++) {
f = frames[i];
var ref;
if (ref = f.uri, loaded.indexOf(ref) >= 0) {
continue;
}
loaded.push(f.uri);
_loadAnnotationsFrom({uri: f.uri}, 0);
}
if (loaded.length > 0) {
streamFilter.resetFilter().addClause('/uri', 'one_of', loaded);
streamer.setConfig('filter', {filter: streamFilter.getFilter()});
}
};
$scope.$on(events.GROUP_FOCUSED, function () {
_resetAnnotations(annotationMapper, drafts, threading);
loaded = [];
return loadAnnotations(crossframe.frames);
});
$scope.$watchCollection(function () {
return crossframe.frames;
}, loadAnnotations);
$scope.focus = function (annotation) {
var highlights = [];
if (angular.isObject(annotation)) {
highlights = [annotation.$$tag];
}
return crossframe.call('focusAnnotations', highlights);
};
$scope.scrollTo = function (annotation) {
if (angular.isObject(annotation)) {
return crossframe.call('scrollToAnnotation', annotation.$$tag);
}
};
$scope.hasFocus = function (annotation) {
if (!annotation || !$scope.focusedAnnotations) {
return false;
}
return annotation.$$tag in $scope.focusedAnnotations;
};
$rootScope.$on('beforeAnnotationCreated', function (event, data) {
if (data.$highlight || (data.references && data.references.length > 0)) {
return;
}
return $scope.clearSelection();
});
};
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