diff --git a/src/core/js/ext_applicable.js b/src/core/js/ext_applicable.js index 20f3a593c5b8d7329017b8a46b4c964b28907648..8ff06bc0f162062ca512ba0f144e559a187ccaf6 100644 --- a/src/core/js/ext_applicable.js +++ b/src/core/js/ext_applicable.js @@ -60,18 +60,26 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity const version = "0.1"; /** + * Run through all creators and call the "create" function of the first + * creator which returns true from is_applicable(entity). + * * @param {HTMLElement} entity - * @param {Creator[]} creators - + * @param {Creator[]} creators - list of creators + * @return {HTMLElemen|String} content - the result of the first matching + * creator. */ const root_creator = async function (entity, creators) { for (let c of creators) { + var is_applicable = false; try { - if (await c.is_applicable(entity)) { - const content = await c.create(entity); - return content; - } + is_applicable = await c.is_applicable(entity); } catch (err) { - logger.error(err); + logger.error(`error in is_applicable function of creator`, c, err); + continue; + } + if (is_applicable) { + const content = await c.create(entity); + return content; } } return undefined; @@ -80,23 +88,16 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity var _set_content = function (container, content) { const _container = $(container); _container.empty(); - // TODO show/hide buttons for expanding the content - var buttons = _container.siblings(`.${_css_class_preview_container_button}`); - // toggle the buttons which expand the container if (content) { - buttons.css({ - "visibility": "initial" - }); _container.append(content); - } else { - buttons.css({ - "visibility": "hidden" - }); } } - const set_content = async function (container, content, contentReadyEvent) { + /** + * TODO + */ + const set_content = async function (container, content, contentReadyEvent, noContentEvent) { try { const wait = "Please wait..."; _set_content(container, wait); @@ -104,7 +105,10 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity _set_content(container, result); if (result && contentReadyEvent) { container.dispatchEvent(contentReadyEvent); + } else if (!result && noContentEvent) { + container.dispatchEvent(noContentEvent); } + } catch (err) { logger.error(err); const err_msg = "An error occured while loading this content."; @@ -117,12 +121,14 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity * @param {HTMLElement} entity * @param {get_container_cb} get_container_cb * @param {Creator[]} creators + * @param {Event} [contentReadyEvent] + * @param {Event} [noContentEvent] */ - const _root_handler = function (entity, get_container_cb, creators, contentReadyEvent) { + const _root_handler = function (entity, get_container_cb, creators, contentReadyEvent, noContentEvent) { const _container = get_container_cb(entity); if (_container) { const content = root_creator(entity, creators); - set_content(container, content, contentReadyEvent); + set_content(_container, content, contentReadyEvent, noContentEvent); } } @@ -133,17 +139,17 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity * @param {CreatorConfig[]} - config for the creators. * @returns {Creator[]} - array of creators. */ - var make_creators = function (creators) { - const result = []; - for (let c of creators) { - result.push({ - id: c.id, - is_applicable: typeof c.is_applicable === "function" ? c.is_applicable : eval(c.is_applicable), - create: typeof c.create === "function" ? c.create : eval(c.create) - }); - } - return result; - } + //var make_creators = function (creators) { + //const result = []; + //for (let c of creators) { + //result.push({ + //id: c.id, + //is_applicable: typeof c.is_applicable === "function" ? c.is_applicable : eval(c.is_applicable), + //create: typeof c.create === "function" ? c.create : eval(c.create) + //}); + //} + //return result; + //} /** @@ -163,6 +169,7 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity } } + /** * Initialize the scroll watcher which listens on the scroll event of the * window and triggers the root handler with a delay after the last @@ -175,7 +182,7 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity var init_watcher = function(delay, trigger) { var scroll_timeout = undefined; $(window).scroll(() => { - if (!scroll_timeout) { + if (scroll_timeout) { clearTimeout(scroll_timeout); } scroll_timeout = setTimeout(trigger, delay); @@ -187,11 +194,10 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity window.addEventListener( preview.previewReadyEvent.type, () => { - if (!preview_timeout) { - clearTimeout(scroll_timeout); + if (preview_timeout) { + clearTimeout(preview_timeout); } - scroll_timeout = setTimeout(trigger, 100); - return true; + preview_timeout = setTimeout(trigger, delay); }, true); @@ -234,6 +240,15 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity }; } + const _make_creator = function (c) { + return { + id: c.id, + is_applicable: typeof c.is_applicable === "function" ? + c.is_applicable : eval(c.is_applicable), + create: typeof c.create === "function" ? c.create : eval(c.create) + }; + } + /** * @param {IsApplicableConfig} @@ -241,12 +256,12 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity */ const _make_creators = function(config) { const creators = []; - // for ... in ... - // TODO - // creators.push(creator) - const fallback_creator = _make_fallback_creator(config["fallback"]); + for (let c of config.creators) { + creators.push(_make_creator(c)); + } + const fallback_creator = _make_fallback_creator(config.fallback); if (fallback_creator) { - creator.push(fallback_creator); + creators.push(fallback_creator); } return creators; } @@ -289,33 +304,42 @@ var ext_applicable = function($, logger, is_in_view_port, load_config, getEntity * @param {string} that_version - a version string. * @throws {Error} if that_version doesn't match this modules version. */ - const check_version = function(that_version) { - if(that_version !== _version) { - throw new Error("Wrong version in config."); + const _check_version = function(that_version) { + if(that_version != version) { + throw new Error(`Wrong version in config. Was '${that_version}', should be '${version}'.`); } } /** - * @param {get_container_cb|string} get_container + * @param {string} app_name * @param {IsApplicableConfig} config + * @param {get_container_cb} get_container + * @param {Event} [contentReadyEvent] + * @param {Event} [noContentEvent] * returns {IsApplicableApp} */ - const create_is_applicable_app = function(app_name, config, get_container, contentReadyEvent) { + const create_is_applicable_app = function(app_name, config, get_container, contentReadyEvent, noContentEvent) { logger.debug("create_is_applicable_app", config, get_container); - check_version(config["version"]) - creators = _make_creators(config) - get_container_wrapper = _make_get_container_wrapper(get_container, app_name); + _check_version(config["version"]) + const creators = _make_creators(config) + const get_container_wrapper = _make_get_container_wrapper(get_container, app_name); + + const root_handler = (entity) => _root_handler(entity, get_container_wrapper, creators, contentReadyEvent, noContentEvent); - root_handler = (entity) => _root_handler(entity, get_container_wrapper, creators, contentReadyEvent); + if (config.init_watcher) { + init_watcher(config.delay || 500, () => {root_handler_trigger(root_handler);}); + } + return { + config: config, + creators: creators, + root_handler: root_handler, + } } return { - init_watcher: init_watcher, - root_creator: root_creator, - root_handler: _root_handler, - root_handler_trigger: root_handler_trigger, - make_creators: make_creators, create_is_applicable_app: create_is_applicable_app, + root_handler_trigger: root_handler_trigger, + init_watcher: init_watcher, helpers: helpers, }; diff --git a/src/core/js/ext_bottom_line.js b/src/core/js/ext_bottom_line.js index d598fcbd9f52e5be8eccd69703224fbe0d58cf28..8c657698cfb9d076b1252f99cab8d57e01239f50 100644 --- a/src/core/js/ext_bottom_line.js +++ b/src/core/js/ext_bottom_line.js @@ -45,7 +45,9 @@ */ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntityPath, connection, ext_applicable) { + const contentShownEvent = new Event("ext_bottom_line.content.shown"); const contentReadyEvent = new Event("ext_bottom_line.content.ready"); + const noContentEvent = new Event("ext_bottom_line.content.none"); const _css_class_preview_container = "caosdb-f-ext_bottom_line-container"; const _css_class_preview_container_button = "caosdb-f-ext_bottom_line-container-button"; @@ -118,25 +120,11 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit is_applicable: (entity) => ext_applicable.helpers.path_has_file_extension( entity, ["mp4", "mov", "webm"]), create: _create_video_preview, - }, { // fallback - id: "_default_creators.fallback", - is_applicable: (entity) => true, - create: (entity) => fallback_preview, }, ]; - const contentShownEvent = new Event("ext_bottom_line.content.shown"); - - - /** - * Store the list of creators. - * - * @member {Creator[]} - */ - const _creators = []; - /** * Return the preview container of the entity. * @@ -145,7 +133,7 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit * doesn't have any. */ const get_preview_container = function(entity) { - return $(entity).children(`.${ext_applicable. + return $(entity).children(`.${ext_bottom_line. _css_class_preview_container}`)[0]; } @@ -200,10 +188,16 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit container.on("shown.bs.collapse", () => { container[0].dispatchEvent(contentShownEvent); }); + container.on(noContentEvent.type, () => { + button_hide.css({ + "visibility": "hidden" + }); + button_show.css({ + "visibility": "hidden" + }); + }); + - container.on(ext_applicable.contentReadyEvent.type, () => { - container[0].dispatchEvent(contentReadyEvent); - } $(entity).append(container); $(entity).append(button_show); $(entity).append(button_hide); @@ -211,6 +205,11 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit return container[0]; } + var get_container = function (entity) { + const container = get_preview_container(entity) || add_preview_container(entity); + return container; + } + /** * Create a preview for the entity and append it to the entity. * @@ -221,85 +220,59 @@ var ext_bottom_line = function($, logger, is_in_view_port, load_config, getEntit * @param {HTMLElement} entity - the entity for which the preview is to * created. */ - var root_preview_handler = async function(entity) { - const container = get_preview_container(entity) || add_preview_container(entity); - if (container) { - await ext_applicable.root_handler(entity, container, _creators); - } else { - logger.error(new Error("Could not create the preview container.")); - } - } - - /** - * Trigger the root_preview_handler for all entities within the view port - * when the view port. - */ - var root_preview_handler_trigger = function() { - ext_applicable.root_handler_trigger(root_preview_handler); - } - - + //var root_preview_handler = async function(entity) { + //const container = get_preview_container(entity) || add_preview_container(entity); + //if (container) { + //await ext_applicable.root_handler(entity, container, _creators); + //} else { + //logger.error(new Error("Could not create the preview container.")); + //} + //} /** - * Configure the creators. + * Initialize this module as an is_applicable_app. * - * @param {BottomLineConfig} config - */ - var configure = async function(config) { - logger.debug("configure", config); - if (config.version != "0.1") { - throw new Error("Wrong version in config."); - } - - // append/load custom creators - _creators.splice(0, _creators.length); - for (let c of ext_applicable.make_creators(config.creators)) { - _creators.push(c); - } - - // append default creators - for (let c of _default_creators) { - _creators.push(c); - } - fallback_preview = config.fallback || fallback_preview; - }; - - - /** - * Initialize this module. - * - * I.e. configure the list of creators and setup the scroll listener which - * triggers the root_preview_handler. - * - * @property {BottomLineConfig} [config] - an optional config. Per default, the - * configuration is fetched from the server. + * @property {IsApplicableConfig} [config] - an optional config. + * Per default, the configuration is fetched from the server. */ const init = async function(config) { logger.info("ext_bottom_line initialized"); - try { let _config = config || await load_config("json/ext_bottom_line.json"); - await configure(_config); - - ext_applicable.init_watcher(_config.delay || 500, - root_preview_handler_trigger); - + // apply defaults if options are not set + _config.fallback = _config.fallback || fallback_preview; + _config.init_watcher = _config.init_watcher || true; + _config.delay = _config.delay || 500; + _config.creators = _config.creators || []; + if (typeof _config.use_default_creators == "undefined") { + _config.use_default_creators = true; + } + + // append default creators + if (_config.use_default_creators == true) { + _config.creators = _config.creators.concat(_default_creators); + } + + // create is_applicable_app + const app = ext_applicable.create_is_applicable_app("bottom_line", + _config, get_container, contentReadyEvent, noContentEvent); + + ext_bottom_line.app = app; + logger.trace("created bottom line app", app); } catch (err) { logger.error(err); } - } return { contentReadyEvent: contentReadyEvent, contentShownEvent: contentShownEvent, + noContentEvent: noContentEvent, init: init, - init_watcher: ext_applicable.init_watcher, - configure: configure, - add_preview_container: add_preview_container, - root_preview_handler: root_preview_handler, - _creators: _creators, + app: undefined, + get_container: get_container, + _css_class_preview_container: _css_class_preview_container, } }($, log.getLogger("ext_bottom_line"), resolve_references.is_in_viewport_vertically, load_config, getEntityPath, connection, ext_applicable); diff --git a/test/core/js/modules/ext_bottom_line.js.js b/test/core/js/modules/ext_bottom_line.js.js index b7e825f7b469c12a58b73ecdb1ecd126390f9c98..067aade473ba9918f9c1d44eee2b9bb60e8cc863 100644 --- a/test/core/js/modules/ext_bottom_line.js.js +++ b/test/core/js/modules/ext_bottom_line.js.js @@ -32,19 +32,19 @@ var ext_bottom_line_test_suite = function ($, ext_bottom_line, QUnit) { "fallback": "blablabla", "creators": [ { "id": "test.success", - "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) === 'TestPreviewRecord-success'", + "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) == 'TestPreviewRecord-success'", "create": "(entity) => 'SUCCESS'" }, { "id": "test.error", - "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) === 'TestPreviewRecord-error'", + "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) == 'TestPreviewRecord-error'", "create": "(entity) => new Promise((res,rej) => {rej('Test Error');})" }, { "id": "test.load-forever", - "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) === 'TestPreviewRecord-load-forever'", + "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) == 'TestPreviewRecord-load-forever'", "create": "(entity) => new Promise((res,rej) => {})" }, { "id": "test.success-2", - "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) !== 'TestPreviewRecord-fall-back'", + "is_applicable": "(entity) => getParents(entity).map(par => par.name).includes('TestPreviewRecordType') && getEntityName(entity) != 'TestPreviewRecord-fall-back'", "create": "(entity) => { return plotly_preview.create_plot([{x: [1,2,3,4,5], y: [1,2,4,8,16]}], { 'xaxis': {'title': 'time [samples]'}}); }" } ] @@ -53,7 +53,7 @@ var ext_bottom_line_test_suite = function ($, ext_bottom_line, QUnit) { QUnit.module("ext_bottom_line.js", { before: async function (assert) { // setup before module - await ext_bottom_line.configure(test_config); + await ext_bottom_line.init(test_config); }, beforeEach: function (assert) { // setup before each test @@ -66,52 +66,51 @@ var ext_bottom_line_test_suite = function ($, ext_bottom_line, QUnit) { } }); - QUnit.test("_creators", function (assert) { - assert.equal(ext_bottom_line._creators.length, 7, "seven creators"); + QUnit.test("app.creators", function (assert) { + assert.equal(ext_bottom_line.app.creators.length, 7, "seven creators"); }); - QUnit.test("add_preview_container", function(assert) { + QUnit.test("get_container - creation", function(assert) { var entity = $("<div/>"); - var container = $(ext_bottom_line.add_preview_container(entity[0])); - assert.ok(container.hasClass(ext_applicable._css_class_preview_container), `has class ${ext_applicable._css_class_preview_container}`); - assert.ok(container.hasClass(ext_applicable._css_class_preview_container_resolvable), `has class ${ext_applicable._css_class_preview_container_resolvable}`); + var container = $(ext_bottom_line.get_container(entity[0])); + assert.ok(container.hasClass(ext_bottom_line._css_class_preview_container), `new container has class ${ext_bottom_line._css_class_preview_container}`); }); - QUnit.test("root_preview_handler", async function(assert) { - for (let name_suffix of ["fall-back", "error", "load-forever", "success"]) { + QUnit.test("get_container - pre-existing", function(assert) { + var entity = $("<div/>"); + var _pre_existing = $(`<div id="preexisting" class="${ext_bottom_line._css_class_preview_container}"/>`)[0]; + entity.append(_pre_existing); + var container = ext_bottom_line.get_container(entity[0]); + + assert.equal(container.id, _pre_existing.id, "correct container found"); + }); + + QUnit.test("run with test config", async function(assert) { + for (let name_suffix of ["error", "fall-back", "load-forever", "success"]) { let name = "TestPreviewRecord-" + name_suffix; let entity_xml = `<Response><Record name="${name}"><Parent name='TestPreviewRecordType'/></Record></Response>`; let entity = (await transformation.transformEntities(str2xml(entity_xml)))[0]; assert.equal(getEntityName(entity), name, "correct name"); - var container = $(ext_bottom_line.add_preview_container(entity)); - - assert.ok(container.hasClass(ext_applicable._css_class_preview_container), `before has class ${ext_applicable._css_class_preview_container}`); - assert.ok(container.hasClass(ext_applicable._css_class_preview_container_resolvable), `before has class ${ext_applicable._css_class_preview_container_resolvable}`); + var container = $(ext_bottom_line.get_container(entity)); - if (name_suffix === "load-forever"){ - ext_bottom_line.root_preview_handler(entity); - await sleep(1000); - } else { - await ext_bottom_line.root_preview_handler(entity); - } + assert.ok(container.hasClass(ext_bottom_line._css_class_preview_container), `before has class ${ext_bottom_line._css_class_preview_container}`); - // ..._resolvable class removed - assert.ok(container.hasClass(ext_applicable._css_class_preview_container), `after has class ${ext_applicable._css_class_preview_container}`); - assert.notOk(container.hasClass(ext_applicable._css_class_preview_container_resolvable), `after missing class ${ext_applicable._css_class_preview_container_resolvable}`); + ext_bottom_line.app.root_handler(entity); + await sleep(1000); switch(name_suffix) { - case "fall-back": - assert.equal(container.text(), "blablabla"); - break; case "error": - assert.equal(container.text(), "An error occured while loading this content."); + assert.equal(container.text(), "An error occured while loading this content.", `${name_suffix} has correct content`); break; case "load-forever": - assert.equal(container.text(), "Please wait..."); + assert.equal(container.text(), "Please wait...", `${name_suffix} has correct content`); break; case "success": - assert.equal(container.text(), "SUCCESS"); + assert.equal(container.text(), "SUCCESS", `${name_suffix} has correct content`); + break; + case "fall-back": + assert.equal(container.text(), "blablabla", `${name_suffix} has correct content`); break; default: assert.ok(false);