From: Jason Skomorowski Date: Mon, 22 Sep 2014 14:14:39 +0000 (-0400) Subject: Merge branch 'master' into templateallthemarkup X-Git-Tag: 1.0.0~324^2~24 X-Git-Url: http://git.indexdata.com/?p=mkws-moved-to-github.git;a=commitdiff_plain;h=a28667229f37a181910a8d5fff3815c1e3949057;hp=85cdd94123b186c815a7484b746628a7eae00f74 Merge branch 'master' into templateallthemarkup --- diff --git a/.gitignore b/.gitignore index b25c15b..b72f9be 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *~ +*.swp diff --git a/README b/README index 5696cd1..7aacc48 100644 --- a/README +++ b/README @@ -30,6 +30,11 @@ or you can download the source from http://nodejs.org/download/ and build it yourself. Looks like you need node and npm, make install puts them into /usr/local/bin. +To compile the default templates you'll need to install the stable version +of Handlebars. Currently it's at 1.3.0 and available by npm: + + npm install handlebars@1.3.0 -g + For apache setup, see tools/apache2/README NEWS diff --git a/examples/htdocs/jasmine-local-popup.html b/examples/htdocs/jasmine-local-popup.html index d5758b3..386e830 100644 --- a/examples/htdocs/jasmine-local-popup.html +++ b/examples/htdocs/jasmine-local-popup.html @@ -10,7 +10,7 @@ - + @@ -24,6 +24,7 @@ + diff --git a/examples/htdocs/jasmine-popup.html b/examples/htdocs/jasmine-popup.html index 7d1b157..a6d1fff 100644 --- a/examples/htdocs/jasmine-popup.html +++ b/examples/htdocs/jasmine-popup.html @@ -10,7 +10,7 @@ - + @@ -18,6 +18,7 @@ + diff --git a/examples/htdocs/mkws-widget-credo-bs.js b/examples/htdocs/mkws-widget-credo-bs.js deleted file mode 100644 index 49651ea..0000000 --- a/examples/htdocs/mkws-widget-credo-bs.js +++ /dev/null @@ -1,109 +0,0 @@ -// The Google Images database returns links like: -// http://images.google.com/url?q=http://eofdreams.com/fish.html&sa=U&ei=RAB-U9XNDo2Dqga1o4L4Bw&ved=0CC4Q9QEwAA&usg=AFQjCNFhRtn6GMevHbpITZ6kfx6rsHV2ow -// This Handlebars helper avoids a pointless redirect by transforming -// this to the URL of the underling page, in this case -// http://eofdreams.com/fish.html -// -Handlebars.registerHelper('mkws-googleurl', function(obj) { - if (!obj) { - return "obj undefined"; - } else if (!obj[0]) { - return "obj[0] undefined, JSON=" + $.toJSON(obj); - } else { - return mkws.getParameterByName('q', obj[0]); - } -}); - - -// ### This works inefficiently by having multiple teams all run the -// same search against different sets of targets. A much better -// approach would be run a single search, with all these panels -// members of the same team, but picking out only the results relevant -// to them. That will be more work. - -mkws.registerWidgetType('Credo', function() { - var that = this; - - this.team.registerTemplate('CredoImage', '\ -
\ - \ - {{#mkws-first md-thumburl}}\ - {{../md-title}}\ - {{/mkws-first}}\ -
\ -
\ -

{{{md-title}}}

\ -
\ -'); - - var s = [] - // Main panel: encylopaedia and images on the left, topics on the right - s.push('
'); - - s.push('
'); - //s.push(section('encyclopaedia', 'Topic Page: ', - s.push(this.subwidget('Reference', { _team: 'ref', paragraphs: 1 })); - // The Images widget needs to be in our team so we can set its template - s.push('
'); - - s.push('
'); - s.push(section('topics', 'Related Topics', - this.subwidget('Facet', { _team: 'main', facet: 'subject' }))); - s.push('
'); - - s.push('
'); - - s.push('
'); - s.push(section('image col-md-12', 'Images', this.subwidget('GoogleImage', { maxrecs: 4, template: 'CredoImage', target: 'google_images_js' }))); - s.push('
'); - - - s.push('
'); - s.push(section('entries clearfix col-md-4 col-sm-6', 'News', - this.subwidget('Records', { _team: 'news', targetfilter: 'categories=news', perpage: 10 }))); - s.push(section('articles clearfix col-md-4 col-sm-6', 'Articles', - this.subwidget('Records', { _team: 'articles', targetfilter: 'categories=articles', perpage: 10 }))); - s.push(section('books clearfix col-md-4 col-sm-6', 'Books', - this.subwidget('Records', { _team: 'books', targetfilter: 'categories=books', perpage: 10 }))); - s.push(section('news col-md-4 col-sm-6', 'Results from all targets', - this.subwidget('Records', { _team: 'main' }))); - s.push('
'); - this.node.html(s.join('')); - - // Fill in the titles from the query once widgets have all been prepared - var that = this; - this.team.queue("ready").subscribe(function() { - var query = toTitleCase(that.config.query); - that.log("got query '" + query + "' from team config"); - mkws.$('.x-mkws-title').html(query); - mkws.$('title').html("MKWS: " + query); - - mkws.$(".mkwsSummary img").addClass("media-object"); - console.log(mkws.$("body").html()); - - // Derived from http://stackoverflow.com/questions/196972/convert-string-to-title-case-with-javascript - function toTitleCase(str) { - return str.replace(/\w\S*/g, function(txt) { - return txt.charAt(0).toUpperCase() + txt.substr(1); - }); - } - }); - - - function section(xclass, title, content) { - var s = []; - s.push('
'); - s.push('

' + title + '

'); - s.push('
' + content + '
'); - s.push('
'); - return s.join(''); - } - - function sectionRow(xclass, title, content) { - var s = []; - s.push('
'); - s.push(section(xclass, title, content)); - s.push('
'); - return s.join(''); - } -}); diff --git a/examples/htdocs/ref-bootstrap.html b/examples/htdocs/ref-bootstrap.html deleted file mode 100644 index 503a8fe..0000000 --- a/examples/htdocs/ref-bootstrap.html +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - - MKWS demo: Compound reference widget - - - - - - - - - - - - - - - diff --git a/examples/htdocs/topic-bootstrap.css b/examples/htdocs/topic-bootstrap.css new file mode 100644 index 0000000..303942a --- /dev/null +++ b/examples/htdocs/topic-bootstrap.css @@ -0,0 +1,102 @@ +body { + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + background: radial-gradient(ellipse at center, #ffffff 0%,#f8f8f8 100%); + font-weight: 300; +} +b, strong { + font-weight: 400; +} +.panel { + border: none; + background: white; + padding: 1em .75em; + border-radius: 3px; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + color: #76868A; + box-shadow: 0 0 2px rgba(0, 0, 0, 0.09); + margin-bottom: 2em; +} + +.panel-default > .panel-heading { + background-image: none; + border: none; + background-color: white; +} + +.panel-heading { + padding: 0.5em 15px 0; +} + +.panel-title { + font-size: 130%; + color: #4A5456; +} + +.panel-body { + padding: 10px 15px; +} + +.mkwsFacetTitle { + font-style: italic; + font-weight: 400; +} + .mkwsTerm { + padding: .1em 0; + } + .mkwsTerm a { + padding-right: .5em; + font-weight: 400; + } + +.mkwsSummary { + padding: .5em 0; + line-height: 1.35; +} +.mkwsSummary:last-child { + padding-bottom: 0; +} + +.container .jumbotron { + background: none; + padding: 0 2em 0 0; + box-shadow: none; + color: black; +} + .jumbotron .mkwsSummary { + line-height: 1.5; + } + .jumbotron p{ + font-size: 150%; + } + .mkwsCredit { + font-size: 125% !important; + font-style: italic; + } + +.mkwsGoogleImage .mkwsSummary { + padding: 0; + font-weight: 400; +} + .mkwsGoogleImage img { + max-width: 100%; + max-height: 200px; + padding: 1em 0 .5em; + } +.list-group-item { + padding: 10px 15px !important; +} +.list-group { + box-shadow: none; + -webkit-box-shadow: none; +} +@media screen and (min-width:700px) { + .multicol { + column-count: 2; + column-gap: 20px; + -moz-column-count: 2; + -moz-column-gap: 20px; + -webkit-column-count: 2; + -webkit-column-gap: 20px; + } +} diff --git a/examples/htdocs/topic-bootstrap.html b/examples/htdocs/topic-bootstrap.html new file mode 100644 index 0000000..d72aa70 --- /dev/null +++ b/examples/htdocs/topic-bootstrap.html @@ -0,0 +1,127 @@ + + + + + + + MKWS demo: Compound reference widget, Bootstrap edition + + + + + + + + + + + + + + + + + + + + diff --git a/examples/htdocs/two-teams.html b/examples/htdocs/two-teams.html index 006f11b..694522e 100644 --- a/examples/htdocs/two-teams.html +++ b/examples/htdocs/two-teams.html @@ -9,7 +9,7 @@ - + diff --git a/src/mkws-core.js b/src/mkws-core.js index 6b3e00d..718c350 100644 --- a/src/mkws-core.js +++ b/src/mkws-core.js @@ -20,6 +20,7 @@ window.mkws = { // initial value allows jQuery popup to use logging. teams: {}, widgetType2function: {}, + defaultTemplates: {}, locale_lang: { "de": { @@ -188,6 +189,7 @@ mkws.setMkwsConfig = function(overrides) { facets: ["xtargets", "subject", "author"], /* display facets, in this order, [] for none */ responsive_design_width: undefined, /* a page with less pixel width considered as narrow */ log_level: 1, /* log level for development: 0..2 */ + template_vars: {}, /* values that may be exposed to templates */ dummy: "dummy" }; @@ -211,110 +213,6 @@ mkws.objectInheritingFrom = function(o) { } -mkws.defaultTemplate = function(name) { - if (name === 'Record') { - return '\ -
\ - \ - \ - \ - \ - {{#if md-date}}\ - \ - \ - \ - \ - {{/if}}\ - {{#if md-author}}\ - \ - \ - \ - \ - {{/if}}\ - {{#if md-electronic-url}}\ - \ - \ - \ - \ - {{/if}}\ - {{#mkws-if-any location having="md-subject"}}\ - \ - \ - \ - \ - {{/mkws-if-any}}\ - \ - \ - \ - \ -
{{mkws-translate "Title"}}\ - {{md-title}}\ - {{#if md-title-remainder}}\ - ({{md-title-remainder}})\ - {{/if}}\ - {{#if md-title-responsibility}}\ - {{md-title-responsibility}}\ - {{/if}}\ -
{{mkws-translate "Date"}}{{md-date}}
{{mkws-translate "Author"}}{{md-author}}
{{mkws-translate "Links"}}\ - {{#each md-electronic-url}}\ - Link{{mkws-index1}}\ - {{/each}}\ -
{{mkws-translate "Subject"}}\ - {{#mkws-first location having="md-subject"}}\ - {{#if md-subject}}\ - {{#mkws-commaList md-subject}}\ - {{this}}{{/mkws-commaList}}\ - {{/if}}\ - {{/mkws-first}}\ -
{{mkws-translate "Locations"}}\ - {{#mkws-commaList location}}\ - {{mkws-attr "@name"}}{{/mkws-commaList}}\ -
\ -'; - } else if (name === "Summary") { - return '\ -\ - {{md-title}}\ -\ -{{#if md-title-remainder}}\ - {{md-title-remainder}}\ -{{/if}}\ -{{#if md-title-responsibility}}\ - {{md-title-responsibility}}\ -{{/if}}\ -{{#if md-date}}, {{md-date}}\ -{{#if location}}\ -, {{#mkws-first location}}{{mkws-attr "@name"}}{{/mkws-first}}\ -{{/if}}\ -{{#if md-medium}}\ -, {{md-medium}}\ -{{/if}}\ -{{/if}}\ -'; - } else if (name === "Image") { - return '\ - \ - {{#mkws-first md-thumburl}}\ - {{../md-title}}\ - {{/mkws-first}}\ -
\ -
\ -'; - } else if (name === 'Facet') { - return '\ -{{term}}\ -{{count}}\ -'; - } - - return null; -}; - - // The following functions are dispatchers for team methods that // are called from the UI using a team-name rather than implicit // context. diff --git a/src/mkws-handlebars.js b/src/mkws-handlebars.js index 866dc8a..bd9af8d 100644 --- a/src/mkws-handlebars.js +++ b/src/mkws-handlebars.js @@ -96,3 +96,11 @@ Handlebars.registerHelper('mkws-commaList', function(items, options) { Handlebars.registerHelper('mkws-index1', function(obj) { return obj.data.index + 1; }); + +Handlebars.registerHelper('mkws-repeat', function(count, options) { + var out = ""; + for (var i = 0; i < count; i++) { + out += options.fn(this); + } + return out; +}); diff --git a/src/mkws-team.js b/src/mkws-team.js index 011d643..92536f1 100644 --- a/src/mkws-team.js +++ b/src/mkws-team.js @@ -41,11 +41,13 @@ function team($, teamName) { that.submitted = function() { return m_submitted; }; that.sortOrder = function() { return m_sortOrder; }; that.perpage = function() { return m_perpage; }; + that.query = function() { return m_query; }; that.totalRecordCount = function() { return m_totalRecordCount; }; that.currentPage = function() { return m_currentPage; }; that.currentRecordId = function() { return m_currentRecordId; }; that.currentRecordData = function() { return m_currentRecordData; }; that.filters = function() { return m_filterSet; }; + that.gotRecords = function() { return m_gotRecords; }; // Accessor methods for individual widgets: writers that.set_sortOrder = function(val) { m_sortOrder = val }; @@ -149,6 +151,7 @@ function team($, teamName) { log("record"); // FIXME: record is async!! clearTimeout(m_paz.recordTimer); + queue("record").publish(data); var detRecordDiv = findnode(recordDetailsId(data.recid[0])); if (detRecordDiv.length) { // in case on_show was faster to redraw element @@ -303,6 +306,12 @@ function team($, teamName) { m_paz.search(m_query, m_perpage, m_sortOrder, pp2filter, undefined, params); } + // fetch record details to be retrieved from the record queue + that.fetchDetails = function(recId) { + log("fetchDetails() requesting record '" + recId + "'"); + m_paz.record(recId); + }; + // switching view between targets and records function switchView(view) { @@ -389,38 +398,34 @@ function team($, teamName) { function loadTemplate(name, fallbackString) { var template = m_template[name]; - - if (template === undefined) { - // Fall back to generic template if there is no team-specific one + if (template === undefined && Handlebars.compile) { var source; var node = $(".mkwsTemplate_" + name + " .mkwsTeam_" + that.name()); if (node && node.length < 1) { node = $(".mkwsTemplate_" + name); } - if (node) { - source = node.html(); - } - - // If the template is not defined in HTML, check the following - // in order: template registered in the team by a widget; - // fallback string provided on this invocation; global default. - if (!source) { - source = m_templateText[name]; - } - if (!source) { - source = fallbackString; + if (node) source = node.html(); + if (!source) source = m_templateText[name]; + if (source) { + template = Handlebars.compile(source); + log("compiled template '" + name + "'"); } - if (!source) { - source = mkws.defaultTemplate(name); - } - - if (!source) return null; - template = Handlebars.compile(source); - log("compiled template '" + name + "'"); + } + //if (template === undefined) template = mkws_templatesbyteam[m_teamName][name]; + if (template === undefined && Handlebars.templates) { + template = Handlebars.templates[name]; + } + if (template === undefined && mkws.defaultTemplates) { + template = mkws.defaultTemplates[name]; + } + if (template) { m_template[name] = template; + return template; } - - return template; + else { + mkws.log("No MKWS template for " + name); + return null; + } } that.loadTemplate = loadTemplate; diff --git a/src/mkws-widget-main.js b/src/mkws-widget-main.js index fcffe5b..fce4779 100644 --- a/src/mkws-widget-main.js +++ b/src/mkws-widget-main.js @@ -6,43 +6,36 @@ mkws.registerWidgetType('Targets', function() { if (!this.config.show_switch) return; var that = this; - var M = mkws.M; this.node.html('No information available yet.'); this.node.css("display", "none"); this.team.queue("targets").subscribe(function(data) { - var table ='' + - '' + - '' + - '' + - '' + - '' + - ''; - + // There is a bug in pz2.js wherein it makes each data object an array but + // simply assigns properties to it. + // TODO: remove this when PAZ-946 is addressed. + var cleandata = []; for (var i = 0; i < data.length; i++) { - table += ""; + var cur = {}; + cur.id = data[i].id; + cur.hits = data[i].hits; + cur.diagnostic = data[i].diagnostic; + cur.records = data[i].records; + cur.state = data[i].state; + cleandata.push(cur); } - table += '
' + M('Target ID') + '' + M('Hits') + '' + M('Diags') + '' + M('Records') + '' + M('State') + '
" + data[i].id + - "" + data[i].hits + - "" + data[i].diagnostic + - "" + data[i].records + - "" + data[i].state + "
'; - that.node.html(table); + var template = that.team.loadTemplate(that.config.template || "Targets"); + that.node.html(template({data: cleandata})); }); }); mkws.registerWidgetType('Stat', function() { var that = this; - var M = mkws.M; - this.team.queue("stat").subscribe(function(data) { - that.node.html(' -- ' + - '' + M('Active clients') + ': ' + data.activeclients + '/' + data.clients + '' + - ' -- ' + - M('Retrieved records') + ': ' + data.records + '/' + data.hits); + var template = that.team.loadTemplate(that.config.template || "Stat"); + that.node.html(template(data)); }); }); @@ -52,92 +45,92 @@ mkws.registerWidgetType('Pager', function() { var M = mkws.M; this.team.queue("pager").subscribe(function(data) { - that.node.html(drawPager(data)) - - function drawPager(data) { - var teamName = that.team.name(); - var s = '
' + M('Displaying') + ': ' - + (data.start + 1) + ' ' + M('to') + ' ' + (data.start + data.num) + - ' ' + M('of') + ' ' + data.merged + ' (' + M('found') + ': ' - + data.total + ')
'; - - //client indexes pages from 1 but pz2 from 0 - var onsides = 6; - var pages = Math.ceil(that.team.totalRecordCount() / that.team.perpage()); - var currentPage = that.team.currentPage(); - - var firstClkbl = (currentPage - onsides > 0) - ? currentPage - onsides - : 1; - - var lastClkbl = firstClkbl + 2*onsides < pages - ? firstClkbl + 2*onsides - : pages; - - var prev = '<< ' + M('Prev') + ' | '; - if (currentPage > 1) - prev = '' - +'<< ' + M('Prev') + ' | '; - - var middle = ''; - for(var i = firstClkbl; i <= lastClkbl; i++) { - var numLabel = i; - if(i == currentPage) - numLabel = '' + i + ''; - - middle += ' ' - + numLabel + ' '; + var teamName = that.team.name(); + var output = {}; + output.first = data.start + 1; + output.last = data.start + data.num; + output.count = data.merged; + output.found = data.total; + + //client indexes pages from 1 but pz2 from 0 + var onsides = 6; + var pages = Math.ceil(that.team.totalRecordCount() / that.team.perpage()); + var currentPage = that.team.currentPage(); + + var firstClkbl = (currentPage - onsides > 0) + ? currentPage - onsides + : 1; + var lastClkbl = firstClkbl + 2*onsides < pages + ? firstClkbl + 2*onsides + : pages; + + if (firstClkbl > 1) output.morePrev = true; + if (lastClkbl < pages) output.moreNext = true; + + if (currentPage > 1) output.prevClick = "mkws.pagerPrev(\'" + teamName + "\');"; + + output.pages = []; + for(var i = firstClkbl; i <= lastClkbl; i++) { + var o = {}; + o.number = i; + if (i !== currentPage) { + o.click = "mkws.showPage(\'" + teamName + "\', " + i + ");"; } + output.pages.push(o); + } - var next = ' | ' + M('Next') + ' >>'; - if (pages - currentPage > 0) - next = ' | ' - + M('Next') + ' >>'; - - var predots = ''; - if (firstClkbl > 1) - predots = '...'; - - var postdots = ''; - if (lastClkbl < pages) - postdots = '...'; + if (pages - currentPage > 0) output.nextClick = "mkws.pagerNext(\'" + teamName + "\')"; - s += '
' - + prev + predots + middle + postdots + next + '
'; + var template = that.team.loadTemplate(that.config.template || "Pager"); + that.node.html(template(output)); + }); +}); - return s; +mkws.registerWidgetType('Details', function() { + var that = this; + var recid = that.node.attr("data-mkws-recid"); + if (this.team.gotRecords()) { + that.team.fetchDetails(recid); + } else { + this.team.queue("firstrecords").subscribe(function() { + that.team.fetchDetails(recid); + }); + } + this.team.queue("record").subscribe(function(data) { + console.log(data); + if ($.inArray(recid, data.recid) > -1) { + var template = that.team.loadTemplate(that.config.template || "Record"); + that.node.html(template(data)); } }); + that.autosearch(); }); - mkws.registerWidgetType('Records', function() { var that = this; var team = this.team; this.team.queue("records").subscribe(function(data) { - var html = []; for (var i = 0; i < data.hits.length; i++) { var hit = data.hits[i]; that.team.queue("record").publish(hit); - var divId = team.recordElementId(hit.recid[0]); - html.push('
', renderSummary(hit), '
'); + hit.detailLinkId = team.recordElementId(hit.recid[0]); + hit.detailClick = "mkws.showDetails('" + team.name() + "', '" + hit.recid[0] + "');return false;"; + hit.containerClass = "mkwsSummary mkwsTeam_" + team.name(); + hit.containerClass += " " + hit.detailLinkId; // ### At some point, we may be able to move the // m_currentRecordId and m_currentRecordData members // from the team object into this widget. if (hit.recid == team.currentRecordId()) { - if (team.currentRecordData()) - html.push(team.renderDetails(team.currentRecordData())); + if (team.currentRecordData()) { + hit.renderedDetails = team.renderDetails(team.currentRecordData()); + console.log(hit.renderedDetails); + } } } - that.node.html(html.join('')); - - function renderSummary(hit) { - var template = team.loadTemplate(that.config.template || "Summary"); - hit._id = team.recordElementId(hit.recid[0]); - hit._onclick = "mkws.showDetails('" + team.name() + "', '" + hit.recid[0] + "');return false;" - return template(hit); - } + var template = team.loadTemplate(that.config.template || "Records"); + var targs = $.extend({}, {"hits": data.hits}, that.config.template_vars); + that.node.html(template(targs)); }); that.autosearch(); @@ -147,26 +140,29 @@ mkws.registerWidgetType('Records', function() { mkws.registerWidgetType('Navi', function() { var that = this; var teamName = this.team.name(); - var M = mkws.M; this.team.queue("navi").subscribe(function() { var filters = that.team.filters(); - var text = ""; + var output = {filters:[]}; filters.visitTargets(function(id, name) { - if (text) text += " | "; - text += M('source') + ': ' + name + ''; + var cur = {}; + cur.facet = 'source'; + cur.value = name; + cur.click = "mkws.delimitTarget('" + teamName + "', '" + id + "'); return false;"; + output.filters.push(cur); }); filters.visitFields(function(field, value) { - if (text) text += " | "; - text += M(field) + ': ' + value + ''; + var cur = {}; + cur.facet = field; + cur.value = value; + cur.click = "mkws.delimitQuery('" + teamName + "', '" + field + "', '" + value + "'" + ");return false;"; + output.filters.push(cur); }); - that.node.html(text); + var template = that.team.loadTemplate(that.config.template || "Navi"); + that.node.html(template(output)); }); }); @@ -202,9 +198,9 @@ mkws.registerWidgetType('Perpage', function() { mkws.registerWidgetType('Done', function() { var that = this; - this.team.queue("complete").subscribe(function(n) { - that.node.html("Search complete: found " + n + " records"); + var template = that.team.loadTemplate(that.config.template || "Done"); + that.node.html(template({count: n})); }); }); @@ -212,23 +208,21 @@ mkws.registerWidgetType('Done', function() { mkws.registerWidgetType('Switch', function() { if (!this.config.show_switch) return; var tname = this.team.name(); - this.node.html('\ -Records \ -| \ -Targets'); + var output = {}; + output.recordClick = "mkws.switchView(\'" + tname + "\', \'records\')"; + output.targetClick = "mkws.switchView(\'" + tname + "\', \'targets\')"; + var template = this.team.loadTemplate(this.config.template || "Switch"); + this.node.html(template(output)); this.hideWhenNarrow(); }); mkws.registerWidgetType('Search', function() { - var tname = this.team.name(); - var M = mkws.M; - - this.node.html('\ -
\ - \ - \ -
'); + var output = {}; + output.team = this.team.name(); + output.queryWidth = this.config.query_width; + var template = this.team.loadTemplate(this.config.template || "Search"); + this.node.html(template(output)); }); @@ -243,90 +237,42 @@ mkws.registerWidgetType('SearchForm', function() { mkws.registerWidgetType('Results', function() { - var tname = this.team.name(); - - this.node.html('\ -\ - \ - \ - \ - \ - \ - \ - \ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
\ -
'); - + var template = this.team.loadTemplate(this.config.template || "Results"); + this.node.html(template({team: this.team.name()})); this.autosearch(); }); mkws.registerWidgetType('Ranking', function() { - var tname = this.team.name(); - var that = this; - var M = mkws.M; - - var s = '
'; - if (this.config.show_sort) { - s += M('Sort by') + ' ' + mkwsHtmlSort() + ' '; - } - if (this.config.show_perpage) { - s += M('and show') + ' ' + mkwsHtmlPerpage() + ' ' + M('per page') + '.'; + var output = {}; + output.perPage = []; + output.sort = []; + output.team = this.team.name(); + output.showSort = this.config.show_sort; + output.showPerPage = this.config.show_perpage; + + var order = this.team.sortOrder(); + this.log("making sort, sortOrder = '" + order + "'"); + for (var i = 0; i < this.config.sort_options.length; i++) { + var cur = {}; + var opt = this.config.sort_options[i]; + cur.key = opt[0]; + cur.label = opt.length == 1 ? opt[0] : opt[1]; + if (order == cur.key || order == cur.label) cur.selected = true; + output.sort.push(cur); } - s += '
'; - - this.node.html(s); - - - function mkwsHtmlSort() { - var order = that.team.sortOrder(); - - that.log("making sort HTML, sortOrder = '" + order + "'"); - var sort_html = ''; - - return sort_html; + var perpage = this.team.perpage(); + this.log("making perpage, perpage = " + perpage); + for(var i = 0; i < this.config.perpage_options.length; i++) { + var cur = {}; + cur.perPage = this.config.perpage_options[i]; + if (cur.perPage == perpage) cur.selected = true; + output.perPage.push(cur); } - function mkwsHtmlPerpage() { - var perpage = that.team.perpage(); - - that.log("making perpage HTML, perpage = " + perpage); - var perpage_html = ''; - - return perpage_html; - } + var template = this.team.loadTemplate(this.config.template || "Ranking"); + this.node.html(template(output)); }); @@ -347,34 +293,30 @@ mkws.registerWidgetType('Lang', function() { } for (var k in mkws.locale_lang) { - if (toBeIncluded[k] || lang_options.length == 0) - list.push(k); + if (toBeIncluded[k] || lang_options.length == 0) { + cur = {}; + if (lang === k) cur.selected = true; + cur.code = k; + cur.url = lang_url(k); + list.push(cur); + } } // add english link - if (lang_options.length == 0 || toBeIncluded[lang_default]) - list.push(lang_default); + if (lang_options.length == 0 || toBeIncluded[lang_default]) { + cur = {}; + if (lang === lang_default) cur.selected = true; + cur.code = lang_default; + cur.url = lang_url(lang_default); + list.push(cur); + } this.log("language menu: " + list.join(", ")); - /* the HTML part */ - var data = ""; - for (var i = 0; i < list.length; i++) { - var l = list[i]; - if (data) - data += ' | '; - - if (lang == l) { - data += ' ' + l + ' '; - } else { - data += ' ' + l + ' ' - } - } - - this.node.html(data); + var template = this.team.loadTemplate(this.config.template || "Lang"); + this.node.html(template({languages: list})); this.hideWhenNarrow(); - // set or re-set "lang" URL parameter function lang_url(lang) { var query = location.search; @@ -422,19 +364,13 @@ mkws.registerWidgetType('Config', function() { mkws.registerWidgetType('Progress', function() { var that = this; - this.node.hide(); this.team.queue("stat").subscribe(function(data) { - var s = ''; - for (var i = 0; i < data.clients; i++) { - if (i == data.clients - data.activeclients) { - s += ''; - s += ''; - } - s += '█'; - } - s += ''; - that.node.html(s); + var template = that.team.loadTemplate(that.config.template || "Progress"); + that.node.html(template({ + done: data.clients - data.activeclients, + waiting: data.activeclients + })); that.node.show(); }); }); diff --git a/src/mkws-widget-main.templates/Done.handlebars b/src/mkws-widget-main.templates/Done.handlebars new file mode 100644 index 0000000..59a0b4f --- /dev/null +++ b/src/mkws-widget-main.templates/Done.handlebars @@ -0,0 +1,7 @@ +{{! +Displayed on search completion + +count - number of results found +}} +{{{mkws-translate "Search complete: found"}}} {{count}} {{{mkws-translate "records"}}} + diff --git a/src/mkws-widget-main.templates/Image.handlebars b/src/mkws-widget-main.templates/Image.handlebars new file mode 100644 index 0000000..abee587 --- /dev/null +++ b/src/mkws-widget-main.templates/Image.handlebars @@ -0,0 +1,18 @@ +{{! +Records presented as images. + +hits: + containerClass - class attribute for same + detailLinkId - id for the element triggering detail display + detailClick - a click event handler for details + renderedDetails - active record details rendered from the Record template + md-* - metadata fields passed through from backend +}} +{{#each hits}} + + {{#mkws-first md-thumburl}} + {{../md-title}} + {{/mkws-first}} +
+
+{{/each}} diff --git a/src/mkws-widget-main.templates/Lang.handlebars b/src/mkws-widget-main.templates/Lang.handlebars new file mode 100644 index 0000000..26ee6da --- /dev/null +++ b/src/mkws-widget-main.templates/Lang.handlebars @@ -0,0 +1,19 @@ +{{! +Language selection widget + +languages + code - two character language identifier + selected - exists for the current language + url - url to switch to this language +}} +{{#each languages}} + {{~#if selected~}} + {{code}} + {{~else~}} + {{code}} + {{~/if~}} + {{#unless last}} + | + {{/unless}} +{{/each}} + diff --git a/src/mkws-widget-main.templates/Navi.handlebars b/src/mkws-widget-main.templates/Navi.handlebars new file mode 100644 index 0000000..7f3c554 --- /dev/null +++ b/src/mkws-widget-main.templates/Navi.handlebars @@ -0,0 +1,13 @@ +{{! +Facet breadcrumbs -- filters on the current search + +filters + facet - name of facet being limited + value - limit to this value + click - handler script to remove limit +}} +{{#each filters}} + {{{mkws-translate facet}}}: {{value}} + {{#unless @last}}|{{/unless}} +{{/each}} + diff --git a/src/mkws-widget-main.templates/Pager.handlebars b/src/mkws-widget-main.templates/Pager.handlebars new file mode 100644 index 0000000..3f098e6 --- /dev/null +++ b/src/mkws-widget-main.templates/Pager.handlebars @@ -0,0 +1,46 @@ +{{! +Pager + +nextClick - handler script for "next" button, only available if there is a next page +prevClick - handler script for "previous" button if there is a previous page +moreNext - indicates there are more pages following those displayed +morePrev - indicates there are more pages preceding +first - first record displayed +last - last record displayed +count - number of records available +found - number of records found +pages: + number - page number + click - script to go to this page unless it is the current one +}} +
+ {{mkws-translate "Displaying"}}: + {{first}} {{mkws-translate "to"}} {{last}} + {{mkws-translate "of"}} {{count}} ({{{mkws-translate "found"}}}: {{found}}) +
+ +
+ {{#if prevClick}} + << {{{mkws-translate "Prev"}}} | + {{else}} + << {{{mkws-translate "Prev"}}} | + {{/if}} + + {{#if morePrev}}...{{/if}} + + {{#each pages}} + {{#if click}} + {{number}} + {{else}} + {{number}} + {{/if}} + {{/each}} + + {{#if moreNext}}...{{/if}} + + {{#if nextClick}} + | {{{mkws-translate "Next"}}} >> + {{else}} + | {{{mkws-translate "Next"}}} >> + {{/if}} +
diff --git a/src/mkws-widget-main.templates/Progress.handlebars b/src/mkws-widget-main.templates/Progress.handlebars new file mode 100644 index 0000000..1b08a2c --- /dev/null +++ b/src/mkws-widget-main.templates/Progress.handlebars @@ -0,0 +1,11 @@ +{{! +Progress + +done - number of targets complete +waiting - number of targets waiting +}} +{{#mkws-repeat done}}█{{/mkws-repeat}} +{{~#if waiting~}} +{{#mkws-repeat waiting}}█{{/mkws-repeat}} +{{~/if~}} + diff --git a/src/mkws-widget-main.templates/Ranking.handlebars b/src/mkws-widget-main.templates/Ranking.handlebars new file mode 100644 index 0000000..0f2bc17 --- /dev/null +++ b/src/mkws-widget-main.templates/Ranking.handlebars @@ -0,0 +1,42 @@ +{{! +Ranking -- widget to select sort ordering and number of records to display + +team - team for this widget +showSort +showPerPage +sort + key - machine readable value for this sort option + label - text to display for this sort option + selected - exists if this sort is selected +perPage + perPage - a number of records per page that can be selected + selected - exists if this number is the current selection +}} +
+ {{~#if showSort~}} + {{{mkws-translate "Sort by"}}} + + {{~/if~}} + {{~#if showPerPage~}} + {{{mkws-translate "and show"}}} + + {{{mkws-translate "per page."}}} + {{~/if~}} +
+ diff --git a/src/mkws-widget-main.templates/Record.handlebars b/src/mkws-widget-main.templates/Record.handlebars new file mode 100644 index 0000000..f8bf951 --- /dev/null +++ b/src/mkws-widget-main.templates/Record.handlebars @@ -0,0 +1,59 @@ +{{! +Full record display. +}} + + + + + + {{#if md-date}} + + + + + {{/if}} + {{#if md-author}} + + + + + {{/if}} + {{#if md-electronic-url}} + + + + + {{/if}} + {{#mkws-if-any location having="md-subject"}} + + + + + {{/mkws-if-any}} + + + + +
{{mkws-translate "Title"}} + {{md-title}} + {{#if md-title-remainder}} + ({{md-title-remainder}}) + {{/if}} + {{#if md-title-responsibility}} + {{md-title-responsibility}} + {{/if}} +
{{mkws-translate "Date"}}{{md-date}}
{{mkws-translate "Author"}}{{md-author}}
{{mkws-translate "Links"}} + {{#each md-electronic-url}} + Link{{mkws-index1}} + {{/each}} +
{{mkws-translate "Subject"}} + {{#mkws-first location having="md-subject"}} + {{#if md-subject}} + {{#mkws-commaList md-subject}} + {{this}}{{/mkws-commaList}} + {{/if}} + {{/mkws-first}} +
{{mkws-translate "Locations"}} + {{#mkws-commaList location}} + {{mkws-attr "@name"}}{{/mkws-commaList}} +
diff --git a/src/mkws-widget-main.templates/Records.handlebars b/src/mkws-widget-main.templates/Records.handlebars new file mode 100644 index 0000000..03ba9a2 --- /dev/null +++ b/src/mkws-widget-main.templates/Records.handlebars @@ -0,0 +1,26 @@ +{{! +Records from a search. + +hits: + containerClass - class attribute for same + detailLinkId - id for the element triggering detail display + detailClick - a click event handler for details + renderedDetails - active record details rendered from the Record template + md-* - metadata fields passed through from backend +}} +{{#each hits}} +
+ + {{md-title}} + + {{#if md-title-remainder}} + {{md-title-remainder}} + {{/if}} + {{#if md-title-responsibility}} + {{md-title-responsibility}} + {{/if}} + {{#if renderedDetails}} + {{{renderedDetails}}} + {{/if}} +
+{{/each}} diff --git a/src/mkws-widget-main.templates/Results.handlebars b/src/mkws-widget-main.templates/Results.handlebars new file mode 100644 index 0000000..f0d405c --- /dev/null +++ b/src/mkws-widget-main.templates/Results.handlebars @@ -0,0 +1,24 @@ +{{! +Results -- compound widget to display search results + +team - team for this widget +}} + + + + + + + + +
+
+
+
+
+
+
+
+
+
+ diff --git a/src/mkws-widget-main.templates/Search.handlebars b/src/mkws-widget-main.templates/Search.handlebars new file mode 100644 index 0000000..5530013 --- /dev/null +++ b/src/mkws-widget-main.templates/Search.handlebars @@ -0,0 +1,11 @@ +{{! +Search form + +team - MKWS team +queryWidth - configured width for search box +}} +
+ + +
+ diff --git a/src/mkws-widget-main.templates/Stat.handlebars b/src/mkws-widget-main.templates/Stat.handlebars new file mode 100644 index 0000000..d8ae997 --- /dev/null +++ b/src/mkws-widget-main.templates/Stat.handlebars @@ -0,0 +1,9 @@ +{{! +Search statistics + +activeclients - number of targets currently searching +clients - total targets for this search +records - number of records returned and available +hits - number of hits across all targets +}} + -- {{{mkws-translate "Active clients"}}} : {{activeclients}}/{{clients}} -- {{{mkws-translate "Retrieved records"}}} : {{records}}/{{hits}} diff --git a/src/mkws-widget-main.templates/Switch.handlebars b/src/mkws-widget-main.templates/Switch.handlebars new file mode 100644 index 0000000..2b8e29c --- /dev/null +++ b/src/mkws-widget-main.templates/Switch.handlebars @@ -0,0 +1,9 @@ +{{! +Switch between record and target view + +recordClick - handler to switch to record view +targetClick - handler to switch to target view +}} +{{{mkws-translate "Records"}}} +| +{{{mkws-translate "Targets"}}} diff --git a/src/mkws-widget-main.templates/Targets.handlebars b/src/mkws-widget-main.templates/Targets.handlebars new file mode 100644 index 0000000..99ed801 --- /dev/null +++ b/src/mkws-widget-main.templates/Targets.handlebars @@ -0,0 +1,33 @@ +{{! +Target detail + +data: + id - target id + hits - number of hits for this target + diagnostic - + records - + state - target state +}} + + + + + + + + + + + + + {{#each data}} + + + + + + + + {{/each}} + +
{{{mkws-translate "Target ID"}}}{{{mkws-translate "Hits"}}}{{{mkws-translate "Diags"}}}{{{mkws-translate "Records"}}}{{{mkws-translate "State"}}}
{{{id}}}{{hits}}{{diagnostic}}{{records}}{{hits}}
diff --git a/src/mkws-widget-record.js b/src/mkws-widget-record.js index 106c8a3..13d67c3 100644 --- a/src/mkws-widget-record.js +++ b/src/mkws-widget-record.js @@ -1,10 +1,18 @@ +// A widget for one record mkws.registerWidgetType('Record', function() { - mkws.promotionFunction('Records').call(this); if (!this.config.maxrecs) this.config.maxrecs = 1; + var that = this; + var team = this.team; + team.queue("records").subscribe(function(data) { + var template = team.loadTemplate(that.config.template || "Record"); + var targs = $.extend({}, data.hits[0], that.config.template_vars); + that.node.html(template(targs)); + }); + that.autosearch(); }); mkws.registerWidgetType('Image', function() { - mkws.promotionFunction('Record').call(this); + mkws.promotionFunction('Records').call(this); if (!this.config.template) this.config.template = 'Image'; }); diff --git a/src/mkws-widget-reference.js b/src/mkws-widget-reference.js index b1044d2..c8d41be 100644 --- a/src/mkws-widget-reference.js +++ b/src/mkws-widget-reference.js @@ -2,19 +2,6 @@ mkws.registerWidgetType('Reference', function() { mkws.promotionFunction('Record').call(this); if (!this.config.target) this.config.target = 'wikimedia_wikipedia_single_result'; if (!this.config.template) this.config.template = 'Reference'; - var nPara = this.config.paragraphs || 0; - var nSent = this.config.sentences || 0; - - this.team.registerTemplate('Reference', '\ - {{md-title}}\ -

{{md-title}}

\ -{{#if md-title-remainder}}\ - {{md-title-remainder}}\ -{{/if}}\ -{{#if md-title-responsibility}}\ - {{md-title-responsibility}}\ -{{/if}}\ - {{{mkws-paragraphs md-description ' + nPara + ' ' + nSent + '}}}\ -

Wikipedia

\ -'); + this.config.template_vars.paragraphs = this.config.paragraphs || 0; + this.config.template_vars.sentences = this.config.sentences || 0; }); diff --git a/src/mkws-widget-reference.templates/Reference.handlebars b/src/mkws-widget-reference.templates/Reference.handlebars new file mode 100644 index 0000000..62a9d2a --- /dev/null +++ b/src/mkws-widget-reference.templates/Reference.handlebars @@ -0,0 +1,17 @@ +{{! +Paragraphs and images from a reference source. + +sentences - number of sentences to include +paragraphs - number of paragraphs to include +md-* - metadata fields passed through from backend +}} +{{md-title}} +

{{md-title}}

+{{#if md-title-remainder}} +{{md-title-remainder}} +{{/if}} +{{#if md-title-responsibility}} +{{md-title-responsibility}} +{{/if}} +{{{mkws-paragraphs md-description paragraphs sentences}}} +

Wikipedia

diff --git a/src/mkws-widget-termlists.js b/src/mkws-widget-termlists.js index fef616f..8f6e467 100644 --- a/src/mkws-widget-termlists.js +++ b/src/mkws-widget-termlists.js @@ -1,22 +1,19 @@ mkws.registerWidgetType('Termlists', function() { - var that = this; - // Initially hide the termlists; display when we get results + var that = this; + var team = this.team; mkws.$(document).ready(function() { that.node.hide(); }); - this.team.queue("termlists").subscribe(function(data) { + team.queue("termlists").subscribe(function(data) { that.node.show(); }); - var acc = []; - var facets = this.config.facets; - acc.push('
' + mkws.M('Termlists') + '
'); - for (var i = 0; i < facets.length; i++) { - acc.push('
', '
'); - } - this.node.html(acc.join('')); - + var template = team.loadTemplate(this.config.template || "Termlists"); + this.node.html(template({ + team: team.name(), + facets: this.config.facets + })); this.autosearch(); }); @@ -27,8 +24,8 @@ mkws.registerWidgetType('Facet', function() { subject: [ "Subjects", 10, true ], author: [ "Authors", 10, true ] } - var that = this; + var team = this.team; var name = that.config.facet; var ref = facetConfig[name] || [ "Unknown", 10, true ]; var caption = this.config['facet_caption_' + name] || ref[0]; @@ -36,47 +33,50 @@ mkws.registerWidgetType('Facet', function() { var pzIndex = ref[2] ? name : null; that.toString = function() { - return '[Widget ' + that.team.name() + ':' + that.type + '(' + name + ')]'; + return '[Widget ' + team.name() + ':' + that.type + '(' + name + ')]'; }; - var t2 = that.team.loadTemplate('Facet-' + caption); - that.log("template for Facet-" + caption + " is " + !!t2); - if (!t2) { - that.log("no " + caption + "-specific template, falling back"); - t2 = that.team.loadTemplate('Facet'); - } - that.log("template for Facet is " + !!t2); - - that.team.queue("termlists").subscribe(function(data) { + team.queue("termlists").subscribe(function(data) { data = data[name]; - var t1 = that.team.loadTemplate('facetTitle-' + caption, mkws.M(caption)) - var title = t1({ query: that.config.query }); - var acc = []; - acc.push('
', title, '
'); - - var teamName = that.team.name(); + var terms = []; + var teamName = team.name(); for (var i = 0; i < data.length && i < max; i++) { - var fn, field; - // General case modifies the query; special case selects a target - if (pzIndex) { - fn = 'limitQuery'; field = pzIndex; - } else if (!that.team.targetFiltered(data[i].id)) { - fn = 'limitTarget'; field = data[i].id; + var linkdata = ""; + var action = ""; + if (!pzIndex) { + // Special case: target selection + linkdata += ('target_id='+data[i].id+' '); + if (!team.targetFiltered(data[i].id)) { + action = 'mkws.limitTarget(\'' + teamName + '\', this.getAttribute(\'target_id\'),this.firstChild.nodeValue)'; + } + } else { + action = 'mkws.limitQuery(\'' + teamName + '\', \'' + pzIndex + '\', this.firstChild.nodeValue)'; } - - var s = t2({ - team: teamName, - fn: fn, - field: field, + linkdata += 'onclick="' + action + ';return false;"'; + terms.push({ term: data[i].name, count: data[i].freq, - query: that.config.query - }); - acc.push('
', s, '
'); + linkdata: linkdata + }); } - - that.node.html(acc.join('')); + // configured template > facet specific template > default facet template + var template; + if (that.config.template) { + template = team.loadTemplate(that.config.template); + } else { + template = team.loadTemplate("Facet-" + caption); + if (template) { + that.log("Using Facet-" + caption + " template.") + } else { + that.log("No " + caption + " specific template, using default.") + template = team.loadTemplate("Facet"); + } + } + that.node.html(template({ + name: name, + caption: caption, + terms: terms + })); }); - this.autosearch(); }); diff --git a/src/mkws-widget-termlists.templates/Facet.handlebars b/src/mkws-widget-termlists.templates/Facet.handlebars new file mode 100644 index 0000000..621c9c1 --- /dev/null +++ b/src/mkws-widget-termlists.templates/Facet.handlebars @@ -0,0 +1,17 @@ +{{! +A facet in the search. + +name - facet identifier, typically English and lowercase +caption - caption for this facet +terms: + term - term name + count - count of items matching in the current search for this team + linkdata - attributes to add to the term element including an onclick handler +}} + +
{{caption}}
+{{#each terms}} +
+ {{term}} {{count}} +
+{{/each}} diff --git a/src/mkws-widget-termlists.templates/Termlists.handlebars b/src/mkws-widget-termlists.templates/Termlists.handlebars new file mode 100644 index 0000000..7c6f3b8 --- /dev/null +++ b/src/mkws-widget-termlists.templates/Termlists.handlebars @@ -0,0 +1,11 @@ +{{! +Termlists, a container of all configured facets. + +team - the current team +facets - array of facet names +}} + +
Termlists
+{{#each facets}} +
+{{/each}} diff --git a/tools/htdocs/Makefile b/tools/htdocs/Makefile index 420bb49..1f78719 100644 --- a/tools/htdocs/Makefile +++ b/tools/htdocs/Makefile @@ -2,7 +2,7 @@ SRC= ../../src -HANDLEBARS_FILE = handlebars-v1.1.2.js +HANDLEBARS_FILE = handlebars-v1.3.0.js JQUERY_FILE = jquery-1.10.0.min.js JQUERY_JSON_FILE = jquery.json-2.4.js PP2_FILE = pz2.js @@ -19,6 +19,7 @@ COMPONENTS = ${SRC}/mkws-handlebars.js \ ${SRC}/mkws-core.js \ ${SRC}/mkws-team.js \ ${SRC}/mkws-filter.js \ + ${SRC}/mkws-templates.js \ ${SRC}/mkws-popup.js \ ${SRC}/mkws-widget.js \ ${SRC}/mkws-widget-main.js \ @@ -31,7 +32,7 @@ COMPONENTS = ${SRC}/mkws-handlebars.js \ ${SRC}/mkws-widget-builder.js GENERATED = ${HANDLEBARS_FILE} ${JQUERY_FILE} ${JQUERY_JSON_FILE} ${PP2_FILE} \ - mkws.js mkws.min.js mkws-complete.js mkws-complete.min.js + mkws.js mkws.min.js mkws-complete.js mkws-complete.min.js mkws-templates.js **make-default**: all @@ -89,12 +90,15 @@ release: mkws.js mkws-complete.js mkws.min.js mkws-complete.min.js echo "Made release $(VERSION)"; \ fi -mkws.js: $(COMPONENTS) Makefile +mkws.js: mkws-templates.js $(COMPONENTS) Makefile rm -f $@ cat ${COMPONENTS} > $@.tmp mv -f $@.tmp $@ chmod 444 $@ +mkws-templates.js: + handlebars -n mkws.defaultTemplates ${SRC}/*.templates/*.handlebars -f ${SRC}/mkws-templates.js + mkws-html-includes: echo $(COMPONENTS) | perl -npe "s,${SRC},,g; s/\s+/\0/g" | \ perl -n0e 'chomp(); print qq{ \n}'