diff --git a/css/mirador.css b/css/mirador.css index 04927ca73e..17354d293d 100644 --- a/css/mirador.css +++ b/css/mirador.css @@ -1983,6 +1983,9 @@ a.mirador-icon-window-menu, a.mirador-icon-view-type { max-height: 200px; overflow: auto; } +.qtip-content { + text-align: inherit; +} .annotation-tooltip { padding: 10px; } diff --git a/js/src/annotations/tinymce-annotation-editor.js b/js/src/annotations/tinymce-annotation-editor.js index 1abad51548..8560f5f38b 100644 --- a/js/src/annotations/tinymce-annotation-editor.js +++ b/js/src/annotations/tinymce-annotation-editor.js @@ -4,7 +4,11 @@ jQuery.extend(this, { annotation: null, - windowId: null + windowId: null, + config: { + plugins: '', + toolbar: '' + } }, options); this.init(); @@ -42,11 +46,11 @@ .prepend(this.editorMarkup); tinymce.init({ selector: selector + ' textarea', - plugins: "image link media", + plugins: this.config.plugins, menubar: false, statusbar: false, toolbar_items_size: 'small', - toolbar: "bold italic | bullist numlist | link image media | removeformat", + toolbar: this.config.toolbar, default_link_target:"_blank", setup: function(editor) { editor.on('init', function(args) { diff --git a/js/src/settings.js b/js/src/settings.js index e6dcbc95c3..2aa4a201ef 100644 --- a/js/src/settings.js +++ b/js/src/settings.js @@ -187,7 +187,12 @@ 'annotationBodyEditor': { 'module': 'TinyMCEAnnotationBodyEditor', - 'options': {} + 'options': { + config: { + plugins: "image link media directionality", + toolbar: "bold italic | bullist numlist | link image media | removeformat | ltr rtl" + } + } }, 'jsonStorageEndpoint': { diff --git a/js/src/utils/eventemitter.js b/js/src/utils/eventemitter.js index 72cc80afad..1f16f82720 100644 --- a/js/src/utils/eventemitter.js +++ b/js/src/utils/eventemitter.js @@ -1,38 +1,109 @@ (function($) { $.EventEmitter = function(config) { jQuery.extend(true, this, { - 'debug': $.EventEmitter.debug + 'debug': $.EventEmitter.debug, // Log events to console if true + 'trace': $.EventEmitter.trace, // Use console.trace for logging if true, console.log if false + 'debugExclude': $.EventEmitter.excludePatterns // substring patterns for event IDs to exclude }, config); this.emitterId = $.EventEmitter.nextId(); + if (this.debug) { + this.logger = new $.EventEmitter.Logger({ + trace: this.trace, + debugExclude: this.debugExclude + }); + } }; - - $.EventEmitter.debug = false; + $.EventEmitter.id = 0; $.EventEmitter.nextId = function() { $.EventEmitter.id++; return $.EventEmitter.id; }; - + + /************************ + * BEGIN debug settings * + ************************/ + $.EventEmitter.debug = false; + $.EventEmitter.trace = false; + + // Event IDs that contains any substring in the array will be ignored by the logger + // e.g. ['updateTooltips', 'ANNO.*UPDATED'] + $.EventEmitter.excludePatterns = []; + + $.EventEmitter.Logger = function(options) { + this.trace = options.trace; + this.debugExclude = options.debugExclude; + this.scaffoldMap = {}; + }; + $.EventEmitter.Logger.prototype = { + log: function() { + if (this.trace) { + console.trace.apply(console, arguments); + } else { + console.log.apply(console, arguments); + } + }, + exclude: function(str) { + var patterns = this.debugExclude; + for (var i = 0; i < patterns.length; ++i) { + if (str.match(patterns[i])) { + return true; + } + } + return false; + }, + scaffoldHandler: function(eventId, handler) { + var _this = this; + var scaffold = function() { + _this.log("EventEmitter:handler:", eventId, handler, Array.prototype.slice.call(arguments)); + handler.apply(null, arguments); + }; + this.scaffoldMap[handler] = scaffold; + return scaffold; + }, + unscaffold: function(handler) { + var scaffold = this.scaffoldMap[handler]; + delete this.scaffoldMap[handler]; + return scaffold; + } + }; + /********************** + * END debug settings * + **********************/ + ['subscribe', 'unsubscribe', 'publish'].forEach(function(action) { $.EventEmitter.prototype[action] = function() { var args = Array.prototype.slice.call(arguments); + var eventId = args[0]; + var globalEventId = this.emitterId.toString() + '::' + eventId; + var logging = this.logger && !this.logger.exclude(globalEventId); var event; - if(action === 'subscribe'){ + + if (action === 'subscribe') { event = { - name:args[0], - handler:args[1] + name: eventId, + handler: args[1] }; + if (logging) { + args[1] = this.logger.scaffoldHandler(globalEventId, args[1]); + } + } + + if (action === 'unsubscribe') { + if (logging) { + args[1] = this.logger.unscaffold(args[1]); + } } - args[0] = this.emitterId.toString() +"::"+ args[0]; - if (this.debug) { - console.trace("EventEmitter:"+action+":", args); + args[0] = globalEventId; + if (logging) { + this.logger.log("EventEmitter:" + action + ":", args); } jQuery[action].apply(jQuery, args); - if(event){ + if (event) { return event; } }; }); -})(Mirador); \ No newline at end of file +})(Mirador); diff --git a/js/src/utils/utils.js b/js/src/utils/utils.js index c1d636f398..4991f8390a 100644 --- a/js/src/utils/utils.js +++ b/js/src/utils/utils.js @@ -274,7 +274,8 @@ allowedTags: ['a', 'b', 'br', 'i', 'img', 'p', 'span', 'strong', 'em'], allowedAttributes: { 'a': ['href', 'target'], - 'img': ['src', 'alt'] + 'img': ['src', 'alt'], + 'p': ['dir'] } }); }; diff --git a/js/src/viewer.js b/js/src/viewer.js index f68068e167..b91c4c0356 100644 --- a/js/src/viewer.js +++ b/js/src/viewer.js @@ -48,8 +48,9 @@ fallbackLng: 'en', load: 'unspecific', debug: false, - getAsync: true, - resGetPath: _this.state.getStateProperty('buildPath') + _this.state.getStateProperty('i18nPath')+'__lng__/__ns__.json' + backend: { + loadPath: _this.state.getStateProperty('buildPath') + _this.state.getStateProperty('i18nPath')+'{{lng}}/{{ns}}.json' + } }, _this.setupViewer.bind(_this)); // because this is a callback, we need to bind "_this" to explicitly retain the calling context of this function (the viewer object instance)); }, diff --git a/locales/de/translation.json b/locales/de/translation.json index 39b6c21dcb..5aa0049767 100644 --- a/locales/de/translation.json +++ b/locales/de/translation.json @@ -74,7 +74,7 @@ "selectGrid": "Anordnung auswählen", "sidePanelTooltip": "Seitenleiste", "solid": "Durchgängig", - "thumbnailsView": "Gallerie", + "thumbnailsView": "Galerie", "unspecified": "nicht spezifiziert", "url": "URL", "viewTypeTooltip": "Ansichtstyp wechseln", diff --git a/package.json b/package.json index 23b7c87018..81d1bec2d7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mirador", - "version": "2.1.4", + "version": "2.2.0", "description": "Multi-window image viewer, a web-based tool to support researcher goals", "repository": { "type": "git", diff --git a/spec/annotations/tinymce-annotation-editor.test.js b/spec/annotations/tinymce-annotation-editor.test.js index a183987bee..b942c6d52e 100644 --- a/spec/annotations/tinymce-annotation-editor.test.js +++ b/spec/annotations/tinymce-annotation-editor.test.js @@ -1,17 +1,34 @@ describe('TinyMCEAnnotationBodyEditor', function() { + var subject; beforeEach(function() { - + Handlebars.registerHelper('t', function(i18n_key) { + var result = i18next.t(i18n_key); + return new Handlebars.SafeString(result); + }); + subject = new Mirador.TinyMCEAnnotationBodyEditor(); }); afterEach(function() { }); - xdescribe('Initialization', function() { - it('should initialize', function() { - + describe('Initialization', function() { + it('should initialize with provided default configs', function() { + console.log(subject) + expect(subject.config.plugins).toBe(''); + expect(subject.config.toolbar).toBe('') }); + it('should initialize with new configs for TinyMCE', function() { + var thisSubject = new Mirador.TinyMCEAnnotationBodyEditor({ + config: { + plugins: 'all of them', + toolbar: 'some of them' + } + }); + expect(thisSubject.config.plugins).toBe('all of them'); + expect(thisSubject.config.toolbar).toBe('some of them') + }) }); xdescribe('show', function() { diff --git a/spec/locales/de/translation.json b/spec/locales/de/translation.json index b903af9e18..c3ef4af1bd 100644 --- a/spec/locales/de/translation.json +++ b/spec/locales/de/translation.json @@ -70,7 +70,7 @@ "selectGrid": "Anordnung auswählen", "sidePanelTooltip": "Seitenleiste", "solid": "Durchgängig", - "thumbnailsView": "Gallerie", + "thumbnailsView": "Galerie", "unspecified": "nicht spezifiziert", "url": "URL", "viewTypeTooltip": "Ansichtstyp wechseln", diff --git a/spec/utils/eventemitter.test.js b/spec/utils/eventemitter.test.js index 7de3fe8683..230e5757ee 100644 --- a/spec/utils/eventemitter.test.js +++ b/spec/utils/eventemitter.test.js @@ -1,14 +1,14 @@ describe('EventEmitter', function () { beforeEach(function() { - Mirador.EventEmitter.id = 0; + Mirador.EventEmitter.id = 0; }); it('should initialize itself', function() { var em = new Mirador.EventEmitter(); expect(em).toBeTruthy(); }); - + it('should have an identity andpublish, subscribe, and unsubscribe methods', function() { var em = new Mirador.EventEmitter(); expect(typeof em.emitterId).toBe("number"); @@ -26,25 +26,25 @@ describe('EventEmitter', function () { expect(em1.emitterId < em2.emitterId).toBeTruthy(); expect(em2.emitterId < em3.emitterId).toBeTruthy(); }); - + it('should be able to publish and subscribe/unsubscribe to an event', function() { var em = new Mirador.EventEmitter(); var obj = {callback: function() {}}; var eventName = "some_mirador_test_event"; - + spyOn(obj, 'callback'); em.subscribe(eventName, obj.callback); em.publish(eventName); expect(obj.callback).toHaveBeenCalled(); - + obj.callback.calls.reset(); - + em.unsubscribe(eventName, obj.callback); em.publish(eventName); expect(obj.callback).not.toHaveBeenCalled(); }); - + it('should publish events with the unique ID', function() { var em = new Mirador.EventEmitter(); var prefix = em.emitterId.toString() + "::"; @@ -54,22 +54,64 @@ describe('EventEmitter', function () { spyOn(jQuery, 'subscribe'); spyOn(jQuery, 'unsubscribe'); spyOn(jQuery, 'publish'); - + em.publish(eventName); expect(jQuery.publish).toHaveBeenCalledWith(prefix + eventName); - + em.subscribe(eventName, obj.callback); expect(jQuery.subscribe).toHaveBeenCalledWith(prefix + eventName, obj.callback); em.unsubscribe(eventName, obj.callback); expect(jQuery.unsubscribe).toHaveBeenCalledWith(prefix + eventName, obj.callback); }); - + it('should log events when debug mode is enabled', function() { - var em = new Mirador.EventEmitter({"debug": true}); + var em = new Mirador.EventEmitter({debug: true}); var eventName = "foo"; - spyOn(console, 'trace'); + spyOn(console, 'log'); em.publish(eventName); - expect(console.trace).toHaveBeenCalled(); + expect(console.log).toHaveBeenCalled(); + }); + + it('should recognize trace option when logging events', function() { + var em = new Mirador.EventEmitter({debug: true, trace: true}); + spyOn(console, 'trace'); + em.publish('foo'); + expect(console.trace).toHaveBeenCalled(); + }); + + it('should recognize exclude patterns when logging events', function() { + var em = new Mirador.EventEmitter({debug: true, + debugExclude: ['hello'] + }); + spyOn(console, 'log'); + em.publish('hello_world'); + expect(console.log).not.toHaveBeenCalled(); + em.publish('goodbye_world'); + expect(console.log).toHaveBeenCalled(); + }); + + it('should log when subscribed handlers are called in debug mode', function() { + var em = new Mirador.EventEmitter({debug: true}); + var handler = jasmine.createSpy('spy'); + spyOn(console, 'log'); + em.subscribe('foo', handler); + expect(console.log.calls.count()).toEqual(1); // trace from subscribe + em.publish('foo'); + expect(console.log.calls.count()).toEqual(3); // trace from publish and handler + expect(handler).toHaveBeenCalled(); + }); + + it('should unsubscribe correctly when in debug mode', function() { + var em = new Mirador.EventEmitter({debug: true}); + var handler = jasmine.createSpy('spy'); + spyOn(console, 'log'); + em.subscribe('foo', handler); + em.publish('foo'); + expect(handler).toHaveBeenCalled(); + handler.calls.reset(); + em.unsubscribe('foo', handler); + em.publish('foo'); + expect(handler).not.toHaveBeenCalled(); }); }); \ No newline at end of file