Merge branch 'master' into templateallthemarkup
authorJason Skomorowski <jason@indexdata.com>
Mon, 7 Jul 2014 23:30:04 +0000 (19:30 -0400)
committerJason Skomorowski <jason@indexdata.com>
Mon, 7 Jul 2014 23:30:04 +0000 (19:30 -0400)
Conflicts:
src/.gitignore
src/mkws-core.js
src/mkws-team.js
src/mkws-widget-termlists.js
tools/htdocs/Makefile

44 files changed:
Makefile
README
doc/Makefile
doc/library-configuration.txt
examples/apache2/mkws-examples
examples/apache2/mkws-examples-mike
examples/htdocs/auto3.html
examples/htdocs/jasmine-cors-popup.html [new file with mode: 0644]
examples/htdocs/jasmine-local-popup.html
examples/htdocs/jasmine-popup.html
examples/htdocs/jasmine-pp2.html
examples/htdocs/jasmine.html
examples/htdocs/mike.html
examples/htdocs/mike2.html
examples/htdocs/mkws-widget-credo.css
examples/htdocs/mkws-widget-credo.js
examples/htdocs/wolfram.html
src/.gitignore [deleted file]
src/Makefile [deleted file]
src/mkws-core.js
src/mkws-handlebars.js
src/mkws-jquery.js
src/mkws-team.js
src/mkws-templates.js [new file with mode: 0644]
src/mkws-widget-main.js
src/releases/.gitkeep [new file with mode: 0644]
test/.gitignore
test/Makefile
test/bin/apache-template-update
test/bin/bomb.pl
test/etc/pazpar2/settings/mkws.xml [new file with mode: 0644]
test/package.json
test/phantom/run-jasmine.js
test/spec/mkws-pazpar2.js
tools/apache2/DELETED/mkws-mike [deleted file]
tools/apache2/mkws-live
tools/apache2/mkws-proxy
tools/apache2/mkws-ssl-px
tools/apache2/mkws-test
tools/apache2/sp-mkws-live [new file with mode: 0644]
tools/htdocs/Makefile [new file with mode: 0644]
tools/htdocs/external/koha/Makefile
tools/htdocs/external/koha/koha-mkws-complete.css
tools/sp-htdocs/index.html [new file with mode: 0644]

index 9504c36..32309e5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,11 +1,11 @@
 # Copyright (c) 2013-2014 IndexData ApS. http://indexdata.com
 
 all:
-       ${MAKE} -C./src $@
-       ${MAKE} -C./doc $@
+       ${MAKE} -C./tools/htdocs $@
+       ${MAKE} -C./doc install
 
 clean distclean:
-       ${MAKE} -C./src $@
+       ${MAKE} -C./tools/htdocs $@
        ${MAKE} -C./doc $@
        ${MAKE} -C./examples/htdocs $@
        ${MAKE} -C./test $@
@@ -18,12 +18,9 @@ phantomjs p:
 
 # must be called once after GIT checkout
 setup: 
-#why?  ${MAKE} -C./tools/htdocs mkws-js-min
        ${MAKE} -C./test node-modules
 
-check: setup check-js
-       @echo ""
-       @echo "To run jasmine regression tests, type: make phantomjs"
+check: setup check-js phantomjs
 
 help:
        @echo "make [ all | setup | clean | distclean ]"
diff --git a/README b/README
index 92df4ce..7aacc48 100644 (file)
--- a/README
+++ b/README
@@ -21,7 +21,7 @@ Required devel tools
 ====================
 
 On debian, you will need:
-$ sudo apt-get install curl git-core pandoc yui-compressor node-js
+$ sudo apt-get install curl git-core pandoc yui-compressor node-js libbsd-resource-perl
 
 On Debian 7 (wheezy), you do not need git-core, plain git will do, but
 you probably have that on a development box already. Unfortunately, node-js
index 75a9e2d..34dc62e 100644 (file)
@@ -14,7 +14,7 @@ uninstall:
 ../tools/htdocs/%: %
        rm -f $@
        cp -p $? $@
-       chmod -w $@
+       chmod ugo-w $@
 
 all: $(DOCS)
 
index 7abcfc0..5132887 100644 (file)
@@ -2,72 +2,112 @@ MKWS Target Selection
 =====================
 
 
+MKWS accesses targets using the Pazpar2 metasearching engine, almost
+always fronted by the Service Proxy to manage target selection. This
+document assumes the SP is used, and so that a library of targets is
+available, maintained using an instance of MKAdmin (often
+http://mkc-admin.indexdata.com/console/)
+
+
 1. Selecting targets within the library
 ---------------------------------------
 
-MKWS applications can choose what subset of the available targets to
+MKWS applications can choose what subset of the library's targets to
 use, by means of several alternative settings on individual widgets or
 in the mkws_config structure:
 
 * targets -- contains a Pazpar2 targets string, typically of the form
   "pz:id=" or "pz:id~" followed by a pipe-separated list of low-level
-  target IDs. At present, these IDs are based on ZURLs, so a typical
-  value would be something like:
-       pz:id~josiah.brown.edu:210/innopac|connect.indexdata.com:9000/mit_opencourseware'
+  target IDs.
+
+  At present, these IDs can take one of two forms, depending on the
+  configuration of the Service Proxy being used: they may be based on
+  ZURLs, so a typical value would be something like:
+       pz:id=josiah.brown.edu:210/innopac|lui.indexdata.com:8080/solr4/select?fq=database:4902
+  Or they may be UDBs, so a typical value would be something like:
+       pz:id=brown|artstor
 
 * targetfilter -- contains a CQL query which is used to find relevant
   targets from the relvant library. For example,
        udb==Google_Images
+  Or
+       categories=news
 
 * target -- contains a single UDB, that of the sole target to be
   used. For example
        Google_Images
+  This is merely syntactic sugar for "targetfilter" with the query
+       udb==NAME
 
 
-2. Changing the library
------------------------
+2. Authenticating onto the library
+----------------------------------
 
-Some MKWS applications will want to define their own library providing
-a different range of available targets. This is particularly important
-in the case of applications that authenticate onto subscription
-resources by means of credentials stored in MKAdmin, in that such
-library accounts need to prohibit unauthorised access.
+Some MKWS applications will be content to use the default library with
+its selection of targets. Most, though, will want to define their own
+library providing a different range of available targets. An important
+case is that of applications that authenticate onto subscription
+resources by means of credentials stored in MKAdmin: precautions must
+be taken so that such library accounts do not allow unauthorised
+access.
 
-Setting up such a library is a two-stage process.
+Setting up such a library is a two, three or four-stage process.
 
-Stage A (on MKAdmin)
+Stage A: create the library
 
-Create the library:
+Use MKAdmin to create the library:
        - Make a new library on http://mkc-admin.indexdata.com/console/
        - Select relevant targets
-       - Add authentication credentials as necessary
+       - Add authentication credentials to the targets as necessary
        - Create an end-user account
-       - Set its username and password
-
-Stage B (on the application's web-server):
-
-Authentication onto the library can be achieved by a single HTTP GET
-to the relevant Service Proxy, passing in the credentials and thereby
-initiating an HTTP session. This can most simply be done just by
-setting service_proxy_auth to a URL such as
-       http://mkws.indexdata.com/service-proxy/?command=auth&action=login&username=MIKE&password=SWORDFISH
-
-However, doing so reveals the the credentials to public view -- to
-anyone who does View Source on the MKWS application. This may be
-acceptable for some libraries, but is intolerable for those which
-provide authenticated access to subscription resources. For such
-circumstances, a more elaborate approach is necessary. The idea is to
-make a local URL that is used for authentication onto the Service
-Proxy, hiding the credentials, and to use local mechanisms to limit
-access to that local authentication URL. Here is one way to do it when
-Apache2 is the application's web-server:
+       - Depending on what authentication method it be used, set the
+         end-user account's username and password, or IP-address
+         range, or referring URL, or hostname.
+
+Stage B: tell the application to use the library
+
+In the HTML of the application, tell MKWS to authenticate on to the
+Service Proxy. When IP-based, referer-based or hostname-based
+authentication is used, this is very simple:
+
+       <script type="text/javascript">
+         var mkws_config = { service_proxy_auth:
+         "http://mkws.indexdata.com/service-proxy/?command=auth&action=check,login" };
+        </script>
+
+And ensure that access to the MWKS application is from the correct
+IP-range, referer or hostname.
+
+Stage C (optional): embed credentials for access to the library
+
+When credential-based authentication is in use (username and
+password), it's necessary to pass these credentials into the Service
+Proxy when establishing the session. This can most simply be done just
+by setting the service_proxy_auth configuration item to a URL such as
+       http://mkws.indexdata.com/service-proxy/?command=auth&action=check,login&username=MIKE&password=SWORDFISH
+
+Stage D (optional): conceal credentials from HTML source
+
+Using a Service-Proxy authentication URL such as the one above reveals
+the the credentials to public view -- to anyone who does View Source
+on the MKWS application. This may be acceptable for some libraries,
+but is intolerable for those which provide authenticated access to
+subscription resources.
+
+In these circumstances, a more elaborate approach is necessary. The
+idea is to make a local URL that is used for authentication onto the
+Service Proxy, hiding the credentials, and to use local mechanisms to
+limit access to that local authentication URL. Here is one way to do
+it when Apache2 is the application's web-server, which we will call
+example.com:
 
        - Add a rewriting authentication alias to the configuration:
                RewriteEngine on
-               RewriteRule /spauth/ http://mkws.indexdata.com/service-proxy/?command=auth&action=login&username=U&password=PW [P]
-       - Extend the MKWS configuration to set service_proxy_auth:
-               http://application.com/spauth/
-       - Protect access to /apauth/ (e.g. using a .htaccess file).
+               RewriteRule /spauth/ http://mkws.indexdata.com/service-proxy/?command=auth&action=check,login&username=U&password=PW [P]
+       - Set thwe MKWS configuration item "service_proxy_auth" to:
+               http://example.com/spauth/
+       - Protect access to the local path http://example.com/spauth/
+               (e.g. using a .htaccess file).
 
 Once such a library has been set up, and access to it established,
 target selection within the set that it makes available can be done
index 472071b..42f0673 100644 (file)
@@ -3,6 +3,10 @@
 <VirtualHost *:80>
     ServerName example.indexdata.com
     DocumentRoot /home/indexdata/mkws/examples/htdocs/
+    Alias /tools/htdocs/ /home/indexdata/mkws/tools/htdocs/
+    Alias /src/ /home/indexdata/mkws/src/
+    Alias /jasmine/ /home/indexdata/mkws/examples/jasmine/
+    Alias /test/ /home/indexdata/mkws/test/
     ErrorLog /var/log/apache2/mkws-examples-error.log
     CustomLog /var/log/apache2/mkws-examples-access.log combined
 
index c867600..4167584 100644 (file)
@@ -3,6 +3,9 @@
     DocumentRoot /usr/local/src/git/mkws/examples/htdocs/
     Alias /tools/htdocs/ /usr/local/src/git/mkws/tools/htdocs/
     Alias /src/ /usr/local/src/git/mkws/src/
+    Alias /jasmine/ /usr/local/src/git/mkws/examples/jasmine/
+    Alias /test/ /usr/local/src/git/mkws/test/
+
     ErrorLog /var/log/apache2/mkws-examples-error.log
     CustomLog /var/log/apache2/mkws-examples-access.log combined
 
index 6f60df0..4fef994 100644 (file)
@@ -38,7 +38,7 @@
 <div class='mkwsRecords mkwsTeam_plos'
        autosearch='dinosaur'
        sort='relevance'
-       targets='pz:id~lui.indexdata.com:8080/solr4/#6265'
+       targets='pz:id=lui.indexdata.com:8080/solr4/select?fq=database:6265/'
 >PLOS ONE articles will appear here</div>
        </td>
        <td style="background: #e0f8f0">
@@ -46,7 +46,7 @@
 <div class='mkwsRecords mkwsTeam_gutenberg'
        autosearch='dinosaur'
        sort='relevance'
-       targets='pz:id~lui.indexdata.com:8080/solr4/#3552'
+       targets='pz:id=lui.indexdata.com:8080/solr4/select?fq=database:3552/'
 >Free e-books will appear here</div>
        </td>
        <td style="background: #e0f0f8">
@@ -54,7 +54,7 @@
 <div class='mkwsRecords mkwsTeam_blog'
        autosearch='dinosaur'
        sort='relevance'
-       targets='pz:id~lui.indexdata.com:8080/solr4/#5802'
+       targets='pz:id=lui.indexdata.com:8080/solr4/select?fq=database:5802/'
 >Library catalog entries will appear here</div>
        </td>
       </tr>
diff --git a/examples/htdocs/jasmine-cors-popup.html b/examples/htdocs/jasmine-cors-popup.html
new file mode 100644 (file)
index 0000000..d7d1a7a
--- /dev/null
@@ -0,0 +1,95 @@
+<html>
+  <head>
+    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+    <title>MKWS demo: jQuery popup plugin with jasmine test framework</title>
+
+    <script type="text/javascript" src="//code.jquery.com/jquery-1.10.0.min.js"></script>
+    <script>
+      // create a function in external jquery object
+      $.extend({
+       mkws_external_jquery_object: function () { return true; }
+      });
+    </script>
+
+    <script type="text/javascript" src="//mkws.indexdata.com/mkws-complete.js"></script>
+    <link rel="stylesheet" type="text/css" href="//mkws.indexdata.com/mkws.css" />
+
+    <script type="text/javascript" src="//code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
+    <link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
+
+    <link rel="shortcut icon" type="image/png" href="jasmine/lib/jasmine-1.3.1/jasmine_favicon.png">
+    <link rel="stylesheet" type="text/css" href="jasmine/lib/jasmine-1.3.1/jasmine.css">
+    <script type="text/javascript" src="jasmine/lib/jasmine-1.3.1/jasmine.js"></script>
+    <script type="text/javascript" src="jasmine/lib/jasmine-1.3.1/jasmine-html.js"></script>
+
+    <script type="text/javascript" src="test/spec/true.spec.js"></script>
+    <script type="text/javascript" src="test/spec/mkws-config.js"></script>
+    <script type="text/javascript" src="test/spec/mkws-pazpar2.js"></script>
+
+    <script type="text/javascript" src="test/js/mkws-jasmine-run.js"></script>
+    <script type="text/javascript"> mkws_jasmine_init(500); </script>
+
+    <style type="text/css">
+      body > div > form > input.ui-state-default { background: none; }
+    </style>
+  </head>
+
+  <body>
+    <script>
+      // jquery.js from <head> section
+      if (!($.mkws_external_jquery_object && $.mkws_external_jquery_object())) {
+       alert("External jquery(1) mis-match!!!");
+      }
+      if (mkws.$.mkws_external_jquery_object && mkws.$.mkws_external_jquery_object()) {
+       alert("Internal jquery(1) mis-match!!!");
+      }
+    </script>
+
+    <script type="text/javascript">
+    var mkws_config = {
+      perpage_default: 10
+    };
+
+    var jasmine_config = {
+      search_query: "netbsd",
+      expected_hits: 10,
+      check_motd: false,
+      show_record_url: true
+    };
+    </script>
+
+    <h2>MKWS Jasmine regression tests</h2>
+
+      <div class="mkwsSwitch"></div>
+      <div class="mkwsLang"></div>
+      <div class="mkwsProgress"></div>
+      <div class="mkwsSearch"></div>
+      <div class="mkwsResults"></div>
+      <div class="mkwsTargets"></div>
+      <div class="mkwsStat"></div>
+      <div class="mkwsBuilder"></div>
+
+      <!-- include jquery again -->
+      <script type="text/javascript" src="//code.jquery.com/jquery-1.10.0.js"></script>
+      <script>
+       $(document).ready(function() {
+         // now the second jquery is used
+         if ($.mkws_external_jquery_object && $.mkws_external_jquery_object()) {
+           alert("External jquery(2) mis-match!!!");
+         }
+         if (mkws.$.mkws_external_jquery_object && mkws.$.mkws_external_jquery_object()) {
+           alert("Internal jquery(2) mis-match!!!");
+         }
+
+         // jquery null test
+         $ = undefined;
+         jQuery = undefined;
+
+         if (!mkws.$) {
+           alert("MKWS jquery(3) is not set!!!");
+         }
+
+       });
+      </script>
+  </body>
+</html>
index 02f1863..3a85e82 100644 (file)
@@ -6,12 +6,11 @@
     <link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
     <link rel="stylesheet" type="text/css" href="tools/htdocs/mkws.css" />
 
-    <script type="text/javascript" src="src/jquery-1.10.0.min.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery-1.10.0.min.js"></script>
     <script type="text/javascript" src="//code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
-    <script type="text/javascript" src="src/pz2.js"></script>
-    <script type="text/javascript" src="src/jquery.json-2.4.js"></script>
-    <script type="text/javascript" src="src/handlebars-v1.1.2.js"></script>
-    <!-- <script type="text/javascript" src="src/mkws.js"></script> -->
+    <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
+    <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
     <script type="text/javascript" src="src/mkws-handlebars.js"></script>
     <script type="text/javascript" src="src/mkws-core.js"></script>
     <script type="text/javascript" src="src/mkws-team.js"></script>
@@ -55,8 +54,9 @@
     var jasmine_config = {
       search_query: "netbsd",
       expected_hits: 10,
-      // active_clients: 17,
+      active_clients: 13,
       check_motd: false,
+      check_sortby: true,
       show_record_url: true
     };
 
     });
     </script>
 
-    <pre>
-An embryonic MasterKey Widget Set
-=================================
-
-This directory contains an embryonic MasterKey Widget Set, based
-initially on "jsdemo" though now far removed from those beginnnings.
-[...]
-    </pre>
+<h2>MKWS Jasmine regression tests</h2>
+<p/>
 
   </body>
 </html>
index f00cd84..c27e0f4 100644 (file)
@@ -6,13 +6,11 @@
     <link rel="stylesheet" type="text/css" href="//code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css" />
     <link rel="stylesheet" type="text/css" href="tools/htdocs/mkws.css" />
 
-    <script type="text/javascript" src="//code.jquery.com/jquery-1.10.0.min.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery-1.10.0.min.js"></script>
     <script type="text/javascript" src="//code.jquery.com/ui/1.10.3/jquery-ui.min.js"></script>
-    <script type="text/javascript" src="//git.indexdata.com/?p=pazpar2.git;a=blob_plain;f=js/pz2.js;hb=HEAD"></script>
-    <script type="text/javascript" src="//jquery-json.googlecode.com/files/jquery.json-2.4.js"></script>
-    <script type="text/javascript" src="//builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.1.2.js"></script>
-
-    <!-- <script type="text/javascript" src="src/mkws.js"></script> -->
+    <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
+    <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
     <script type="text/javascript" src="src/mkws-handlebars.js"></script>
     <script type="text/javascript" src="src/mkws-core.js"></script>
     <script type="text/javascript" src="src/mkws-team.js"></script>
     <script type="text/javascript" src="src/mkws-widget-main.js"></script>
     <script type="text/javascript" src="src/mkws-widget-termlists.js"></script>
     <script type="text/javascript" src="src/mkws-widget-authname.js"></script>
-    <script type="text/javascript" src="src/mkws-widget-categories.js"></script>
     <script type="text/javascript" src="src/mkws-widget-log.js"></script>
     <script type="text/javascript" src="src/mkws-widget-record.js"></script>
-    <script type="text/javascript" src="src/mkws-widget-builder.js"></script>
+
     <script type="text/javascript" src="src/mkws-jquery.js"></script>
-    <script type="text/javascript">$ = undefined;</script>
 
     <link rel="shortcut icon" type="image/png" href="jasmine/lib/jasmine-1.3.1/jasmine_favicon.png">
     <link rel="stylesheet" type="text/css" href="jasmine/lib/jasmine-1.3.1/jasmine.css">
     <script type="text/javascript" src="test/js/mkws-jasmine-run.js"></script>
     <script type="text/javascript"> mkws_jasmine_init(500); </script>
 
+    <!-- jquery null test 
+    <script type="text/javascript">$ = undefined;</script>
+       -->
+
     <style type="text/css">
       body > div > form > input.ui-state-default { background: none; }
     </style>
 
   <body>
     <script type="text/javascript">
+
+    // use a our main service proxy with mkws test account
     var mkws_config = {
       perpage_default: 10,
-      // active_clients: 17,
       pazpar2_url:          "//mkws.indexdata.com/service-proxy/",
       service_proxy_auth:   "//mkws.indexdata.com/service-proxy-testauth"
     };
 
+    var jasmine_config = {
+      search_query: "netbsd",
+      expected_hits: 10,
+      active_clients: 13,
+      check_motd: false,
+      show_record_url: true
+    };
+
     jQuery.pazpar2({
         "layout": "popup",               /* "table" [default], "div", "popup" */
-        "id_button": "input.mkwsButton", /* submit button id in search field */
-        "id_popup": ".mkwsPopup",        /* internal id of popup window */
         "width": 990,                    /* popup width, should be at least 800 */
         "height": 760                    /* popup height, should be at least 600 */
     });
     </script>
 
-    <h1 style= "font-family: Monaco, 'Lucida Console', monospace">Jasmine test of MKWS</h1>
-     <div id="testMOTD"><div class="mkwsMOTD">This is the mkwsMOTD div</div></div>
+<h2>MKWS Jasmine regression tests</h2>
+<p/>
+
   </body>
 </html>
index 60aa55f..0e106bb 100644 (file)
       };
     </script>
     <script type="text/javascript" src="//code.jquery.com/jquery-1.7.2.min.js"></script>
-    <script type="text/javascript" src="src/pz2.js"></script>
-    <script type="text/javascript" src="src/handlebars-v1.1.2.js"></script>
-    <script type="text/javascript" src="src/jquery.json-2.4.js"></script>
-    <script type="text/javascript" src="src/mkws.js"></script>
+    <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
+    <script type="text/javascript" src="tools/htdocs/mkws.js"></script>
 
     <link rel="stylesheet" type="text/css" href="tools/htdocs/mkws.css" />
     <style type="text/css">
index 66183f1..e47e374 100644 (file)
       };
     </script>
     <script type="text/javascript" src="//code.jquery.com/jquery-1.7.2.min.js"></script> 
-    <script type="text/javascript" src="src/pz2.js"></script>
-    <script type="text/javascript" src="src/handlebars-v1.1.2.js"></script>
-    <script type="text/javascript" src="src/jquery.json-2.4.js"></script>
-    <script type="text/javascript" src="src/mkws.js"></script>
+    <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
+    <script type="text/javascript" src="tools/htdocs/mkws.js"></script>
     <style type="text/css">
       .mkwsTermlists div.facet {
       float:left;
index b5afab9..2ea8c21 100644 (file)
@@ -1,17 +1,18 @@
 <html>
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-    <title>MKWS demo: Reference Universe widget</title>
-    <link rel="stylesheet" type="text/css" href="//x.mkws.indexdata.com/mkws.css" />
+    <title>MKWS demo: Orex</title>
+    <link rel="stylesheet" type="text/css" href="tools/htdocs/mkws.css" />
     <script type="text/javascript">
       var mkws_config = {
-        service_proxy_auth: "//mkws.indexdata.com/service-proxy/?command=auth&action=check,login&username=mkwstest&password=mkwstest"
+       pazpar2_url:        "//sp-mkws.indexdata.com/service-proxy/",
+       service_proxy_auth: "//sp-mkws.indexdata.com/service-proxy/?command=auth&action=check,login&username=orex&password=orexmkc"
       };
     </script>
     <script type="text/javascript" src="//code.jquery.com/jquery-1.10.0.min.js"></script>
-    <script type="text/javascript" src="//x.mkws.indexdata.com/jquery.json-2.4.js"></script>
-    <script type="text/javascript" src="//x.mkws.indexdata.com/handlebars-v1.1.2.js"></script>
-    <script type="text/javascript" src="//x.mkws.indexdata.com/pazpar2/js/pz2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
+    <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
     <script type="text/javascript" src="src/mkws-handlebars.js"></script>
     <script type="text/javascript" src="src/mkws-core.js"></script>
     <script type="text/javascript" src="src/mkws-team.js"></script>
@@ -32,7 +33,7 @@
     <div class="mkwsLang"></div>
     <div class="mkwsProgress"></div>
     <div class="mkwsSearch"></div>
-    <div class="mkwsResults" autosearch="!param!q" target="!param!t"></div>
+    <div class="mkwsResults"></div>
     <div class="mkwsTargets"></div>
     <div class="mkwsStat"></div>
     <div class="mkwsBuilder"></div>
index c51f022..188cbc2 100644 (file)
@@ -2,17 +2,16 @@
   <head>
     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
     <title>MKWS demo: Reference Universe widget</title>
-    <link rel="stylesheet" type="text/css" href="//x.mkws.indexdata.com/mkws.css"></script>
+    <link rel="stylesheet" type="text/css" href="tools/htdocs/mkws.css" />
     <script type="text/javascript">
       var mkws_config = {
-        //service_proxy_auth: "//mkws.indexdata.com/service-proxy-credoauth"
-        service_proxy_auth: "//mkws.indexdata.com/service-proxy/?command=auth&action=login&username=credo&password=emu",
+        service_proxy_auth: "//mkws.indexdata.com/service-proxy/?command=auth&action=check,login&username=mkwstest&password=mkwstest"
       };
     </script>
     <script type="text/javascript" src="//code.jquery.com/jquery-1.10.0.min.js"></script>
-    <script type="text/javascript" src="//x.mkws.indexdata.com/jquery.json-2.4.js"></script>
-    <script type="text/javascript" src="//x.mkws.indexdata.com/handlebars-v1.1.2.js"></script>
-    <script type="text/javascript" src="//x.mkws.indexdata.com/pazpar2/js/pz2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
+    <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
     <script type="text/javascript" src="src/mkws-handlebars.js"></script>
     <script type="text/javascript" src="src/mkws-core.js"></script>
     <script type="text/javascript" src="src/mkws-team.js"></script>
     <div class="mkwsLang"></div>
     <div class="mkwsProgress"></div>
     <div class="mkwsSearch"></div>
-    <div class="mkwsResults"></div>
+    <div class="mkwsResults" autosearch="!param!q" targets="!param!t"></div>
     <div class="mkwsTargets"></div>
     <div class="mkwsStat"></div>
     <div class="mkwsBuilder"></div>
   </body>
+  <script class="mkwsTemplate_facetTitle-Sources" type="text/x-handlebars-template">
+    <a href="?q={{query}}">Targets</a>
+  </script>
+  <script class="mkwsTemplate_facet-Sources" type="text/x-handlebars-template">
+    <a href="mike2.html?q={{query}}&t={{field}}">{{term}}</a>
+    <span>{{count}}</span>
+  </script>
 </html>
index 57c51cf..4091ee0 100644 (file)
     content: "Source: ";
     font-weight: bold;
 }
+
+.mkwsFacet a {
+    text-decoration: none;
+}
+
+.mkwsFacet a:hover {
+    text-decoration: underline;
+}
+
+.mkwsTerm {
+    clear: both;
+}
+
+.mkwsTerm span {
+    float: right;
+}
+
index 052aaec..0d8c3e6 100644 (file)
@@ -36,6 +36,9 @@ mkws.registerWidgetType('Credo', function() {
       </div>\
 ');
 
+  // Inhibit the display of the sole facet's title.
+  this.team.registerTemplate('facetTitle-Subject', 'doo');
+
   var s = []
   s.push('<table>');
 
@@ -52,7 +55,7 @@ mkws.registerWidgetType('Credo', function() {
 
   s.push('<td class="side">');
   s.push(section('topics', 'Related Topics',
-                 this.subwidget('Facet', { _team: 'main', facet: 'subject' })));
+                 this.subwidget('Facet', { _team: 'main', facet: 'subject', template: 'CredoFacet' })));
   s.push('</td>');
 
   s.push('</tr>');
index 6ac1edd..a15f554 100644 (file)
          service_proxy_auth : "/service-proxy-testauth"
       };
     </script>
-    <script type="text/javascript" src="src/jquery-1.10.0.min.js"></script>
-    <script type="text/javascript" src="src/pz2.js"></script>
-    <script type="text/javascript" src="src/handlebars-v1.1.2.js"></script>
-    <script type="text/javascript" src="src/jquery.json-2.4.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery-1.10.0.min.js"></script>
+    <script type="text/javascript" src="tools/htdocs/pz2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/handlebars-v1.1.2.js"></script>
+    <script type="text/javascript" src="tools/htdocs/jquery.json-2.4.js"></script>
    
-    <!-- <script type="text/javascript" src="src/mkws.js"></script> -->
+    <!-- <script type="text/javascript" src="tools/htdocs/mkws.js"></script> -->
     <script type="text/javascript" src="src/mkws-handlebars.js"></script>
     <script type="text/javascript" src="src/mkws-core.js"></script>
     <script type="text/javascript" src="src/mkws-team.js"></script>
diff --git a/src/.gitignore b/src/.gitignore
deleted file mode 100644 (file)
index ccd54ee..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-README.html
-README.odt
-README.pdf
-handlebars-v1.1.2.js
-jquery-1.10.0.min.js
-jquery.json-2.4.js
-mkws-complete.js
-mkws-complete.min.js
-mkws.js
-mkws.min.js
-pz2.js
-whitepaper.html
-whitepaper.odt
-whitepaper.pdf
-mkws-templates.js
diff --git a/src/Makefile b/src/Makefile
deleted file mode 100644 (file)
index 8e8e48d..0000000
+++ /dev/null
@@ -1,105 +0,0 @@
-HANDLEBARS_FILE = handlebars-v1.1.2.js
-JQUERY_FILE = jquery-1.10.0.min.js
-JQUERY_JSON_FILE = jquery.json-2.4.js
-PP2_FILE = pz2.js
-
-HANDLEBARS_URL = http://builds.handlebarsjs.com.s3.amazonaws.com/${HANDLEBARS_FILE}
-JQUERY_URL = http://code.jquery.com/${JQUERY_FILE}
-JQUERY_JSON_URL = https://jquery-json.googlecode.com/files/${JQUERY_JSON_FILE}
-PP2_URL = http://git.indexdata.com/?p=pazpar2.git;a=blob_plain;f=js/${PP2_FILE};hb=HEAD
-
-JQUERY_UI_URL =        http://code.jquery.com/ui/1.10.3/jquery-ui.js
-VERSION = $(shell tr -d '\012' < VERSION)
-
-COMPONENTS = mkws-handlebars.js\
-       mkws-core.js mkws-team.js mkws-filter.js \
-       mkws-templates.js\
-       mkws-widget.js mkws-widget-main.js mkws-widget-termlists.js \
-       mkws-widget-authname.js mkws-widget-categories.js mkws-widget-log.js \
-       mkws-widget-record.js mkws-widget-reference.js 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-templates.js
-
-INSTALLABLE = mkws-jquery.js VERSION NEWS $(GENERATED)
-
-INSTALLED = $(INSTALLABLE:%=../tools/htdocs/%)
-
-**make-default**: ../tools/htdocs/mkws.js
-
-install: $(INSTALLED)
-
-uninstall:
-       rm -f $(INSTALLED)
-
-../tools/htdocs/%: %
-       rm -f $@ && cp -p $? $@ && chmod 444 $@
-
-all: mkws.min.js mkws-complete.min.js
-
-mkws-js mkws-complete.js: Makefile mkws.js mkws-jquery.js ${HANDLEBARS_FILE} ${JQUERY_FILE} ${JQUERY_JSON_FILE} ${PP2_FILE}
-       ( set -e; \
-         echo "/*! Copyright (c) 2013-2014 IndexData ApS. http://indexdata.com"; \
-         echo "   Licence: GPL, http://www.indexdata.com/licences/gpl"; \
-         echo "   created at: $$(date)"; \
-         echo "   MKWS GIT id: $$(git show | head -n 1 | perl -npe 's,\S+\s+,,')"; \
-         echo "   pz2.js GIT id: $$(curl -sSf 'http://git.indexdata.com/?p=pazpar2.git;a=rss' | egrep '<guid' | head -1 | perl -ne 'print "$$1\n" if m,.*=([0-9a-f]+)</guid>,')"; \
-         echo "*/"; \
-         cat ${JQUERY_FILE}; \
-         cat ${JQUERY_JSON_FILE}; \
-         cat ${HANDLEBARS_FILE}; \
-         cat ${PP2_FILE}; \
-         cat  mkws.js; \
-         cat  mkws-jquery.js; \
-       ) > mkws-complete.js.new
-       mv -f mkws-complete.js.new mkws-complete.js
-
-%.min.js: %.js
-       yui-compressor $? > $@.new
-       mv -f $@.new $@
-
-mkws-syntax-check:
-       yui-compressor mkws.js >/dev/null
-
-${HANDLEBARS_FILE}:
-       curl -sSf ${HANDLEBARS_URL} -o $@.tmp
-       mv -f $@.tmp $@
-
-${JQUERY_FILE}:
-       curl -sSf ${JQUERY_URL} -o $@.new
-       perl -npe 's,sourceMappingURL=jquery.*map,,' $@.new > $@
-       rm -f $@.new
-
-${JQUERY_JSON_FILE}:
-       curl -sSf ${JQUERY_JSON_URL} -o $@.tmp
-       mv -f $@.tmp $@
-
-${PP2_FILE}:
-       curl -sSf "${PP2_URL}" -o $@.tmp
-       mv -f $@.tmp $@
-
-release: mkws.js mkws-complete.js mkws.min.js mkws-complete.min.js
-       @if [ -f releases/mkws-$(VERSION).js ]; then \
-               echo "*** There is already a release $(VERSION)"; \
-       else \
-               cp -p mkws.js releases/mkws-$(VERSION).js; \
-               cp -p mkws.min.js releases/mkws-$(VERSION).min.js; \
-               cp -p mkws-complete.js releases/mkws-complete-$(VERSION).js; \
-               cp -p mkws-complete.min.js releases/mkws-complete-$(VERSION).min.js; \
-               echo "Made release $(VERSION)"; \
-       fi
-
-mkws.js: $(COMPONENTS) Makefile
-       rm -f $@
-       cat $(COMPONENTS) > $@
-       chmod 444 $@
-
-mkws-templates.js:
-       handlebars -n mkws.defaultTemplates *.templates/*.handlebars -f mkws-templates.js
-
-distclean: clean
-       rm -f *.orig *.bak *.rej
-
-clean:
-       rm -f ${GENERATED}
-
index b3f25cf..ce71fda 100644 (file)
@@ -79,6 +79,12 @@ var mkws = {
   }
 };
 
+// We may be using a separate copy
+if (typeof(mkws_jQuery) !== "undefined") {
+  mkws.$ = mkws_jQuery;
+} else {
+  mkws.$ = jQuery;
+}
 
 mkws.log = function(string) {
   if (!mkws.log_level)
@@ -99,6 +105,24 @@ mkws.log = function(string) {
 };
 
 
+// Incredible that the standard JavaScript runtime doesn't define a
+// unique windowId. Instead, we have to make one up. And since there's
+// no global area shared between windows, the best we can do for
+// ensuring uniqueness is generating a random ID and crossing our
+// fingers. We stash this in window.name, as it's the only place to
+// keep data that is preserved across reloads and within-site
+// navigation. pz2.js picks this up and uses it as part of the
+// cookie-name, to ensure each tab gets its own session.
+if (window.name) {
+  mkws.log("Using existing window.name '" + window.name + "'");
+} else {
+  // Ten chars from 26 alpha-numerics = 36^10 = 3.65e15 combinations.
+  // At one per second, it will take 116 million years to duplicate a session
+  window.name = Math.random().toString(36).slice(2, 12);
+  mkws.log("Generated new window.name '" + window.name + "'");
+}
+
+
 // Translation function.
 mkws.M = function(word) {
   var lang = mkws.config.lang;
@@ -140,7 +164,7 @@ mkws.setMkwsConfig = function(overrides) {
 
   var config_default = {
     use_service_proxy: true,
-    pazpar2_url: "//mkws.indexdata.com/service-proxy/",
+    pazpar2_url:        "//mkws.indexdata.com/service-proxy/",
     service_proxy_auth: "//mkws.indexdata.com/service-proxy-auth",
     lang: "",
     sort_options: [["relevance"], ["title:1", "title"], ["date:0", "newest"], ["date:1", "oldest"]],
@@ -311,12 +335,13 @@ mkws.pagerNext = function(tname) {
 
     request.get(null, function(data) {
       if (!$.isXMLDoc(data)) {
-        alert("service proxy auth response document is not valid XML document, give up!");
+        alert("Service Proxy authentication response is not a valid XML document");
         return;
       }
       var status = $(data).find("status");
       if (status.text() != "OK") {
-        alert("service proxy auth response status: " + status.text() + ", give up!");
+        var message = $(data).find("message");
+        alert("Service Proxy authentication response: " + status.text() + " (" + message.text() + ")");
         return;
       }
 
@@ -487,4 +512,4 @@ mkws.pagerNext = function(tname) {
     var widgetSelector = selectorForAllWidgets();
     if (widgetSelector && $(widgetSelector).length !== 0) init();
   });
-})(jQuery);
+})(mkws.$);
index a8dc142..ce24206 100644 (file)
@@ -1,7 +1,7 @@
 // Handlebars helpers
 
 Handlebars.registerHelper('mkws-json', function(obj) {
-  return $.toJSON(obj);
+  return mkws.$.toJSON(obj);
 });
 
 
index 73bd406..4145652 100644 (file)
@@ -144,5 +144,6 @@ function _mkws_jquery_plugin($) {
 };
 
 
-// enable before page load, so we could call it before mkws() runs
-_mkws_jquery_plugin(jQuery);
+// XXX: enable before page load, so we could call it before mkws() runs
+_mkws_jquery_plugin(mkws.$);
+
index d209e43..52f2ee5 100644 (file)
@@ -386,7 +386,7 @@ function team($, teamName) {
   };
 
 
-  function loadTemplate(name) {
+  function loadTemplate(name, fallbackString) {
     var template = m_template[name];
     if (template === undefined && Handlebars.compile) {
       var source;
@@ -415,7 +415,7 @@ function team($, teamName) {
     else {
       alert("Missing MKWS template for " + name);
     }  
-    }
+  }
   that.loadTemplate = loadTemplate;
 
 
diff --git a/src/mkws-templates.js b/src/mkws-templates.js
new file mode 100644 (file)
index 0000000..86150aa
--- /dev/null
@@ -0,0 +1,388 @@
+(function() {
+  var template = Handlebars.template, templates = mkws.defaultTemplates = mkws.defaultTemplates || {};
+templates['Image'] = template(function (Handlebars,depth0,helpers,partials,data) {
+  this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+  var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this, helperMissing=helpers.helperMissing;
+
+function program1(depth0,data) {
+  
+  var buffer = "", stack1, helper, options;
+  buffer += "\n  <a href=\"#\" id=\"";
+  if (helper = helpers.detailLinkId) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.detailLinkId); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\" onclick=\"";
+  if (helper = helpers.detailClick) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.detailClick); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\">\n    ";
+  stack1 = (helper = helpers['mkws-first'] || (depth0 && depth0['mkws-first']),options={hash:{},inverse:self.noop,fn:self.programWithDepth(2, program2, data, depth0),data:data},helper ? helper.call(depth0, (depth0 && depth0['md-thumburl']), options) : helperMissing.call(depth0, "mkws-first", (depth0 && depth0['md-thumburl']), options));
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    <br/>\n  </a>\n";
+  return buffer;
+  }
+function program2(depth0,data,depth1) {
+  
+  var buffer = "", stack1;
+  buffer += "\n      <img src=\""
+    + escapeExpression((typeof depth0 === functionType ? depth0.apply(depth0) : depth0))
+    + "\" alt=\""
+    + escapeExpression(((stack1 = (depth1 && depth1['md-title'])),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
+    + "\"/>\n    ";
+  return buffer;
+  }
+
+  buffer += "\n";
+  stack1 = helpers.each.call(depth0, (depth0 && depth0.hits), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n";
+  return buffer;
+  });
+templates['Record'] = template(function (Handlebars,depth0,helpers,partials,data) {
+  this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+  var buffer = "", stack1, helper, options, functionType="function", escapeExpression=this.escapeExpression, helperMissing=helpers.helperMissing, self=this;
+
+function program1(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n        (";
+  if (helper = helpers['md-title-remainder']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title-remainder']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + ")\n      ";
+  return buffer;
+  }
+
+function program3(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n        <i>";
+  if (helper = helpers['md-title-responsibility']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title-responsibility']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</i>\n      ";
+  return buffer;
+  }
+
+function program5(depth0,data) {
+  
+  var buffer = "", stack1, helper, options;
+  buffer += "\n  <tr>\n    <th>"
+    + escapeExpression((helper = helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']),options={hash:{},data:data},helper ? helper.call(depth0, "Date", options) : helperMissing.call(depth0, "mkws-translate", "Date", options)))
+    + "</th>\n    <td>";
+  if (helper = helpers['md-date']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-date']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</td>\n  </tr>\n  ";
+  return buffer;
+  }
+
+function program7(depth0,data) {
+  
+  var buffer = "", stack1, helper, options;
+  buffer += "\n  <tr>\n    <th>"
+    + escapeExpression((helper = helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']),options={hash:{},data:data},helper ? helper.call(depth0, "Author", options) : helperMissing.call(depth0, "mkws-translate", "Author", options)))
+    + "</th>\n    <td>";
+  if (helper = helpers['md-author']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-author']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</td>\n  </tr>\n  ";
+  return buffer;
+  }
+
+function program9(depth0,data) {
+  
+  var buffer = "", stack1, helper, options;
+  buffer += "\n  <tr>\n    <th>"
+    + escapeExpression((helper = helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']),options={hash:{},data:data},helper ? helper.call(depth0, "Links", options) : helperMissing.call(depth0, "mkws-translate", "Links", options)))
+    + "</th>\n    <td>\n      ";
+  stack1 = helpers.each.call(depth0, (depth0 && depth0['md-electronic-url']), {hash:{},inverse:self.noop,fn:self.program(10, program10, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    </td>\n  </tr>\n  ";
+  return buffer;
+  }
+function program10(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n        <a href=\""
+    + escapeExpression((typeof depth0 === functionType ? depth0.apply(depth0) : depth0))
+    + "\">Link";
+  if (helper = helpers['mkws-index1']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['mkws-index1']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</a>\n      ";
+  return buffer;
+  }
+
+function program12(depth0,data) {
+  
+  var buffer = "", stack1, helper, options;
+  buffer += "\n  <tr>\n    <th>"
+    + escapeExpression((helper = helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']),options={hash:{},data:data},helper ? helper.call(depth0, "Subject", options) : helperMissing.call(depth0, "mkws-translate", "Subject", options)))
+    + "</th>\n    <td>\n      ";
+  stack1 = (helper = helpers['mkws-first'] || (depth0 && depth0['mkws-first']),options={hash:{
+    'having': ("md-subject")
+  },inverse:self.noop,fn:self.program(13, program13, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.location), options) : helperMissing.call(depth0, "mkws-first", (depth0 && depth0.location), options));
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    </td>\n  </tr>\n  ";
+  return buffer;
+  }
+function program13(depth0,data) {
+  
+  var buffer = "", stack1;
+  buffer += "\n        ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-subject']), {hash:{},inverse:self.noop,fn:self.program(14, program14, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n      ";
+  return buffer;
+  }
+function program14(depth0,data) {
+  
+  var buffer = "", stack1, helper, options;
+  buffer += "\n          ";
+  stack1 = (helper = helpers['mkws-commaList'] || (depth0 && depth0['mkws-commaList']),options={hash:{},inverse:self.noop,fn:self.program(15, program15, data),data:data},helper ? helper.call(depth0, (depth0 && depth0['md-subject']), options) : helperMissing.call(depth0, "mkws-commaList", (depth0 && depth0['md-subject']), options));
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n        ";
+  return buffer;
+  }
+function program15(depth0,data) {
+  
+  var buffer = "";
+  buffer += "\n            "
+    + escapeExpression((typeof depth0 === functionType ? depth0.apply(depth0) : depth0));
+  return buffer;
+  }
+
+function program17(depth0,data) {
+  
+  var buffer = "", helper, options;
+  buffer += "\n        "
+    + escapeExpression((helper = helpers['mkws-attr'] || (depth0 && depth0['mkws-attr']),options={hash:{},data:data},helper ? helper.call(depth0, "@name", options) : helperMissing.call(depth0, "mkws-attr", "@name", options)));
+  return buffer;
+  }
+
+  buffer += "\n<table>\n  <tr>\n    <th>"
+    + escapeExpression((helper = helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']),options={hash:{},data:data},helper ? helper.call(depth0, "Title", options) : helperMissing.call(depth0, "mkws-translate", "Title", options)))
+    + "</th>\n    <td>\n      ";
+  if (helper = helpers['md-title']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\n      ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-title-remainder']), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n      ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-title-responsibility']), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    </td>\n  </tr>\n  ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-date']), {hash:{},inverse:self.noop,fn:self.program(5, program5, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n  ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-author']), {hash:{},inverse:self.noop,fn:self.program(7, program7, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n  ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-electronic-url']), {hash:{},inverse:self.noop,fn:self.program(9, program9, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n  ";
+  stack1 = (helper = helpers['mkws-if-any'] || (depth0 && depth0['mkws-if-any']),options={hash:{
+    'having': ("md-subject")
+  },inverse:self.noop,fn:self.program(12, program12, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.location), options) : helperMissing.call(depth0, "mkws-if-any", (depth0 && depth0.location), options));
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n  <tr>\n    <th>"
+    + escapeExpression((helper = helpers['mkws-translate'] || (depth0 && depth0['mkws-translate']),options={hash:{},data:data},helper ? helper.call(depth0, "Locations", options) : helperMissing.call(depth0, "mkws-translate", "Locations", options)))
+    + "</th>\n    <td>\n      ";
+  stack1 = (helper = helpers['mkws-commaList'] || (depth0 && depth0['mkws-commaList']),options={hash:{},inverse:self.noop,fn:self.program(17, program17, data),data:data},helper ? helper.call(depth0, (depth0 && depth0.location), options) : helperMissing.call(depth0, "mkws-commaList", (depth0 && depth0.location), options));
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    </td>\n  </tr>\n</table>\n";
+  return buffer;
+  });
+templates['Records'] = template(function (Handlebars,depth0,helpers,partials,data) {
+  this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+  var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n  <div class=\"";
+  if (helper = helpers.containerClass) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.containerClass); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\">\n    <a href=\"#\" id=\"";
+  if (helper = helpers.detailLinkId) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.detailLinkId); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\" onclick=\"";
+  if (helper = helpers.detailClick) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.detailClick); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\">\n      <b>";
+  if (helper = helpers['md-title']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</b>\n    </a>\n    ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-title-remainder']), {hash:{},inverse:self.noop,fn:self.program(2, program2, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-title-responsibility']), {hash:{},inverse:self.noop,fn:self.program(4, program4, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    ";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0.renderedDetails), {hash:{},inverse:self.noop,fn:self.program(6, program6, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n  </div>\n";
+  return buffer;
+  }
+function program2(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n      <span>";
+  if (helper = helpers['md-title-remainder']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title-remainder']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</span>\n    ";
+  return buffer;
+  }
+
+function program4(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n      <span><i>";
+  if (helper = helpers['md-title-responsibility']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title-responsibility']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</i></span>\n    ";
+  return buffer;
+  }
+
+function program6(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n      ";
+  if (helper = helpers.renderedDetails) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.renderedDetails); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n    ";
+  return buffer;
+  }
+
+  buffer += "\n";
+  stack1 = helpers.each.call(depth0, (depth0 && depth0.hits), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n";
+  return buffer;
+  });
+templates['Reference'] = template(function (Handlebars,depth0,helpers,partials,data) {
+  this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+  var buffer = "", stack1, helper, options, functionType="function", escapeExpression=this.escapeExpression, self=this, helperMissing=helpers.helperMissing;
+
+function program1(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n<b>";
+  if (helper = helpers['md-title-remainder']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title-remainder']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</b>\n";
+  return buffer;
+  }
+
+function program3(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n<i>";
+  if (helper = helpers['md-title-responsibility']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title-responsibility']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</i>\n";
+  return buffer;
+  }
+
+  buffer += "\n<img src=\"";
+  if (helper = helpers['md-thumburl']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-thumburl']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\" alt=\"";
+  if (helper = helpers['md-title']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\">\n<h1><a href=\"";
+  if (helper = helpers['md-electronic-url']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-electronic-url']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\">";
+  if (helper = helpers['md-title']) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0['md-title']); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</a></h1>\n";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-title-remainder']), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n";
+  stack1 = helpers['if'].call(depth0, (depth0 && depth0['md-title-responsibility']), {hash:{},inverse:self.noop,fn:self.program(3, program3, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n";
+  stack1 = (helper = helpers['mkws-paragraphs'] || (depth0 && depth0['mkws-paragraphs']),options={hash:{},data:data},helper ? helper.call(depth0, (depth0 && depth0['md-description']), (depth0 && depth0.paragraphs), (depth0 && depth0.sentences), options) : helperMissing.call(depth0, "mkws-paragraphs", (depth0 && depth0['md-description']), (depth0 && depth0.paragraphs), (depth0 && depth0.sentences), options));
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n<p class=\"mkwsCredit\">Wikipedia</p>\n";
+  return buffer;
+  });
+templates['Facet'] = template(function (Handlebars,depth0,helpers,partials,data) {
+  this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+  var buffer = "", stack1, helper, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n  <div class=\"mkwsTerm\">\n    <a href=\"#\" ";
+  if (helper = helpers.linkdata) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.linkdata); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + ">";
+  if (helper = helpers.term) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.term); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</a> <span>";
+  if (helper = helpers.count) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.count); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</span>\n  </div>\n";
+  return buffer;
+  }
+
+  buffer += "\n\n<div class=\"mkwsFacetTitle\">";
+  if (helper = helpers.caption) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.caption); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "</div>\n";
+  stack1 = helpers.each.call(depth0, (depth0 && depth0.terms), {hash:{},inverse:self.noop,fn:self.program(1, program1, data),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n";
+  return buffer;
+  });
+templates['Termlists'] = template(function (Handlebars,depth0,helpers,partials,data) {
+  this.compilerInfo = [4,'>= 1.0.0'];
+helpers = this.merge(helpers, Handlebars.helpers); data = data || {};
+  var buffer = "", stack1, functionType="function", escapeExpression=this.escapeExpression, self=this;
+
+function program1(depth0,data,depth1) {
+  
+  var buffer = "", stack1, helper;
+  buffer += "\n  <div class=\"mkwsFacet mkwsTeam_"
+    + escapeExpression(((stack1 = (depth1 && depth1.team)),typeof stack1 === functionType ? stack1.apply(depth0) : stack1))
+    + "\" data-mkws-facet=\"";
+  if (helper = helpers.name) { stack1 = helper.call(depth0, {hash:{},data:data}); }
+  else { helper = (depth0 && depth0.name); stack1 = typeof helper === functionType ? helper.call(depth0, {hash:{},data:data}) : helper; }
+  buffer += escapeExpression(stack1)
+    + "\"></div>\n";
+  return buffer;
+  }
+
+  buffer += "\n\n<div class=\"mkwsTermlistsTitle\">Termlists</div>\n";
+  stack1 = helpers.each.call(depth0, (depth0 && depth0.facets), {hash:{},inverse:self.noop,fn:self.programWithDepth(1, program1, data, depth0),data:data});
+  if(stack1 || stack1 === 0) { buffer += stack1; }
+  buffer += "\n";
+  return buffer;
+  });
+})();
\ No newline at end of file
index 5106839..f5fb1f7 100644 (file)
@@ -429,7 +429,7 @@ mkws.registerWidgetType('Progress', function() {
         s += '</span>';
         s += '<span class="mkwsWaiting">';
       }
-      s += '&#x2588';
+      s += '&#x2588;';
     }
     s += '</span>';
     that.node.html(s);
diff --git a/src/releases/.gitkeep b/src/releases/.gitkeep
new file mode 100644 (file)
index 0000000..e69de29
index 1e39bc5..dd1c75a 100644 (file)
@@ -4,3 +4,4 @@ logs/jasmine-dev
 logs/mkws-jasmine-access.log
 logs/mkws-jasmine-error.log
 logs/mkws-jasmine-rewrite.log
+logs/mkws-jasmine.pid
index 9ee3b80..ce5c954 100644 (file)
@@ -3,9 +3,12 @@
 # For running on Mike's local install of node.js
 MIKE = PATH=$$PATH:/usr/local/lib/node-v0.10.24-linux-x64/bin
 
+APACHE_PORT:= $(shell echo $${APACHE_PORT-4040})
+
 #PHANTOMJS_URL=https://mkws-dev.indexdata.com/jasmine-popup.html       
-PHANTOMJS_URL=http://localhost:4040/jasmine-local-popup.html
-PHANTOMJS_TIMEOUT=16
+PHANTOMJS_PATH=jasmine-local-popup.html
+PHANTOMJS_URL=http://localhost:${APACHE_PORT}/${PHANTOMJS_PATH}
+PHANTOMJS_TIMEOUT=18
 
 NPM_INSTALL_FLAGS=-q
 JASMINE_NODE=  ./node_modules/jasmine-node/bin/jasmine-node
@@ -35,7 +38,7 @@ clean-tmp:
        touch ${TMP_DIR}/.gitkeep
 
 mkws-complete-syntax-check:
-       ${MAKE} -C../src mkws-complete.min.js
+       ${MAKE} -C../tools/htdocs mkws-complete.min.js
 
 check: mkws-complete-syntax-check
        @if [ ! -e node_modules ]; then echo "please run first: make node-modules"; exit 1; fi 
@@ -50,7 +53,7 @@ phantomjs p: apache-stop apache-start _phantomjs
        ${MAKE} apache-stop
 
 _phantomjs:
-       ./bin/bomb.pl --timeout="${PHANTOMJS_TIMEOUT}.5" ${PHANTOMJS} phantom/run-jasmine.js ${PHANTOMJS_URL} ${PHANTOMJS_TIMEOUT}
+       perl ./bin/bomb.pl --timeout="${PHANTOMJS_TIMEOUT}.5" ${PHANTOMJS} phantom/run-jasmine.js ${PHANTOMJS_URL} ${PHANTOMJS_TIMEOUT}
 
 mike-test:
        $(MAKE) _phantomjs PHANTOMJS_URL=http://x.example.indexdata.com/jasmine-popup.html
@@ -103,5 +106,10 @@ help:
        @echo "     [ node-modules ]"
        @echo "     [ apache-stop apache-start ]"
        @echo ""
-       @echo "DEBUG=1 make phantomjs PHANTOMJS_TIMEOUT=12 PHANTOMJS_URL=${PHANTOMJS_URL}"
+       @echo "Examples"
+       @echo ""
+       @echo "make phantomjs PHANTOMJS_PATH=${PHANTOMJS_PATH}"
+       @echo "DEBUG=0 APACHE_PORT=5050 make -s phantomjs PHANTOMJS_TIMEOUT=12 PHANTOMJS_PATH=${PHANTOMJS_PATH}"
+       @echo "DEBUG=2 make phantomjs PHANTOMJS_TIMEOUT=12 PHANTOMJS_URL=${PHANTOMJS_URL}"
+       @echo ""
 
index 4f37a41..bf14cbd 100755 (executable)
@@ -12,7 +12,7 @@ export APACHE_RUN_GROUP=$(groups | awk '{ print $1 }')
 export APACHE_LOG_DIR=$APACHE_SERVER_ROOT/logs
 export APACHE_PID_FILE=$APACHE_LOG_DIR/mkws-jasmine.pid
 export APACHE_LOCK_FILE=$APACHE_LOG_DIR/accept.lock
-export APACHE_PORT=4040
+: ${APACHE_PORT=4040}; export APACHE_PORT
 
 export MKWS_ROOT=$(pwd)/..
 
index 6211eab..a79be99 100755 (executable)
@@ -38,6 +38,17 @@ my @system = @ARGV;
 die usage if $help;
 die usage if !@system;
 
+# set CPU limit, in case the alarm handler will
+# be ignored
+eval {
+    require BSD::Resource;
+    BSD::Resource::setrlimit("RLIMIT_CPU", $timeout, 2*$timeout) or die "Cannot set CPU limit: $!\n";
+};
+if ($@) {
+    warn "Please install the package BSD::Resource!\n\n$@\n";
+}
+
+
 #
 # use fork/exec instead system()
 #
@@ -54,4 +65,3 @@ if ($pid) {
 else { }
 
 1;
-
diff --git a/test/etc/pazpar2/settings/mkws.xml b/test/etc/pazpar2/settings/mkws.xml
new file mode 100644 (file)
index 0000000..2a3a5c0
--- /dev/null
@@ -0,0 +1,40 @@
+<settings target="*">
+  <!-- based on edu.pz -->
+
+  <set target="acorn.library.vanderbilt.edu:2200/ACORN" name="pz:name" value="Vanderbilt U"/>
+  <set target="advance.biblio.polymtl.ca:210/ADVANCE" name="pz:name" value="Ecole Polytechnique Montreal"/>
+  <set target="albert.rit.edu:210/INNOPAC" name="pz:name" value="Rochester U of Tech"/>
+  <set target="atrium.bib.umontreal.ca:210/ADVANCE" name="pz:name" value="U of Montreal"/>
+  <set target="biblio.hec.ca:210/hec" name="pz:name" value="HEC Montreal"/>
+  <set target="books.luther.edu:210/innopac" name="pz:name" value="Luther College"/>
+  <set target="cat.cisti.nrc.ca:210/INNOPAC" name="pz:name" value="NRC Canada"/>
+  <set target="cat.lib.grin.edu:210/innopac" name="pz:name" value="Grinell College"/>
+  <set target="catalog.crl.edu:210/INNOPAC" name="pz:name" value="Center for Research Lib"/>
+  <set target="catalog.lib.jhu.edu:210/horizon" name="pz:name" value="Johns Hopkins U"/>
+  <set target="clavis.ucalgary.ca:2200/UNICORN" name="pz:name" value="U of Calgary"/>
+  <set target="cornelia.whoi.edu:7090/VOYAGER" name="pz:name" value="Woods Hole OI"/>
+  <set target="csulib.ctstateu.edu:210/INNOPAC" name="pz:name" value="CT State U"/>
+  <set target="info.library.mun.ca:2200/UNICORN" name="pz:name" value="Memorial U"/>
+  <set target="jasper.acadiau.ca:2200/UNICORN" name="pz:name" value="Acadia U"/>
+  <set target="ksclib.keene.edu:210/INNOPAC" name="pz:name" value="NH Keene State"/>
+
+  <!-- mapping for unqualified search -->
+  <set name="pz:cclmap:term" value="u=1016 t=l,r s=al"/>
+  
+  <!-- field-specific mappings -->
+  <set name="pz:cclmap:au" value="u=1 s=al"/>
+  <set name="pz:cclmap:ti" value="u=4 s=al"/>
+  <set name="pz:cclmap:su" value="u=21 s=al"/>
+  <set name="pz:cclmap:isbn" value="u=7"/>
+  <set name="pz:cclmap:issn" value="u=8"/>
+  <set name="pz:cclmap:date" value="u=30 r=r"/>
+
+  <!-- Retrieval settings -->
+  <set name="pz:requestsyntax" value="marc21"/>
+  <set name="pz:elements" value="F"/>
+
+  <!-- Result normalization settings -->
+  <set name="pz:nativesyntax" value="iso2709"/>
+  <set name="pz:xslt" value="marc21.xsl"/>
+</settings>
+
index 3f69e77..1416efa 100644 (file)
@@ -14,6 +14,7 @@
         "xmlhttprequest": "*",
         "jsdom": "*",
         "request": "*",
+        "jasmine-reporters": "<= 0.4.1",
         "jasmine-node": "*",
         "phantomjs": "*"
     },
index 27c465b..396417b 100644 (file)
@@ -108,14 +108,14 @@ page.open(url, function (status) {
 
     var exit = wait_for_jasmine(function () {
         return page.evaluate(function () {
-            if (!window || !window.$ || !window.mkws) {
+            if (!window || !window.mkws || !window.mkws.$) {
                 console.log("No window object found");
                 return false;
             }
 
-            var $ = window.$;
+            var $ = window.mkws.$;
             var error_msg = [""];
-            var passing = $(".passingAlert").text() || window.$(".failingAlert").text();
+            var passing = $(".passingAlert").text() || $(".failingAlert").text();
 
             // extract failed tests
             var list = $('.results > #details > .specDetail.failed');
index 466d9de..e849dbf 100644 (file)
@@ -37,6 +37,10 @@ function init_jasmine_config() {
         show_record_url: true,
         // check for valid URL in records
         check_motd: true,
+
+        // check sort by and per page menu
+        check_sortby: false,
+
         dummy: false
     };
 
@@ -241,6 +245,8 @@ describe("Check Termlist", function () {
     });
 });
 
+
+
 describe("Check Author Facets", function () {
     it("limit search to first author", function () {
         if (mkws.config.disable_facet_authors_search) {
@@ -275,6 +281,7 @@ describe("Check Author Facets", function () {
 
         waitsFor(function () {
             var hits_single_target = get_hit_counter();
+            // debug("hits_single_target='" + hits_single_target + "' cf. hits_all_targets='" + hits_all_targets + "'");
             return hits_single_target > 0 && hits_single_target < hits_all_targets ? true : false;
         }, "Limited author search for less than " + hits_all_targets + " hits", 4.5 * jasmine_config.second);
 
@@ -459,11 +466,11 @@ describe("Check switch menu Records/Targets", function () {
         expect(mkws.$("div.mkwsSwitch").length).toBe(1);
 
         // expect 2 clickable links
-        expect(mkws.$("div.mkwsSwitch a").length).toBe(2);
+        expect(mkws.$("div.mkwsSwitch > a").length).toBe(2);
     });
 
     it("switch to target view", function () {
-        mkws.$("div.mkwsSwitch").children('a').eq(1).trigger("click");
+        mkws.$("div.mkwsSwitch > a").eq(1).trigger("click");
 
         // now the target table must be visible
         expect(mkws.$("div.mkwsTargets").is(":visible")).toBe(true);
@@ -482,7 +489,7 @@ describe("Check switch menu Records/Targets", function () {
     });
 
     it("switch back to record view", function () {
-        mkws.$("div.mkwsSwitch").children('a').eq(0).trigger("click");
+        mkws.$("div.mkwsSwitch > a").eq(0).trigger("click");
 
         // now the target table must be visible
         expect(mkws.$("div.mkwsTargets").is(":visible")).toBe(false);
@@ -521,6 +528,173 @@ describe("Check status client counter", function () {
     });
 });
 
+/* remove the "source" and "author" facet link to get more records again */
+describe("Check removable facets links", function () {
+    var $ = mkws.$;
+
+    it("remove links for source and author", function () {
+        var waitcount = 0;
+        if (!jasmine_config.check_sortby) {
+            debug("ignore check for removable facets");
+            return;
+        }
+
+
+        runs(function () {
+            var click = $("a.mkwsRemovable").eq(0).trigger("click");
+            debug("Removed first facets link: " + click.length);
+            expect(click.length).toBe(1);
+        });
+
+        runs(function () {
+            $(".mkwsPager").bind("DOMNodeInserted DOMNodeRemoved propertychange", function () {
+                waitcount++;
+                debug("DOM change for removeable: " + waitcount);
+            });
+        });
+
+        waitsFor(function () {
+            return $("a.mkwsRemovable").length == 1 ? 1 : 0;
+        });
+
+        runs(function () {
+            var click = $("a.mkwsRemovable").eq(0).trigger("click");
+            debug("Removed second facets link: " + click.length);
+            expect(click.length).toBe(1);
+        });
+
+        waitsFor(function () {
+            // debug("wait for: " + waitcount);
+            return waitcount >= 2 ? true : false;
+        }, "Records DOM change, by per page", 2 * jasmine_config.second);
+
+
+        runs(function () {
+            debug("unbind removable");
+            $(".mkwsPager").unbind("DOMNodeInserted DOMNodeRemoved propertychange");
+        });
+    });
+});
+
+
+describe("Check per page options", function () {
+    var $ = mkws.$;
+
+    it("show per page", function () {
+        if (!jasmine_config.check_sortby) {
+            debug("ignore check for per page select");
+            return;
+        }
+        var waitcount = 0;
+        var per_page_number = 20;
+
+
+        runs(function () {
+            var select = $("select.mkwsPerpage option[selected='selected']");
+            debug("per page default is: " + select.text() + " and unselect it");
+            select.removeAttr('selected');
+
+            select = $("select.mkwsPerpage option[value='" + per_page_number + "']").attr('selected', true);
+            debug("per page is set to: " + select.text());
+            select.trigger("change");
+
+            $("div.mkwsRecords").bind("DOMNodeInserted DOMNodeRemoved propertychange", function () {
+                waitcount++;
+                debug("DOM wait for change, per page: " + waitcount);
+            });
+        });
+
+        waitsFor(function () {
+            //debug("wait for: " + waitcount);
+            return waitcount >= 30 ? true : false;
+        }, "Records DOM change, by per page", 3 * jasmine_config.second);
+
+        runs(function () {
+            $("div.mkwsRecords").unbind("DOMNodeInserted DOMNodeRemoved propertychange");
+            debug("unbind per page");
+        });
+
+        runs(function () {
+            var records = $("div.mkwsRecords > div.mkwsSummary");
+            debug("Got now " + records.length + " records");
+            expect(records.length).toBe(per_page_number);
+        });
+    });
+});
+
+describe("Check SortBy options", function () {
+    var $ = mkws.$;
+
+    it("show per page", function () {
+        if (!jasmine_config.check_sortby) {
+            debug("ignore check for sort by");
+            return;
+        }
+
+        var waitcount = 0;
+        var sort_value = 'title:1';
+        var per_page_number = 20;
+        var title_list_old = title_list("xxx ");
+
+        function title_list(prefix) {
+            var list = [];
+            var terms = $("div.mkwsRecords > div.mkwsSummary > a");
+            for (var i = 0; i < terms.length; i++) {
+                var term = $(terms[i]).text();
+                list.push(term);
+                // debug(prefix + "title: " + term);
+            }
+            return list;
+        }
+
+        runs(function () {
+            $("div.mkwsRecords").bind("DOMNodeInserted DOMNodeRemoved propertychange", function () {
+                waitcount++;
+                //debug("DOM wait for change, sort by: " + waitcount);
+            });
+
+            var select = $("select.mkwsSort option[selected='selected']");
+            debug("Sort by default is: " + select.text() + " and unselect it");
+            select.removeAttr('selected');
+
+            select = $("select.mkwsSort option[value='" + sort_value + "']").attr('selected', true);
+            debug("sort by is set to: " + select.text());
+            select.trigger("change");
+        });
+
+        waitsFor(function () {
+            //debug("wait for2: " + waitcount);
+            return waitcount >= 6 ? true : false;
+        }, "Records DOM change, by sort page", 3 * jasmine_config.second);
+
+        runs(function () {
+            $("div.mkwsRecords").unbind("DOMNodeInserted DOMNodeRemoved propertychange");
+            debug("unbind per page");
+        });
+
+        runs(function () {
+            var records = $("div.mkwsRecords > div.mkwsSummary a");
+            debug("Got now " + records.length + " records");
+            expect(records.length).toBe(per_page_number);
+        });
+
+        runs(function () {
+            var title_list_new = title_list("yyy ");
+            var diff_flag = 0;
+            for (var i = 0; i < title_list_old.length; i++) {
+                debug(title_list_old[i] + " :: " + title_list_new[i]);
+
+                if (title_list_old[i] != title_list_new[i]) {
+                    diff_flag++;
+                }
+            }
+            debug("Title changes: " + diff_flag + " out of " + per_page_number);
+            expect(diff_flag).not.toBe(0);
+        });
+    });
+});
+
+
 /* done */
 describe("All tests are done", function () {
     it(">>> hooray <<<", function () {
diff --git a/tools/apache2/DELETED/mkws-mike b/tools/apache2/DELETED/mkws-mike
deleted file mode 100644 (file)
index 7808b1f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-# Configuration for the apache web server
-# Running on Mike's local development box
-
-<VirtualHost *:80>
-    ServerName x.mkws.indexdata.com
-    ErrorLog /var/log/apache2/mkws-error.log
-    CustomLog /var/log/apache2/mkws-access.log combined
-
-    DocumentRoot /usr/local/src/git/mkws/tools/htdocs
-    Alias /libjs-pz2/ /usr/local/src/git/libjs-pz2/
-    Alias /pazpar2/ /usr/local/src/git/pazpar2/
-
-    RewriteEngine on
-    RewriteLogLevel 1
-    RewriteLog /var/log/apache2/spclient-rewrite.log 
-    RewriteRule /service-proxy-auth /service-proxy/?command=auth&action=login&username=guest&password=guest [P] # [NE,P]
-
-    # For MKC Service Proxy
-    ProxyPass        /service-proxy/ http://mkc.indexdata.com:9009/service-proxy/
-    ProxyPassReverse /service-proxy/ http://mkc.indexdata.com:9009/service-proxy/
-
-    # For local Service Proxy running as mvn jetty:run
-    #ProxyPass        /service-proxy/ http://localhost:8585/service-proxy/
-    #ProxyPassReverse /service-proxy/ http://localhost:8585/service-proxy/
-
-    # Needed on Mac, which locks Apache down hard by default.
-    <Directory />
-        Allow from all
-    </Directory>
-</VirtualHost>
index 282b476..0630d14 100644 (file)
@@ -20,6 +20,7 @@
     RewriteRule /service-proxy-testauth(/)?(;jsessionid=.+)?   /service-proxy/$2?command=auth&action=check,login&username=mkwstest&password=mkwstest [P]
     RewriteRule /service-proxy-credoauth(/)?(;jsessionid=.+)?          /service-proxy/$2?command=auth&action=check,login&username=credo&password=emu [P]
     RewriteRule /service-proxy-kohaauth(/)?(;jsessionid=.+)?   /service-proxy/$2?command=auth&action=check,login&username=kohademo&password=kohademo [P]
+    RewriteRule /service-proxy-orexauth(/)?(;jsessionid=.+)?   /service-proxy/$2?command=auth&action=check,login&username=orex&password=orexmkc [P]
 
     # The following rule allows the server to accept service-proxy
     # requests that begin with an escaped "%3F" rather than a literal
@@ -32,6 +33,7 @@
     # For MKC Service Proxy
     ProxyPass        /service-proxy/ http://sp-mkc.indexdata.com/service-proxy/
     ProxyPassReverse /service-proxy/ http://sp-mkc.indexdata.com/service-proxy/
+    # Or use: sp-mkc-sansa.indexdata.com:8080 or sp-mkc-arya.indexdata.com:8080
 
     PerlOptions +Parent
     PerlSwitches -I/home/indexdata/mkws/tools/mod_perl
index ef2b431..304b5a8 100644 (file)
@@ -1,6 +1,7 @@
 <VirtualHost *:80>
     ServerName mkws.indexdata.com
     ServerAlias mkws-git.indexdata.com
+    ServerAlias sp-mkws.indexdata.com
 
     ProxyRequests off
     ProxyVia On
index d160cc1..3fa37d0 100644 (file)
@@ -1,8 +1,9 @@
 # A very simple configuration to proxy the irspy
 
 <VirtualHost *:443>
-    ServerName mkws.indexdata.com 
-    ServerAlias mkws-dev.indexdata.com
+    ServerName mkws.indexdata.com
+    ServerAlias mkws-git.indexdata.com
+    ServerAlias sp-mkws.indexdata.com
 
   <IfModule mod_ssl.c>
     SSLEngine on
@@ -35,9 +36,9 @@
     # Didn't help
     # Disable gzipping
     # RequestHeader unset Accept-Encoding
-    # Didn't help 
+    # Didn't help
     # ProxyReceiveBufferSize 8192
-    # Didn't help 
+    # Didn't help
     SetEnv force-proxy-request-1.0 1
     SetEnv proxy-nokeepalive 1
 
index f71972a..54813dd 100644 (file)
     RewriteRule /service-proxy-auth(/)?(;jsessionid=.+)?  /service-proxy/$2?command=auth&action=login&username=mkws&password=mkws [P]
 
     # jasmine test account
-    RewriteRule /service-proxy-testauth(/)?(;jsessionid=.+)?  /service-proxy/$2?command=auth&action=login&username=mkwstest&password=mkwstest [P]
-
-    ProxyPass        /service-proxy/ http://sp-mkc.indexdata.com/service-proxy/
-    ProxyPassReverse /service-proxy/ http://sp-mkc.indexdata.com/service-proxy/
+    RewriteRule /service-proxy-testauth(/)?(;jsessionid=.+)?  /service-proxy/$2?command=auth&action=login&username=mkwsdev&password=mkwsdev [P]
+    RewriteRule /service-proxy-kohaauth(/)?(;jsessionid=.+)?  /service-proxy/$2?command=auth&action=login&username=kohademo&password=kohademo [P]
+    #RewriteRule /service-proxy-kohaauth(/)?(;jsessionid=.+)?  /service-proxy/$2?command=auth&action=login&username=mkwsdev&password=mkwsdev [P]
+
+    ProxyPass        /service-proxy/ http://sp-mkc-sansa.indexdata.com:8080/service-proxy/
+    ProxyPassReverse /service-proxy/ http://sp-mkc-sansa.indexdata.com:8080/service-proxy/
+    #ProxyPass        /service-proxy/ http://sp-mkc-arya.indexdata.com:8080/service-proxy/
+    #ProxyPassReverse /service-proxy/ http://sp-mkc-arya.indexdata.com:8080/service-proxy/
+    #ProxyPass        /service-proxy/ http://sp-mkc.indexdata.com/service-proxy/
+    #ProxyPassReverse /service-proxy/ http://sp-mkc.indexdata.com/service-proxy/
 
     ProxyPass        /pazpar2/         http://localhost:8004/pazpar2/
     ProxyPassReverse /pazpar2/         http://localhost:8004/pazpar2/
diff --git a/tools/apache2/sp-mkws-live b/tools/apache2/sp-mkws-live
new file mode 100644 (file)
index 0000000..bcd9e81
--- /dev/null
@@ -0,0 +1,34 @@
+# Separate hostname for the MKWS instance of the Service Proxy.
+# This allows us to use a different SP configuration that handles
+# authentication differently.
+#
+# Once this is in universal use, we can get rid of the Service Proxy
+# stuff in the configuration for mkws.indexdata.com
+#
+<VirtualHost *:80>
+    ServerName sp-mkws.indexdata.com
+
+    ErrorLog /var/log/apache2/sp-mkws-error.log
+    CustomLog /var/log/apache2/sp-mkws-access.log combined
+
+    DocumentRoot /home/indexdata/mkws/tools/sp-htdocs
+
+    # No rewriting on this server: use the true URL.
+    # (The security value of rewrite is when the application site does it.)
+
+    Header set Access-Control-Allow-Credentials true
+
+    ProxyPreserveHost On
+    # We could use any of the following:
+    #  sp-mkc.indexdata.com/service-proxy/ -- multiplexer
+    #  sp-mkc-sansa.indexdata.com:8080/service-proxy/ -- 1st back-end server
+    #  sp-mkc-arya.indexdata.com:8080/service-proxy/ -- 2nd back-end server
+    ProxyPass        /service-proxy/ http://sp-mkc-sansa.indexdata.com:8080/service-proxy/
+    ProxyPassReverse /service-proxy/ http://sp-mkc-sansa.indexdata.com:8080/service-proxy/
+
+    PerlOptions +Parent
+    PerlSwitches -I/home/indexdata/mkws/tools/mod_perl
+    <Location /service-proxy>
+        PerlOutputFilterHandler MyApache2::SetACAO
+    </Location>
+</VirtualHost>
diff --git a/tools/htdocs/Makefile b/tools/htdocs/Makefile
new file mode 100644 (file)
index 0000000..02d64f8
--- /dev/null
@@ -0,0 +1,102 @@
+# Copyright (c) 2013-2014 IndexData ApS. http://indexdata.com
+
+SRC=   ../../src
+
+HANDLEBARS_FILE = handlebars-v1.1.2.js
+JQUERY_FILE = jquery-1.10.0.min.js
+JQUERY_JSON_FILE = jquery.json-2.4.js
+PP2_FILE = pz2.js
+
+HANDLEBARS_URL = http://builds.handlebarsjs.com.s3.amazonaws.com/${HANDLEBARS_FILE}
+JQUERY_URL = http://code.jquery.com/${JQUERY_FILE}
+JQUERY_JSON_URL = https://jquery-json.googlecode.com/files/${JQUERY_JSON_FILE}
+PP2_URL = http://git.indexdata.com/?p=pazpar2.git;a=blob_plain;f=js/${PP2_FILE};hb=HEAD
+
+JQUERY_UI_URL =        http://code.jquery.com/ui/1.10.3/jquery-ui.js
+VERSION = $(shell tr -d '\012' < ${SRC}/VERSION)
+
+COMPONENTS = ${SRC}/mkws-handlebars.js \
+       ${SRC}/mkws-core.js ${SRC}/mkws-team.js ${SRC}/mkws-filter.js \
+       ${SRC}/mkws-templates.js \
+       ${SRC}/mkws-widget.js ${SRC}/mkws-widget-main.js ${SRC}/mkws-widget-termlists.js \
+       ${SRC}/mkws-widget-authname.js ${SRC}/mkws-widget-categories.js ${SRC}/mkws-widget-log.js \
+       ${SRC}/mkws-widget-record.js ${SRC}/mkws-widget-reference.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-templates.js
+
+**make-default**: all
+
+all: mkws.min.js mkws-complete.min.js
+
+mkws-js mkws-complete.js: Makefile mkws.js ${SRC}/mkws-jquery.js ${HANDLEBARS_FILE} ${JQUERY_FILE} ${JQUERY_JSON_FILE} ${PP2_FILE}
+       ( set -e; \
+         echo "/*! Copyright (c) 2013-2014 IndexData ApS. http://indexdata.com"; \
+         echo "   Licence: GPL, http://www.indexdata.com/licences/gpl"; \
+         echo "   created at: $$(date)"; \
+         echo "   MKWS GIT id: $$(git show | head -n 1 | perl -npe 's,\S+\s+,,')"; \
+         echo "   pz2.js GIT id: $$(curl -sSf 'http://git.indexdata.com/?p=pazpar2.git;a=rss' | egrep '<guid' | head -1 | perl -ne 'print "$$1\n" if m,.*=([0-9a-f]+)</guid>,')"; \
+         echo "*/"; \
+         cat ${JQUERY_FILE}; \
+         cat ${JQUERY_JSON_FILE}; \
+         echo 'mkws_jQuery = jQuery.noConflict(true);'; \
+         cat ${HANDLEBARS_FILE}; \
+         cat ${PP2_FILE}; \
+         cat  mkws.js; \
+         cat  ${SRC}/mkws-jquery.js; \
+       ) > mkws-complete.js.tmp
+       mv -f mkws-complete.js.tmp mkws-complete.js
+
+%.min.js: %.js
+       yui-compressor $? > $@.tmp
+       mv -f $@.tmp $@
+
+mkws-syntax-check:
+       yui-compressor mkws.js >/dev/null
+
+${HANDLEBARS_FILE}:
+       curl -sSf ${HANDLEBARS_URL} -o $@.tmp
+       mv -f $@.tmp $@
+
+${JQUERY_FILE}:
+       curl -sSf ${JQUERY_URL} -o $@.tmp
+       perl -npe 's,sourceMappingURL=jquery.*map,,' $@.tmp > $@
+       rm -f $@.tmp
+
+${JQUERY_JSON_FILE}:
+       curl -sSf ${JQUERY_JSON_URL} -o $@.tmp
+       mv -f $@.tmp $@
+
+${PP2_FILE}:
+       curl -sSf "${PP2_URL}" -o $@.tmp
+       mv -f $@.tmp $@
+
+release: mkws.js mkws-complete.js mkws.min.js mkws-complete.min.js
+       @if [ -f ${SRC}/releases/mkws-$(VERSION).js ]; then \
+               echo "*** There is already a release $(VERSION)"; \
+       else \
+               cp -p mkws.js ${SRC}/releases/mkws-$(VERSION).js; \
+               cp -p mkws.min.js ${SRC}/releases/mkws-$(VERSION).min.js; \
+               cp -p mkws-complete.js ${SRC}/releases/mkws-complete-$(VERSION).js; \
+               cp -p mkws-complete.min.js ${SRC}/releases/mkws-complete-$(VERSION).min.js; \
+               echo "Made release $(VERSION)"; \
+       fi
+
+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
+
+distclean: clean
+       @echo "(No need for distclean, 'make clean' is fine)"
+
+clean:
+       rm -f ${GENERATED}
+
+help:
+       @echo "make [ all | release | clean | distclean ]"
+       @echo "     [ mkws-syntax-check ]"
index 7bfbbed..560545b 100644 (file)
@@ -1,5 +1,9 @@
+# Copyright (c) 2013-2014 IndexData ApS. http://indexdata.com
+#
+# this makefile will generate CSS files for Koha MKWS widgets
+
 E_DIR= ../../../../examples/htdocs
-COMPONENTS = ${E_DIR}/mkws-widget-reference.css
+COMPONENTS = ${E_DIR}/mkws-widget-reference.css koha-mkws-widget-ru.css
 
 KOHA_CSS = koha-mkws.css 
 KOHA_COMPLETE_CSS = koha-mkws-complete.css 
@@ -9,10 +13,13 @@ all: ${KOHA_COMPLETE_CSS}
 clean:
        rm -f ${KOHA_COMPLETE_CSS}
 
-${KOHA_COMPLETE_CSS}:
+${KOHA_COMPLETE_CSS}: ${COMPONENTS} ${KOHA_CSS}
        cat ${COMPONENTS} ${KOHA_CSS} > $@.tmp
        mv -f $@.tmp $@
 
 distclean: clean
        rm -f *.orig *.bak *.rej
 
+help:
+       @echo "make [ all | clean ]"
+
index 7daa169..d52efb3 100644 (file)
     content: "Source: ";
     font-weight: bold;
 }
+/* original "ru" config
+.mkwsReferenceUniverse {
+  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+  background: #FCFBFA; 
+  padding: 0.5em 1em 0.25em;
+  box-shadow: 0 0 2px 0 #7F8F93;
+  border-radius: 0 0 1.5em;
+  -moz-border-radius: 0 0 1.5em;
+  -webkit-border-radius: 0 0 1.5em;
+  line-height: 1.4;
+  color: #86979B;
+  background: radial-gradient(ellipse at center,  #ffffff 0%,#f8f8f8 100%);
+}
+*/
+
+.mkwsReferenceUniverse {
+  padding: 0.5em 1em 0.25em;
+}
+
+.mkwsReferenceUniverse h2 {
+  font-size: 100%;
+  color: #4A5456;
+  padding-bottom: .5em;
+}
+
+.mkwsReferenceUniverse ul {
+  margin: 0;
+  padding: 0;
+}
+
+.mkwsReferenceUniverse li {
+  margin: .95em .25em;
+  padding-top: .75em;
+  border-top: 1px dotted #BEC8CC;
+  font-size: 90%;
+  list-style: none;
+}
+
+.mkwsReferenceUniverse a {
+  text-decoration: none;
+  font-weight:bold;
+  color: #2B77AF;
+}
 /*! Copyright (c) 2013-2014 IndexData ApS. http://indexdata.com
  * Licence: GPL, http://www.indexdata.com/licences/gpl
  *
diff --git a/tools/sp-htdocs/index.html b/tools/sp-htdocs/index.html
new file mode 100644 (file)
index 0000000..6328a36
--- /dev/null
@@ -0,0 +1,7 @@
+<p>
+  There is no content on this web server.
+</p>
+<p>
+  It provides a service on
+  <a href="/service-proxy/">/service-proxy/</a>.
+</p>