diff options
-rw-r--r-- | org.eclipse.help.webapp/build.properties | 3 | ||||
-rw-r--r-- | org.eclipse.help.webapp/index.jsp | 33 | ||||
-rw-r--r-- | org.eclipse.help.webapp/m/README.md | 170 | ||||
-rw-r--r-- | org.eclipse.help.webapp/m/index.css | 668 | ||||
-rw-r--r-- | org.eclipse.help.webapp/m/index.html | 19 | ||||
-rw-r--r-- | org.eclipse.help.webapp/m/index.js | 2845 | ||||
-rw-r--r-- | org.eclipse.help.webapp/plugin.xml | 5 |
7 files changed, 3740 insertions, 3 deletions
diff --git a/org.eclipse.help.webapp/build.properties b/org.eclipse.help.webapp/build.properties index 2c55ef6b3..a61948a72 100644 --- a/org.eclipse.help.webapp/build.properties +++ b/org.eclipse.help.webapp/build.properties @@ -24,7 +24,8 @@ bin.includes = plugin.xml,\ index.jsp,\ advanced/,\ .options,\ - advancedstate/ + advancedstate/,\ + m/ output.. = bin/ javacWarnings..=-unavoidableGenericProblems diff --git a/org.eclipse.help.webapp/index.jsp b/org.eclipse.help.webapp/index.jsp index 6d6f47443..117fc46e0 100644 --- a/org.eclipse.help.webapp/index.jsp +++ b/org.eclipse.help.webapp/index.jsp @@ -12,6 +12,8 @@ IBM Corporation - initial API and implementation --%> <%@ page import="org.eclipse.help.internal.webapp.data.*" errorPage="/advanced/err.jsp" contentType="text/html; charset=UTF-8"%> +<%@ page import="java.util.Scanner" %> +<%@ page import="java.net.URL" %> <% request.setCharacterEncoding("UTF-8"); ServerState.webappStarted(application,request, response); @@ -33,7 +35,34 @@ </body> </html> <% - }else { - request.getRequestDispatcher("/advanced/index.jsp" + data.getQuery()).forward(request, response); + } else { + + // Experimental UI: see bug 501718 + String experimentalUi = System.getProperty("org.eclipse.help.webapp.experimental.ui"); + if (request.getParameter("legacy") == null && experimentalUi != null) { + try { + // In a JSP forwarding to non JSP resources does not work + // (page is shown, but "java.lang.IllegalStateException: STREAM" is thrown) + // so read from URL instead: + URL baseUrl = new URL(request.getRequestURL().toString()); + URL forwardUrl = new URL(baseUrl, experimentalUi); + // Same-origin policy + if (!baseUrl.getProtocol().equals(forwardUrl.getProtocol()) + || !baseUrl.getHost().equals(forwardUrl.getHost()) + || baseUrl.getPort() != forwardUrl.getPort()) throw new Exception(); + // Read it as InputStream and convert it to a String + // (by using a Scanner with a delimiter that cannot be found: \A - start of input) + Scanner scanAll = new Scanner(forwardUrl.openStream()).useDelimiter("\\A"); + response.getWriter().write(scanAll.hasNext() ? scanAll.next() : ""); + } catch (Exception e) { + // Experimental UI resource not found, so fall back to legacy UI + request.getRequestDispatcher("/advanced/index.jsp" + data.getQuery()).forward(request, response); + } + + // legacy UI + } else { + request.getRequestDispatcher("/advanced/index.jsp" + data.getQuery()).forward(request, response); + } + } %> diff --git a/org.eclipse.help.webapp/m/README.md b/org.eclipse.help.webapp/m/README.md new file mode 100644 index 000000000..5f90459a8 --- /dev/null +++ b/org.eclipse.help.webapp/m/README.md @@ -0,0 +1,170 @@ +# Modernized help UI + +This prototype is based on HTML 5 with an `iframe` for the content and, unlike +the legacy UI, does not use HTML 4 `frameset`s. Technically, it sits on top of +the legacy UI. For example, when doing a full search, the HTML page of the +legacy UI will be parsed and rendered in the web browser (widget) using +JavaScript. This has disadvantages, but it has made it easier to develop this +prototype as a plugin without the need to patch Eclipse. + +Once the new UI no longer requires anything from the legacy UI and is no longer +lagging behind in terms of internationalization and accessibility, the legacy UI +can be deprecated and removed: see [_Further development_](#further-development) +below. + +Improvements compared to the legacy UI: +* Responsive, e.g. by automatically hiding the TOC bar on small screens +* Search as you type and auto completion of search terms +* Topic preview when hovering over instant search results +* Search scopes drop-down in the search field to search in all books, in the + current book, in the current chapter or in a user-defined scope +* Full search results shown as a page instead of as side bar +* Full search results can be filtered by books +* Infocenter: UI designed aware of mobile devices +* Infocenter: URL contains topic or search, so a deep link can be bookmarked or + shared in the usual way + + +## Activation + +This prototype can be activated with the following Java property: + + -Dorg.eclipse.help.webapp.experimental.ui=m/index.html + +To test, modify and/or customize this prototype, the files in this folder can +be put in a separate plugin (in `index.html` the links to the JavaScript and +CSS file have to be changed from `m/...` to something like +`rtopic/com.example.my_plugin/...`) and activated as follows (assuming +the plugin has the symbolic name `com.example.my_plugin`; using the raw +topic help link `rtopic/<plugin>/<optional-path>/<file>`): + + -Dorg.eclipse.help.webapp.experimental.ui=rtopic/com.example.my_plugin/index.html + + +## Further development + +Unsorted list of things to improve and where the legacy UI is currently used: +* Create the page dynamically to avoid a callback to determine the mode, whether +Infocenter or Eclipse embedded help (the latter with previous/next navigation +buttons and bookmark support) and to determine the initial content page. + * [`/advanced/tabs.jsp`](http://127.0.0.1:49999/help/advanced/tabs.jsp) + * [`/advanced/content.jsp`](http://127.0.0.1:49999/help/advanced/content.jsp) +* Return search results directly as HTML page instead of parsing and re-rendering legacy HTML page + * [`/advanced/searchView.jsp`](http://127.0.0.1:49999/help/advanced/searchView.jsp?showSearchCategories=false&searchWord=test&maxHits=500) +* Print chapter + * [`/advanced/print.jsp`](127.0.0.1:49999/help/advanced/print.jsp?topic=/../nav/0) +* Rest API to get the following as JSON instead of requesting and parsing the legacy UI + * Search as you type and search term completion: currently, when typing `fo`, a search is executed for `fo*` (which means starts with `fo`), but this disables stemming like in the full search (which means when entering `logging`, pages containing `log` or `logs` are not found; ideally _starts with_ and stemming should be combined; the search term completion proposals are currently computed from the words contained in the results, for which there are better ways to do this on the server side + * [`/advanced/searchView.jsp`](http://127.0.0.1:49999/help/advanced/searchView.jsp?showSearchCategories=false&searchWord=test*&maxHits=7) + * User-defined search scopes + * [`/advanced/workingSetManager.jsp`](http://127.0.0.1:49999/help/advanced/workingSetManager.jsp) - list of scopes + * [`/advanced/workingSetState.jsp`](http://127.0.0.1:49999/help/advanced/workingSetState.jsp?operation=add&workingSet=example_scope) - add, edit or remove a scope + * [`/scopeState.jsp`](http://127.0.0.1:49999/scopeState.jsp?workingSet=) - set or unset scope + * Bookmarks + * [`/advanced/bookmarksView.jsp`](127.0.0.1:49999/help/advanced/print.jsp?topic=/../nav/0) + * Storing UI settings: TOC side bar width and show/hidden, search results filter tree expanded or collapsed, etc. +* Things to improve (where this prototype currently falls behind the legacy UI): + * Internationalization (by externalize Strings) + * Right-to-left (RTL) support + * Accessibility (HTML5 ARIA, keyboard support, color contrast, etc.) + * Dark theme support + * User-defined search scope: + * Prevent to create an empty scope + * Activate search scope on creation + * Printing via shortcut (Ctrl+P) should print the content + only, even when the focus is outside the content (e.g. in + the search field) + * Bookmarks: adding a bookmark should be more intuitive + * Customization, e.g. to be able to specify an Infocenter + (header/banner, footer, etc.): support of + [legacy options](https://help.eclipse.org/latest/topic/org.eclipse.platform.doc.isv/guide/ua_help_setup_preferences.htm) + * ... +* By default redirect via `.../index.jsp#topic/...` instead of via `.../index.jsp?topic=...`, but still support such legacy links +* ... + + +## Design decisions and debugging hints + +To use a web browser for debugging, specify a fixed port for the help server, +e.g. `-Dserver_port=49999`, in Eclipse open the help window +(_Help > Help Contents_) and in the web browser open one or more of the +following pages: +* [Modernized UI: `http://127.0.0.1:49999/help/index.jsp`](http://127.0.0.1:49999/help/index.jsp) +* [Legacy UI: `http://127.0.0.1:49999/help/index.jsp?legacy`](http://127.0.0.1:49999/help/index.jsp?legacy) + + +### Drop of _Index_ tab + +The _Index_ tab of the legacy UI was dropped in favor of a simpler UI. + + +### Browser support + +On the one hand state of the art should be used, on the other hand as many +browsers as possible should be supported. + +→ Support browsers that support Flexbox ([98.74%](https://caniuse.com/#feat=flexbox)): +Chrome 21, Internet Explorer 10, Firefox 22, Android Browser 21, etc. and higher + +See: +* [Browser support](https://caniuse.com/) +* Tutorial/specification: [CSS](https://www.w3schools.com/csS/default.asp), + [JavaScript](https://www.w3schools.com/js/default.asp) +* JavaScript minifiers: + * https://javascript-minifier.com/ + * https://javascriptminifier.com/ + + +### General layout (CSS): [`Flexbox`](https://www.w3schools.com/csS/css3_flexbox.asp) ([tutorial](https://css-tricks.com/snippets/css/a-guide-to-flexbox/), [98.74%](https://caniuse.com/#feat=flexbox)) + +* Instead of `float`, layout via tables (both are deprecated for that) and `gridx` (since it is too new and not yet widely supported) +* If needed, consider add fallback for IE 6-9 (see [Flexbox Fallbacks](http://maddesigns.de/flexbox-fallbacks-2670.html)) + + +### Navigation and deep linking + +Going back in the browser history can cause issues in combination with deep linking, since the navigation is done in +the `iframe` except for the search page which is not shown in the `iframe`. +The problem is that when going back to a search page, the search might need to be submitted again and for this the +query must be known. + +Ways that don't work: + +* Deep link containing query as hash of the top window (`...#q=...`) set via + [`history.pushState(...)`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState): + top window hash might not be restored when navigation happens also in the content `iframe` +* Query as hash or as query of the content `iframe` set via + [`history.pushState(...)`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState): + conflicts with existing hashes/queries of content pages and does not work with external content pages +* [Data URL](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/Data_URIs) used in content `iframe` + containing the query: not allowed in Internet Explorer for security reasons + +Chosen solution: + +* For full search set content `iframe` instead of doing a remote request and get + result from live DOM of the `iframe` when loaded + + +### Search + +* Without [interim results](https://github.com/howlger/Eclipse-Help-Modernized/blob/541481f486008f665244446052d2a7e6d147223c/de.agilantis.help_ui_modernized/index.js#L482-L513) displayed [semi-transparent](https://github.com/howlger/Eclipse-Help-Modernized/blob/541481f486008f665244446052d2a7e6d147223c/de.agilantis.help_ui_modernized/index.js#L607) since this is only helpful in rare cases (very slow responds and previous cached query containing hits of current query) + +To simulate a slow response replace `return function(data) {` with + +``` + return function(data) { +setTimeout(function(data) { return function() {processData(data)}}(data), 1000); +}; +function processData(data) { +``` + + +## Issues caused by `<iframe>` + +To catch mouse and click events (for slider and drop-down menues) add an overlay element covering the whole page (see `createOverlay()`). + +Debug overlay by adding the following line after the line `overlayStyle.width = '100%';`: + +``` +overlayStyle.background = 'rgba(200, 100, 100, .2)'; +``` diff --git a/org.eclipse.help.webapp/m/index.css b/org.eclipse.help.webapp/m/index.css new file mode 100644 index 000000000..2595bff90 --- /dev/null +++ b/org.eclipse.help.webapp/m/index.css @@ -0,0 +1,668 @@ +/******************************************************************************* + * Copyright (c) 2020 Holger Voormann and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ******************************************************************************* + + Structure: + ____________________________________________ + | #h(eader) | + |--------------------------------------------| + | #m(ain area) | + | ________________________________________ | + | | | | | | + | | #t(oc) | #s(lider) | #c(ontent iframe) | | + | |________|___________|___________________| | + |--------------------------------------------| + | #f(ooter) | + |____________________________________________| + + *******************************************************************************/ +* { + border: 0; + margin: 0; + padding: 0; +} +input:focus { + outline: none; +} +html, body, #m { + height: 100%; + overflow: hidden; +} +body, input, button { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", "Lucida Grande", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol"; + font-size: 1rem; + color: #222; +} +body { + display: -webkit-flex; + display: flex; + -webkit-flex-flow: column; + flex-flow: column; + background: #eef0f3; +} +a { + text-decoration: none; +} +strong { + color: #444; +} +button { + background: transparent; + font-size: 1em; + cursor: pointer; +} +#h { + padding: 6px 6px 4px; + display: flex; +} +#m { + width: 100%; + height: 100%; + padding: 2px 0 0; + -webkit-flex: 0 1 auto; + flex: 0 1 auto; + display: -webkit-flex; + display: flex; +} +#t { + width: 400px; + overflow: auto; + scroll-behavior: smooth; +} +#t.hide { + -ms-overflow-style: -ms-autohiding-scrollbar; /* IE and Edge: avoid that the scrollbar is shown even when TOC is hidden */ +} +#t .tree { + padding-top: 6px; +} +#s { + width: 12px; + z-index: 2; + cursor: e-resize; +} +#c, .c { + -webkit-flex: auto; + flex: auto; +} +.c { + overflow: auto; + flex-basis: 0; +} +#t, #c, .c { + height: 1px; + min-height: 100%; + max-height: 100%; +} +#t, #c, .c, .qf, #a { + background: #fff; + box-shadow: 0 1px 3px rgba(0,0,0,0.15); +} + +#f { display: none; } + +/*** main toolbar (in the upper left) ***/ +.y { + display: flex; + margin-right: 6px; +} + +/*** button ***/ +.b { + display: inline-block; + padding: 8px; + font-size: 0; + color: #555; + background: transparent; + box-sizing: content-box; + height: 20px; + width: 20px; +} +.b:hover, #h .q0 > .b:hover { + background: #d7d9db; + border-radius: 5px; +} + +/*** search field (area) ***/ +.q0 { + flex: auto; + display: flex; +} +.q1 { + position: relative; + max-width: 640px; + flex: auto; +} +.q, .qf, .qm { + display: flex; + border-radius: 8px; + background: #fff; +} +.q { + border: 1px solid #fff; + box-shadow: 0 1px 1px rgba(0,0,0,0.05); +} +.qf, .qm { + border: 1px solid #37e; + box-shadow: 0 1px 3px rgba(119,153,238,0.4); +} +.qm { + border-radius: 8px 8px 2px 0; + border-bottom-color: rgba(255,255,255,0); +} +.q0 .b:hover { + background: none; +} +.qf.qa .b, .qm.qa .b, .q.qa .b:hover, #b .a .b, #b .a .b:hover { + color: #fff; + background: #37e; + margin: 0 -0.5px 0 0; /* workaround for Microsoft Edge to avoid white between blue button and blue border */ +} +.qf.qa .b { + border-radius: 0 7px 7px 0; +} +.qm.qa .b { + border-radius: 0 7px 0 0; +} +.q .b:hover { + background: #fff; + cursor: default; +} +.q0 .b { + border-radius: 7px; + margin: 0; + padding: 7.8px; +} +.q .b, .qf .b { + cursor: default; +} +.q2 { + display: flex; + flex: auto; +} +.q2, .q1 .b { + z-index: 2; +} +#q, .qh { + width: 100%; /* needed for some mobiles where "flex: auto" is not enough or does not work */ + flex: auto; + min-width: 24px; + border: 0; + margin: 2px 0; + font-size: 1rem; + background: #fff; +} +.qh { + margin: 0; + color: #ddd; +} +#q::placeholder { + color: #999; + opacity: 1; + font-style: italic; +} +.u { + position: relative; +} + +/*** search proposals (.p) and scopes button (.s) with drop-down (.u) ***/ +.c { + padding: 6px; +} +.r0 { + margin: 1em; + padding: 6px 0; +} +.j, .j li, .j a, .j button { + list-style: none outside none; + display: block; +} +.j { + overflow-wrap: anywhere; +} +.p a, .p button, .u li { + padding: 0.5em 9px; + box-sizing: border-box; + width: 100%; + text-align: left; + cursor: pointer; +} +.p .w { + float: right; +} +.p .n { + clear: both; + margin-top: 3px; +} +.p .v { + display: inline-block; +} +#r .j li a, #b li a, .ba { + max-width: 640px; + margin: 0 0 0.25em 10px; + padding: 6px; + border-radius: 4px; +} +.j .z a, .j .z button, .j a:focus, .j button:focus, .u .z { + background: #e8f2fe; + outline: none; +} +.j, .j a, #r .t, #b a { + color: #222; +} +.j a .w, #r .tree .l, #r .t .tl { + color: #062; +} +#r .l { + cursor: pointer; +} +.w, .n { + font-size: 85%; +} +#r .w, #r .n { + margin-top: 3px; +} +.p, .u { + position: absolute; + float: left; + left: 0; + margin-top: -1px; + top: 100%; + width: 100%; + padding: 0; + z-index: 3; + background: rgba(0, 0, 0, 0); +} +.u { + margin: -4px 0 0 3px; + width: auto; +} +.s0 { + position: relative; + z-index: 5; +} +.s { + background: #f2f4f7; + box-sizing: content-box; + display: flex; + margin: 3px 6px 3px 3px; + padding: 0 0 0 8px; + align-items: center; + border-radius: 5px; + font-size: 1rem; + white-space: nowrap; +} +.s span { + max-width: 240px; + overflow: hidden; +} +.s, .d, .de { + color: #999; +} +.qf .s, .qm .s, .s:hover .d, .s:hover .de { + color: #222; +} +.s .d, .s .de { + display: inline-block; + transform: rotate(90deg); + margin-left: -6px; + margin-right: 6px; + margin: 0 2px 0 -4px; +} +.s .de { + margin: 0 6px 0 -6px; +} +.p ol, .u ul { + display: block; + padding: 0; + margin: 0; + background: #fff; + border: 1px solid #37e; + border-top: none; + border-radius: 0 0 7px 7px; + box-shadow: 0 3px 4px rgba(0, 0, 0, 0.2); + overflow: hidden; +} +.s, .u ul { + border: 1px solid #edeff2; /* -2 V of background */ +} +.u li { + white-space: nowrap; + padding-left: 36px; +} +.u li.x, .u li.y { + padding-left: 9px; +} +.u li.l { + border-top: 1px solid #edeff2; +} +.u li.x:before { + margin-right: 6px; +} +.p .f { + display: none; + position: absolute; + top: 0; + left: 100%; + margin-left: -1px; + height: 100%; + min-height: 42em; + width: 100%; + background: #ffffff; + border: 1px solid #999999; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); + z-index: 5; +} +.p .z .f { + display: block; + z-index: 5; +} +.count { + display: inline-block; + background: #eee; + border-radius: 2em; + color: #666; + font-size: 0.8em; + font-weight: bold; + line-height: 1; + margin: 0; + min-width: 1em; + padding: 3px 7px 4px 7px; + position: relative; + text-align: center; + vertical-align: top; +} +#r .tree { + margin-bottom: 0.75em; +} +.tree input { + margin-right: 0.5em; +} + +#h .q0 > .b { + margin-left: 6px; +} + +/*** bookmarks (#b) and scopes (#o) ***/ +.g { + margin: 0 0 0.5em 10px; + font-weight: bold; +} +#b .b, #o .b { + width: auto; + font-size: 1em; + font-weight: bold; + background: #f2f4f7; + border: 1px solid #edeff2; + border-radius: 5px; + margin: 0.5em -12px 0 18px; + color: #999; +} +#b .b:hover, #o .b:hover { + color: #222; +} +#b .br:hover, #o .br:hover { + color: #fff; + background-color: #c11; +} +#b .bc, #o .bc { + color: #555; + background: none; + border: none; + margin: 1em 0 2px 2em; + float: right; +} +#b .bc:hover, #o .bc:hover { + background: #d7d9db; +} +#b li .b { + margin: 0; +} +#o li .b { + margin: 1px; +} +#b input, #o .a input { + width: 320px; +} +#b .br, #o .br { + color: #fff; + background: #e33; +} +#b li .br, #o li .b { + padding: 4px 8px; +} +#b .a .b { + border: none; + border-radius: 0 5px 5px 0; +} +#b li, #o li { + margin: 0 0 0 18px; + list-style: none; +} +#b li a, .ba { + display: inline-flex; + align-items: center; + margin: 0; + padding: 0 3px; +} +#b li a:hover, .ba:hover { + background-color: #e8f2fe; +} +#b li a span, .ba { + padding: 3px; +} +.c .a { + border: 1px solid #37e; + border-radius: 8px; + margin: 1em 0 1em 16px; + display: inline-flex; + min-height: 36px; + align-items: center; +} +#o div.g { + margin: 1em 0 0.5em 10px; +} +.c .tree { + padding: 0 0 8px 0; +} +#b input, #o .a input { + margin: 3px 0.5em; +} + +/*** menu ***/ +#a { + background: #eef0f3; + height: 100%; + width: 0; + position: fixed; + z-index: 9; + top: 0; + right: 0; + overflow-x: hidden; + transition: all 0.25s ease-in; + border-left: #ddd solid 1px; + display: flex; + flex-direction: column; +} +.e { + text-align: right; + padding: 6px; +} +#a > .b { + width: auto; + height: auto; + font-size: 1em; + margin: 3px 6px; + color: #222; + text-align: left; + padding-left: 40px; /* = 8 + 20 + 12 */ +} +#af { + margin: 3px 6px; +} +#af span { + display: inline-block; + padding: 8px 8px 8px 40px; +} +#af .b { + font-size: 1rem; +} +#a > #ap.b, #a > #app.b { + padding-left: 8px; +} +#a > .b:hover { + color: #111; +} +#a > #ah { + padding-left: 40px; +} +#a > #ah.x { + padding-left: 6px; +} +#ah.x:before, #ap:before, #app:before { + display: inline-block; + vertical-align: bottom; + font-size: 0; + width: 20px; + height: 20px; + margin: 0 12px 0 0; +} +#ah.x:before, .u li.x:before { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' focusable='false' role='presentation'%3E%3Cpath d='M 18.720152,3.3629316 C 18.060084,2.7820531 17.056888,2.8348761 16.47601,3.4949448 L 7.7898314,13.157953 3.4335592,8.9600604 C 2.7999482,8.3528147 1.7966621,8.3791818 1.1894164,9.0128871 0.58217173,9.6465896 0.60853792,10.649785 1.2422432,11.257031 l 5.5443048,5.333102 c 0.2903944,0.290393 0.6864358,0.448864 1.1088434,0.448864 h 0.052827 c 0.4224085,-0.02636 0.8448161,-0.211204 1.1088434,-0.528055 L 18.825715,5.6334442 C 19.432961,4.9470093 19.380137,3.9438138 18.720159,3.3629353 Z' fill='%2354595d'/%3E%3C/svg%3E"); + + /* required for IE: */ + display: inline-block; + width: 20px; + +} +#ap:before, #app:before { + content: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' focusable='false' role='presentation'%3E%3Cpath d='m 6.1536984,14.617279 v 1.536358 h 7.6920456 v -1.536358 z m 0,-2.564015 v 1.536357 h 7.6920456 v -1.536357 z m -4.615227,1.536357 h 1.538409 v 4.10345 c -0.0051,1.246112 0.281016,1.537384 1.538409,1.537384 H 15.384153 c 1.257393,0 1.538409,-0.231787 1.538409,-1.537384 v -4.10345 h 1.538409 c 1.307648,0.02051 1.553281,-0.270247 1.538409,-1.537383 V 6.411404 c 0,-1.2050867 -0.194865,-1.5394343 -1.538409,-1.5394343 H 16.922562 V 2.30898 C 16.937433,1.0423563 16.691801,0.76954505 15.384153,0.76954505 H 4.6152894 c -1.230727,0 -1.543537,0.29332335 -1.538409,1.53943495 v 2.5629897 h -1.538409 c -1.323032,0 -1.538409999868,0.2881953 -1.538409999868,1.5394343 v 5.640834 C -0.0050386,13.298349 0.3077434,13.610134 1.5384714,13.589621 Z M 15.384153,17.692046 H 4.6152894 v -6.66644 H 15.384153 Z m 0.96407,-9.485831 c 0,-0.461523 0.369218,-0.830228 0.830741,-0.830228 0.461522,0 0.830741,0.368705 0.830741,0.830228 0,0.461523 -0.369219,0.830741 -0.830741,0.830741 -0.461523,0 -0.830741,-0.369218 -0.830741,-0.830741 z M 4.6152894,2.3079544 H 15.384153 V 4.8740209 H 4.6152894 Z' fill='%2354595d'/%3E%3C/svg%3E"); +} +.ht { + background: #ff6; +} + +/*** tree ***/ +.tree ul { + margin: 0; + padding: 0 0 0 8px; + line-height: 1.3; +} +.tree .closed ul, div#results .closed ul { + display: none; +} +.tree .l { + display: inline-block; + padding: 2px 6px; + border-radius: 4px; + color: #222; + + /* TODO with icons */ + margin-left: 32px; + + /* TODO without icons */ + margin-left: 12px; + +} +.tree .l img { + + /* TODO with icons */ + margin: 0 4px -2px -20px; + + /* TODO without icons */ + display: none; + +} +.tree li { + padding: 0 0 0 10px; /* indent TOC subitems: 10px => .tree a padding-left: 42px - 10px = 32px */ + list-style: none; + position: relative; +} +.tree li.xx > .l, .tree li.x > .l { + font-weight: bold; +} +.tree li.xx > .l { + color: #333; +} +.tree li.x > .l { + color: #444; +} +.tree li.xx > .l { + background-color: #eef0f3; +} +.tree .l, .tree li.xx > .l, .tree li.x > .l, .tree li.xx > .l:hover, .tree li.x > .l:hover { + transition: all 0.1s ease; +} +.tree li > .l:hover { + transition: all 0.1s 0.1s ease; + background-color: #f4f9ff; +} +.tree li > .l:focus { + background-color: #e8f2fe; + outline: none; +} +.tree .h { + /* prevent text selection */ + user-select: none; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; /* otherwise in MS Edge a blinking text cursor will be shown */ + display: inline-block; + position: absolute; + left: -2px; + cursor: pointer; + animation: fly-left 3s ease-in forwards; + color: #999; + transition: transform .25s, color .25s; +} +.tree .open > .h { + color: #333; + transform: rotate(90deg); +} + +/*** logo ***/ +.k1 /* width: 36..146 */ { + /* logo icon */ +} +.kf /* width: > 146 */ { + /* logo full */ +} + +/******************************************************************************* + * Mobile */ + +@media screen and (max-width: 768px) { + + #t, #s, .i, .k0, .k1, .kf, .ke0, .ke1, .kef { + display: none; + } + #m { + overflow: hidden; + } + #t.show { + display: block; + min-width: 100%; + } + .s span { + max-width: 60px; + overflow: hidden; + } + +} + +@media screen and (max-height: 360px) { + .p ol { + overflow-y: auto; + max-height: calc(100vh - 38px); + -ms-overflow-style: none; + scrollbar-width: none; + } + .p ol::-webkit-scrollbar { + display: none; + } +}
\ No newline at end of file diff --git a/org.eclipse.help.webapp/m/index.html b/org.eclipse.help.webapp/m/index.html new file mode 100644 index 000000000..69c78558e --- /dev/null +++ b/org.eclipse.help.webapp/m/index.html @@ -0,0 +1,19 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <meta http-equiv="X-UA-Compatible" content="IE=edge"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Help</title> + <link rel="stylesheet" type="text/css" href="m/index.css"> + <script type="text/javascript" src="m/index.js"></script> +</head> +<body> + <header id="h"></header> + <div id="m"> + <aside id="t"></aside> + <iframe id="c" name="c"></iframe> + </div> + <footer id="f"></footer> +</body> +</html>
\ No newline at end of file diff --git a/org.eclipse.help.webapp/m/index.js b/org.eclipse.help.webapp/m/index.js new file mode 100644 index 000000000..b06be0671 --- /dev/null +++ b/org.eclipse.help.webapp/m/index.js @@ -0,0 +1,2845 @@ +/******************************************************************************* + * Copyright (c) 2020 Holger Voormann and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + ******************************************************************************/ +(function(window, document) { + + var SMALL_SCREEN_WIDTH = 768; + var LOGO_ICON_WIDTH = 36; + var LOGO_FULL_WIDTH = 146; + var MENU_FONT_SIZING = 0; + var MENU_HELP = 0; + var MENU_ABOUT = 0; + var TOC_SIDEBAR_DEFAULT_WIDTH = 380; + var TOC_SIDEBAR_MINIMUM_WIDTH = 76; + var TOC_SIDEBAR_WIDTH_COOKIE_NAME = 'toc_width'; + var TOC_ICON_DESCRIPTION = 'Toggle table of content'; + var TOC_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><path fill="currentColor" d="M19 5H1V3h18v2zm0 10H1v2h18v-2zm-4-6H1v2h14V9z"/></svg>'; + var HISTORY_BACK_DESCRIPTION = 'Go back one page'; + var HISTORY_BACK_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><path fill="currentColor" d="m 8.27224,17.95644 c 0.39048,0.390343 1.023592,0.390211 1.413938,0 0.390613,-0.390612 0.390613,-1.023597 -1.33e-4,-1.41421 L 4.144057,11.000499 18.27041,10.999748 c 0.55219,-1.31e-4 0.999731,-0.447671 0.999731,-1.0001299 -1.34e-4,-0.552189 -0.447674,-0.999595 -0.999864,-0.999595 l -14.126755,9.99e-4 5.542723,-5.543204 c 0.390479,-0.390479 0.390479,-1.023727 0,-1.414074 -0.195307,-0.195173 -0.451138,-0.292892 -0.707102,-0.292892 -0.255832,0 -0.511664,0.09772 -0.70697,0.292759 l -7.249421,7.250043 c -0.187575,0.18744 -0.292893,0.441666 -0.292893,0.7069649 1.33e-4,0.2653 0.105451,0.519396 0.293025,0.707237 z"/></svg>'; + var HISTORY_FORWARD_DESCRIPTION = 'Go back one page'; + var HISTORY_FORWARD_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><path fill="currentColor" d="m 11.72776,17.95644 c -0.39048,0.390343 -1.023592,0.390211 -1.413938,0 -0.390613,-0.390612 -0.390613,-1.023597 1.33e-4,-1.41421 L 15.855943,11.000499 1.72959,10.999748 C 1.1774,10.999617 0.729859,10.552077 0.729859,9.9996181 0.729993,9.4474291 1.177533,9.0000231 1.729723,9.0000231 l 14.126755,9.99e-4 -5.542723,-5.543204 c -0.390479,-0.390479 -0.390479,-1.023727 0,-1.414074 0.195307,-0.195173 0.451138,-0.292892 0.707102,-0.292892 0.255832,0 0.511664,0.09772 0.70697,0.292759 l 7.249421,7.250043 c 0.187575,0.18744 0.292893,0.441666 0.292893,0.7069649 -1.33e-4,0.2653 -0.105451,0.519396 -0.293025,0.707237 z"/></svg>'; + var BOOKMARKS_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><path fill="currentColor" d="M 10.019531 0.5 A 1.000065 1.000065 0 0 0 9.1035156 1.0566406 L 6.5742188 6.1816406 L 0.91796875 7.0039062 A 1.000065 1.000065 0 0 0 0.36523438 8.7089844 L 4.4570312 12.699219 L 3.4902344 18.332031 A 1.000065 1.000065 0 0 0 4.9414062 19.384766 L 10 16.726562 L 15.058594 19.384766 A 1.000065 1.000065 0 0 0 16.509766 18.332031 L 15.542969 12.699219 L 19.634766 8.7089844 A 1.000065 1.000065 0 0 0 19.082031 7.0039062 L 13.425781 6.1816406 L 10.896484 1.0566406 A 1.000065 1.000065 0 0 0 10.019531 0.5 z M 10 3.7597656 L 11.865234 7.5390625 A 1.000065 1.000065 0 0 0 12.617188 8.0859375 L 16.789062 8.6914062 L 13.771484 11.632812 A 1.000065 1.000065 0 0 0 13.482422 12.517578 L 14.195312 16.673828 L 10.464844 14.710938 A 1.000065 1.000065 0 0 0 9.5351562 14.710938 L 5.8046875 16.673828 L 6.5175781 12.517578 A 1.000065 1.000065 0 0 0 6.2285156 11.632812 L 3.2109375 8.6914062 L 7.3828125 8.0859375 A 1.000065 1.000065 0 0 0 8.1347656 7.5390625 L 10 3.7597656 z"/></svg>'; + var BOOKMARKS_DESCRIPTION = 'Bookmarks'; + var BOOKMARKS_CLOSE_DESCRIPTION = 'Close bookmarks'; + var BOOKMARKS_ADD_PAGE_DESCRIPTION = 'Bookmark current page'; + var BOOKMARKS_ADD_SEARCH_DESCRIPTION = 'Bookmark current search'; + var BOOKMARKS_DELETE = 'Delete'; + var BOOKMARKS_DELETE_DESCRIPTION = 'Delete this bookmarks (cannot be undone)'; + var BOOKMARKS_DELETE_ALL = 'Delete all bookmarks'; + var BOOKMARKS_DELETE_ALL_DESCRIPTION = 'Delete all bookmarks (cannot be undone)'; + var BOOKMARKS_PATTERN = new RegExp('<tr[^<]*<td[^<]*<a\\s+(?:(?!href)[\\w\\-]+\\s*=\\s*(?:(?:\'[^\']*\')|(?:"[^"]*"))\\s+)*href\\s*=\\s*\'([^\']*)\'[^<]*<img[^>]*>\\s*([^<]*)</a>', 'gi'); + var MENU_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><path fill="currentColor" d="M 10 1.5 A 2 2 0 0 0 8 3.5 A 2 2 0 0 0 10 5.5 A 2 2 0 0 0 12 3.5 A 2 2 0 0 0 10 1.5 z M 10 8 A 2 2 0 0 0 8 10 A 2 2 0 0 0 10 12 A 2 2 0 0 0 12 10 A 2 2 0 0 0 10 8 z M 10 14.5 A 2 2 0 0 0 8 16.5 A 2 2 0 0 0 10 18.5 A 2 2 0 0 0 12 16.5 A 2 2 0 0 0 10 14.5 z"/></svg>'; + var MENU_ICON_DESCRIPTION = 'Show menu'; + var MENU_CLOSE_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><path fill="currentColor" d="M 4.34375 2.9296875 L 2.9296875 4.34375 L 8.5859375 10 L 2.9296875 15.65625 L 4.34375 17.070312 L 10 11.414062 L 15.65625 17.070312 L 17.070312 15.65625 L 11.414062 10 L 17.070312 4.34375 L 15.65625 2.9296875 L 10 8.5859375 L 4.34375 2.9296875 z"/></svg>'; + var MENU_CLOSE_ICON_DESCRIPTION = 'Hide menu'; + var TREE_HANDLE = '<svg width="24" height="24" viewBox="0 0 24 24" focusable="false" role="presentation">-<path d="M10.294 9.698a.988.988 0 0 1 0-1.407 1.01 1.01 0 0 1 1.419 0l2.965 2.94a1.09 1.09 0 0 1 0 1.548l-2.955 2.93a1.01 1.01 0 0 1-1.42 0 .988.988 0 0 1 0-1.407l2.318-2.297-2.327-2.307z" fill="currentColor"/></svg>'; + var BOOK_NAME_SHORTENER = function shortenBookName(bookName) { return bookName.replace(/\s+(Documentation\s*)?(\-\s+([0-9,\-]+\s+)?Preview(\s+[0-9,\-]+)?\s*)?$/i, ''); }; + var BOOK_SCOPE_BY_DEFAULT = 0; + var BOOK_SCOPE_COOKIE = 'book-scope'; + var SEARCH_SCOPE_CLOSE_DESCRIPTION = 'Close scopes'; + var SEARCH_ICON = '<svg width="20" height="20" viewBox="0 0 20 20"><g fill="#fff"><path fill="currentColor" d="M 7.5 0 C 3.3578644 0 0 3.3578644 0 7.5 C 0 11.642136 3.3578644 15 7.5 15 C 8.8853834 14.997 10.242857 14.610283 11.421875 13.882812 L 17.185547 19.662109 C 17.632478 20.113489 18.36112 20.112183 18.8125 19.660156 L 19.623047 18.845703 C 20.072507 18.398153 20.072507 17.665594 19.623047 17.214844 L 13.871094 11.447266 C 14.607206 10.26212 14.998156 8.8951443 15 7.5 C 15 3.3578644 11.642136 0 7.5 0 z M 7.5 2 A 5.5 5.5 0 0 1 13 7.5 A 5.5 5.5 0 0 1 7.5 13 A 5.5 5.5 0 0 1 2 7.5 A 5.5 5.5 0 0 1 7.5 2 z"/></g></svg>'; + var SEARCH_FIELD_DESCRIPTION = '* = any string\n? = any character\n"" = phrase\nAND, OR & NOT = boolean operators'; + var SEARCH_FIELD_PLACEHOLDER = 'Search'; + var BASE_URL; + var SEARCH_BASE_URL; + var SEARCH_HITS_MAX = 500; + var SEARCH_AS_YOU_TYPE_PROPOSAL_MAX = 7; + var SEARCH_RESULTS_INDEXING_PATTERN = new RegExp('[\'"]divProgress[\'"]\\s+STYLE\\s*=\\s*[\'"]\\s*width\\s*:\\s*([\\d]+)\\s*px', 'i'); + var SEARCH_RESULTS_PATTERN = new RegExp('<tr[^<]*<td[^<]*<img[^<]*</td[^<]*<td[^<]*<a\\s+(?:(?!href)(?!title)[\\w\\-]+\\s*=\\s*(?:(?:\'[^\']*\')|(?:"[^"]*"))\\s+)*(href|title)\\s*=\\s*"([^"]*)"\\s+(?:(?!href)(?!title)[\\w\\-]+\\s*=\\s*(?:(?:\'[^\']*\')|(?:"[^"]*"))\\s+)*(href|title)\\s*=\\s*"([^"]*)"[^>]*>([^<]*)</a>(?:(?:(?!<[/]?tr)[\\s\\S])*</tr\\s*>\\s*<tr(?:(?!</tr)(?!class="location">)[\\s\\S])*class="location">((?:(?!</div)[\\s\\S])*))?(?:(?:(?!</tr)(?!\\sclass=["\']description["\'])[\\s\\S])*</tr){1,2}(?:(?!</tr)(?!\\sclass=["\']description["\'])[\\s\\S])*\\sclass=["\']description["\'][^>]*>([^<]*)', 'gi'); + var SEARCH_RESULTS_BREADCRUMB_SNIPPET_PATTERN = new RegExp('<a\\s+href="([^"]+)">([^<]+)</a>', 'gi'); + var SEARCH_SCOPE_LABEL_NONE = 'All'; + var SEARCH_SCOPE_LABEL_BOOK = 'Book'; + var SEARCH_SCOPE_LABEL_CHAPTER = 'Chapter'; +// var SEARCH_SCOPE_LABEL_TOPIC = 'Find in page'; + var SEARCH_SCOPE_CURRENT_PATTERN = new RegExp('<div\\s+id\\s*=\\s*"scope"\\s*>([^<]*)<'); + var SEARCH_SCOPE_ALL_PATTERN = new RegExp('<a\\s+(?:(?!title)[\\w\\-]+\\s*=\\s*(?:(?:\'[^\']*\')|(?:"[^"]*"))\\s+)*title\\s*=\\s*"([^"]*)"', 'gi'); + var SEARCH_SCOPE_NAME_PATTERN = new RegExp('<input\\s+type\\s*=\\s*["\']text["\']\\s+(?:(?!value)[\\w\\-]+\\s*=\\s*(?:(?:\'[^\']*\')|(?:"[^"]*"))\\s+)*value\\s*=\\s*\'([^\']*)\'', 'i'); + var SEARCH_SCOPE_IS_NEW_PATTERN = new RegExp('oldName\\s*=\\s*\'\'', 'i'); + var SEARCH_SCOPE_HREFS_PATTERN = new RegExp('<input(?:\\s+(?!checked)[\\w\\-]+\\s*=\\s*(?:(?:\'[^\']*\')|(?:"[^"]*")))*(\\s+checked)?[^<]+<label\\s+for\\s*=\\s*"([^"]*)"[^>]*>([^<]*)</label>\\s*(</div)?', 'gi'); + var SEARCH_AS_YOU_TYPE_CACHE_SIZE = 7; + var SEARCH_FULL_SEARCH_CACHE_SIZE = 3; + var SEARCH_DELAY_IN_MILLISECOND = 99; + var SEARCH_CACHE = {fi: -1, f: [], ti: -1, t: [] }; + + // TODO integration: the browser should not have to calculate the following variables itself; + // only "init()" should be called instead + var embeddedMode = 1; + var title = 'Help'; + var bookmarksPage; + var scopesPage; + var searchPage; + var renderFullSearch; + var updateScopeByToc; + var setSearchScope; + var currentSearch = {}; + var searchScope = {l: 0, s: '', t: 0}; + + addEvent(window, 'load', function() { + + // (search) base URL + var a = createElement(0, 'a'); + a.href = window.location.pathname.indexOf('/index.jsp') >= 0 ? 'x' : '../../x'; + BASE_URL = a.href.substring(0, a.href.length - 1); + SEARCH_BASE_URL = BASE_URL + 'advanced/searchView.jsp?showSearchCategories=false&searchWord='; + + remoteRequest(BASE_URL + 'advanced/search.jsp', function(responseText) { + + // read user defined search scope + var match = SEARCH_SCOPE_CURRENT_PATTERN.exec(responseText); + searchScope.i = match ? match[1] : match; + + // make sure no scope is set (a scope might have been set via the legacy UI before) + remoteRequest(BASE_URL + 'scopeState.jsp?workingSet='); + + // title + remoteRequest(BASE_URL + 'index.jsp?legacy', function(responseText) { + var match = new RegExp('<title>([^<]*)</title>').exec(responseText); + if (!match) return; + document.title = decodeHtml(match[1]); + }); + + // embedded or Infocenter mode? + read scopes + remoteRequest(BASE_URL + 'advanced/tabs.jsp', function(responseText) { + if (responseText.indexOf('e_bookmarks_view.') < 0) embeddedMode = 0; + + // read scopes + remoteRequest(BASE_URL + 'advanced/workingSetManager.jsp?t=' + Date.now(), function(responseText) { + var scopeIndex = 0; + SEARCH_SCOPE_ALL_PATTERN.lastIndex = 0; + for (var match; (match = SEARCH_SCOPE_ALL_PATTERN.exec(responseText)) != null;) { + var scopeName = decodeHtml(match[1]); + if (scopeName.substring(0, 1) == '\u200B') { + if (scopeName.length < 3) { + searchScope.l = scopeName.length; + break; + } else { + searchScope.l = 4; + searchScope.t = scopeName.length - 3; + continue; + } + } + if (searchScope.i && searchScope.i == scopeName) { + searchScope.l = 4; + searchScope.s = scopeName; + break; + } else if (searchScope.l == 4 && searchScope.t == scopeIndex) { + searchScope.s = scopeName; + } + scopeIndex++; + } + + init(); + + }); + }); + + }); + + }); + + function init() { + + // bookmarks page + bookmarksPage = createElement(getElementById('m'), 0, 'c'); + bookmarksPage.id = 'b'; + bookmarksPage.s = function(show) { + bookmarksPage.o = !!show; + if (show && scopesPage) scopesPage.s(); + bookmarksPage.style.display = show ? 'block' : 'none'; + getElementById('c').style.display = show || (searchPage && searchPage.o) ? 'none' : 'block'; + if (searchPage) searchPage.style.display = searchPage.o && !show ? 'block' : 'none'; + if (!show) return; + bookmarksPage.innerHTML = ''; + + // add bookmark + try { + var url = frames.c.location.href; + var title = searchPage.o ? searchPage.l : frames.c.document.title; + if (title == null || title == '') { + title = url; + } + createElement(bookmarksPage, 'span', 'g', 'Add:'); + var addArea = createElement(bookmarksPage, 'span', 'a'); + var input = createElement(addArea, 'input'); + input.type = 'text'; + input.autocomplete = 'off'; + input.value = title; + var addButtonTooltip = searchPage.o ? BOOKMARKS_ADD_SEARCH_DESCRIPTION : BOOKMARKS_ADD_PAGE_DESCRIPTION; + createButton(addArea, BOOKMARKS_ICON, addButtonTooltip, function() { + try { + var url = frames.c.location.href; + if (url.substring(0, BASE_URL.length + 6) == BASE_URL + 'topic/') { + url = url.substring(BASE_URL.length + 5); + + // workaround for a bug of the legacy UI: non-topic links (e.g. ".../nav/..." instead of + // ".../topic/...") are stored absolutely instead of relatively (which doesn't work, because by + // default a random port number is chosen when restarting Eclipse) + } else if (url.substring(0, BASE_URL.length) == BASE_URL) { + url = '/../' + url.substring(BASE_URL.length); + } + + var title = frames.c.document.title; + if (title == null || title == '') { + title = url; + } + remoteRequest( BASE_URL + 'advanced/bookmarksView.jsp?operation=add&' + + 'bookmark=' + encodeURIComponent(url) + + '&title=' + encodeURIComponent(input.value) + + '&t=' + Date.now()); + bookmarksPage.s(); + } catch (e) {} + }); + } catch (e) {} + + // "x" button + setClassName(createButton(bookmarksPage, MENU_CLOSE_ICON, BOOKMARKS_CLOSE_DESCRIPTION, function() { + bookmarksPage.s(); + }), 'b bc'); + + remoteRequest(BASE_URL + 'advanced/bookmarksView.jsp?t=' + Date.now(), function(responseText) { + var ol; + var element = createElement(); + var deleteButtons = []; + BOOKMARKS_PATTERN.lastIndex = 0; + for (var match; (match = BOOKMARKS_PATTERN.exec(responseText)) != null;) { + if (!ol) { + createElement(bookmarksPage, 0, 'g', 'Bookmarks:'); + ol = createElement(bookmarksPage, 'ol'); + } + var groups = []; + for (var i = 1; i < 4; i++) { + element.innerHTML = match[i]; + groups.push((element.textContent ? element.textContent : element.innerText).replace(/^\s+|\s+$/g,'').replace(/\s+/g,' ')); + } + var li = createElement(ol, 'li'); + var href = groups[0].substring(0, 3) == '../' ? BASE_URL + '/' + groups[0] : groups[0]; + var deleteButton = createButton(li, + BOOKMARKS_DELETE, + BOOKMARKS_DELETE_DESCRIPTION, + function(href, title, li) { + return function() { + remoteRequest( BASE_URL + 'advanced/bookmarksView.jsp?operation=remove&' + + 'bookmark=' + encodeURIComponent(href) + + '&title=' + encodeURIComponent(title) + + '&t=' + Date.now()); + li.style.display = 'none'; + } + }(groups[0].substring(0, 9) == '../topic/' ? groups[0].substring(8) : groups[0], groups[1], li)); + deleteButton.style.display = 'none'; + deleteButton.className = 'b br'; + deleteButtons.push(deleteButton); + var a = createElement(li, 'a'); + a.target = 'c'; + a.href = href; + createElement(a, 'span').innerHTML = BOOKMARKS_ICON; + createElement(a, 'span', 0, groups[1]); + } + if (deleteButtons.length) { + var editButton = createButton(bookmarksPage, 'Edit', 'Delete bookmarks', function() { + var editMode = editButton.innerHTML == 'Edit'; + editButton.innerHTML = editMode ? BOOKMARKS_DELETE_ALL : 'Edit'; + editButton.title = editMode ? BOOKMARKS_DELETE_ALL_DESCRIPTION : 'Delete bookmarks'; + setClassName(editButton, editMode ? 'b br' : 'b'); + for (var i = 0; i < deleteButtons.length; i++) { + deleteButtons[i].style.display = 'inline-block'; + } + if (!editMode) { + remoteRequest(BASE_URL + 'advanced/bookmarksView.jsp?operation=removeAll&t=' + Date.now()); + bookmarksPage.s(); + } + }); + } + createButton(bookmarksPage, 'Cancel', BOOKMARKS_CLOSE_DESCRIPTION, function() { bookmarksPage.s(); }); + + }); + } + bookmarksPage.s(); + + // scopes page + scopesPage = createElement(getElementById('m'), 0, 'c'); + scopesPage.id = 'o'; + scopesPage.s = function(show) { + scopesPage.o = !!show; + if (show && bookmarksPage) bookmarksPage.s(); + scopesPage.style.display = show ? 'block' : 'none'; + getElementById('c').style.display = show || (searchPage && searchPage.o) ? 'none' : 'block'; + if (searchPage) searchPage.style.display = searchPage.o && !show ? 'block' : 'none'; + if (!show) return; + function showScopesPage(query, scopeNr) { + scopesPage.innerHTML = ''; + + // "x" button + setClassName(createButton(scopesPage, MENU_CLOSE_ICON, SEARCH_SCOPE_CLOSE_DESCRIPTION, function() { + scopesPage.s(); + }), 'b bc'); + + // get and show content + remoteRequest( BASE_URL + + 'advanced/workingSet' + + (query ? '.jsp?' + query + '&' : 'Manager.jsp?') + + 't=' + Date.now(), + function(responseText) { + var match = SEARCH_SCOPE_NAME_PATTERN.exec(responseText); + if (match) { + createElement(scopesPage, 'span', 'g', 'Scope:'); + var scopeName = decodeHtml(match[1]); + var nameInput = createElement(createElement(scopesPage, 'span', 'a'), 'input'); + nameInput.type = 'text'; + nameInput.value = scopeName; + var tree = []; + var allNodes = []; + var parentNode; + SEARCH_SCOPE_HREFS_PATTERN.lastIndex = 0; + for (; (match = SEARCH_SCOPE_HREFS_PATTERN.exec(responseText)) != null;) { + var node = {n: {l: decodeHtml(match[3]), v: decodeHtml(match[2]), c: [], x: !!match[1]}, l: 1}; + allNodes.push(node.n); + if (match[4] && parentNode) { + parentNode.l = 0; + parentNode.n.c.push(node); + node.n.p = parentNode.n; + } else { + parentNode = node; + tree.push(node); + } + } + createTree(scopesPage, + + // content provider + function(node, processChildrenFn) { + processChildrenFn(node ? node.c : tree); + }, + + // label provider + function(li, node) { + var checkboxWithLabel = createElement(li); + var checkbox = createElement(checkboxWithLabel, 'input'); + checkbox.type = 'checkbox'; + checkbox.id = node.v; + if (node.x) { + checkbox.checked = 'checked'; + } + node.b = checkbox; + checkbox.n = node; + updateOrGetScopeCheckboxStates(node); + if (updateOrGetScopeCheckboxStates(node, 1)) { + setClassName(li, 'open'); + } + addEvent(checkbox, 'click', (function(node) { + return function() { + updateOrGetScopeCheckboxStates(node.p); + setSubtreeState(node, node.b.checked); + }; + })(node)); + var label = createElement(checkboxWithLabel, 'label', 0, node.l); + setAttribute(label, 'for', node.v); + return checkboxWithLabel; + } + + ); + function getHrefs() { + hrefs = ''; + for (var i = 0; i < allNodes.length; i++) { + var node = allNodes[i]; + if ( (node.b ? node.b.checked : node.x) + && (!node.p || !(node.p.b ? node.p.b.checked : node.p.x))) { + hrefs += '&hrefs=' + node.v; + } + } + return hrefs; + } + if (SEARCH_SCOPE_IS_NEW_PATTERN.exec(responseText)) { + createButton(scopesPage, 'Create', 'Add new scope', function() { + doScopesOperation( 'add&oldName=&workingSet=' + + encodeURIComponent(nameInput.value) + + getHrefs()); + }); + } else { + var deleteButton = createButton(scopesPage, 'Delete', 'Delete this scope', function() { + doScopesOperation('remove&workingSet=' + encodeURIComponent(scopeName)); + }); + deleteButton.className = 'b br'; + createButton(scopesPage, 'Apply', 'Update this scope', function() { + doScopesOperation( 'edit&oldName=' + + encodeURIComponent(scopeName) + + '&workingSet=' + + encodeURIComponent(nameInput.value) + + getHrefs()); + setSearchScope([4, scopeName, scopeNr]); + }); + } + createButton(scopesPage, 'Cancel', 'Go back to list of scopes',function() { showScopesPage(); }); + + } else { + createElement(scopesPage, 0, 'g', 'Scopes:'); + var ol = createElement(scopesPage, 'ol'); + SEARCH_SCOPE_ALL_PATTERN.lastIndex = 0; + for (var scopeIndex = 0; (match = SEARCH_SCOPE_ALL_PATTERN.exec(responseText)) != null;) { + var scopeName = decodeHtml(match[1]); + if (scopeName.substring(0, 1) == '\u200B') continue; + var li = createElement(ol, 'li'); + createButton(li, scopeName, 0, function(scopeName, scopeIndex) { + return function() { showScopesPage('operation=edit&workingSet=' + encodeURIComponent(scopeName), scopeIndex); }; + }(scopeName, scopeIndex), 'ba'); + scopeIndex++; + } + createButton(scopesPage, 'New', 'Add a new scope', function() { showScopesPage('operation=add'); }); + createButton(scopesPage, 'Cancel', SEARCH_SCOPE_CLOSE_DESCRIPTION, function() { scopesPage.s(); }); + } + }); + } + function doScopesOperation(query) { + remoteRequest( BASE_URL + + 'workingSetState.jsp?operation=' + + query + + '&t=' + Date.now(), + function() { showScopesPage(); }); + } + showScopesPage(); + } + function updateOrGetScopeCheckboxStates(node, computeState) { + for (var parent = node; parent; parent = parent.p) { + var allChecked = 1; + var allUnchecked = 1; + for (var i = 0; i < parent.c.length; i++) { + var childNode = parent.c[i].n; + if (childNode.b ? childNode.b.checked : childNode.x) { + allUnchecked = 0; + } else { + allChecked = 0; + } + } + if (computeState) { + return !allChecked && !allUnchecked; + } + if (!parent.c.length) continue; + parent.b.checked = !!allChecked; + parent.b.indeterminate = !allChecked && !allUnchecked; + } + } + function setSubtreeState(node, state) { + for (var i = 0; i < node.c.length; i++) { + var childNode = node.c[i].n; + if (childNode.b) { + childNode.b.checked = !!state; + } else { + childNode.x = state; + } + setSubtreeState(childNode, state); + } + } + scopesPage.s(); + + // search page + searchPage = createElement(getElementById('m'), 0, 'c', 'Loading...'); + searchPage.id = 'r'; + searchPage.s = function(show) { + searchPage.o = !!show; + searchPage.style.display = show ? 'block' : 'none'; + getElementById('c').style.display = show ? 'none' : 'block'; + if (bookmarksPage) bookmarksPage.s(); + if (scopesPage) scopesPage.s(); + if (show) { + document.title = searchPage.l + ' - ' + title; + } else { + try { + var topicTitle = contentFrame.contentDocument.title; + document.title = topicTitle ? (topicTitle + ' - ' + title) : title; + } catch(e) { + document.title = title; + } + } + } + searchPage.s(); + + // toolbar: TOC sidebar button and history Back/Forward buttons (in embedded help, but not in Infocenter mode) + var header = getElementById('h'); + var toolbarContainer = createElement(header); + var toolbar = createElement(toolbarContainer, 0, 'y'); + var tocSidebarToggleButton = createButton(toolbar, TOC_ICON, TOC_ICON_DESCRIPTION); + if (embeddedMode) { + createButton(toolbar, HISTORY_BACK_ICON, HISTORY_BACK_DESCRIPTION, function() { + window.history.back(); + }); + createButton(toolbar, HISTORY_FORWARD_ICON, HISTORY_FORWARD_DESCRIPTION, function() { + window.history.forward(); + }); + } + + // TOC slider (to change TOC sidebar width by moving the slider) + var smallScreenAutoCloseFn = createSlider(tocSidebarToggleButton, toolbarContainer, createElement(header, 0, 'i')); + + // fill TOC and create search field + var toc = getElementById('t'); + createTree(toc, + tocContentProvider, + function(li, node) { + if (node.toc) { + li.toc = node.toc; + } + li.n = node; + var a = createElement(li, 'a'); + a.href = node.h; + a.target = 'c'; + addEvent(a, 'click', smallScreenAutoCloseFn); + li.h = a.href; + li.a = a.hash; + li.b = a.protocol + '//' + a.host + a.pathname; + if (node.i) { + var iconImg = createElement(a, 'img'); + iconImg.setAttribute('src', BASE_URL + + 'advanced/images/' + + node.i + + '.svg'); + } + a.appendChild(document.createTextNode(node.t)); + return a; + }, + 1); + toc.c = 1; // scroll into view if needed: to upper third instead of scroll as less as possible + var contentFrame = getElementById('c'); + addEvent(contentFrame, 'load', function() { + + // full search? + var contentFrameHref = frames.c.location.href; + if (contentFrameHref.substring(0, SEARCH_BASE_URL.length) == SEARCH_BASE_URL) { + var data = frames.c.document.documentElement.innerHTML; + renderFullSearch(contentFrameHref.substring(SEARCH_BASE_URL.length), data); + searchPage.s(1); + return; + } + + // close maybe open bookmarks, scopes or search page + if (bookmarksPage) bookmarksPage.s(); + if (scopesPage) scopesPage.s(); + searchPage.s(); + updateDeepLink(); + + // font sizing + setFontSize(0, 1, 1); + + // update title and deep link + try { + var topicTitle = contentFrame.contentDocument.title; + document.title = topicTitle ? (topicTitle + ' - ' + title) : title; + } catch(e) { + document.title = title; + } + + // sync with TOC + try { + syncToc(); + addEvent(contentFrame.contentWindow, 'hashchange', function() { syncToc(); updateDeepLink(); }); + } catch(e) { + toc.x(0); + } + + }); + addEvent(window, 'hashchange', function() { + var newHash = window.location.hash; + if (isQueryHash(newHash)) { + searchFullByHash(newHash); + } else { + searchPage.s(); + } + }); + + } + function createButton(parent, innerHtml, description, clickFn, className, text) { + var button = createElement(parent, 'button', className ? className : 'b', text); + if (description) { + button.title = description; + } + if (innerHtml) { + setInnerHtml(button, innerHtml); + } + if (clickFn) { + addEvent(button, 'click', function(e) { preventDefault(e); clickFn(e); }); + } + return button; + } + + function initContentPage() { + + // set initial start/cover page... + // ...by hash + var hash = window.location.hash; + try { + if (hash && ( 'q=' == hash.substring(1, 3) + || 'nav/' == hash.substring(1, 5) + || 'topic/' == hash.substring(1, 7) + || 'rtopic/' == hash.substring(1, 8) + || 'ntopic/' == hash.substring(1, 8) + || 'nftopic/' == hash.substring(1, 9))) { + if ('q=' == hash.substring(1, 3)) { + searchFullByHash(hash); + } else { + getElementById('c').src = BASE_URL + hash.substring(1); + } + return; + } + } catch(e) {} + + // ...by legacy query parameters (topic/nav or search link) + var params = getParams(window.location.href.replace(/^[^#\?]*(?:\?([^#\?]*))?(#.*)?$/, '$1')); + var topicOrNav = params.topic || params.nav; + if (params.searchWord && params.tab == 'search') { + window.history.replaceState(null, '', window.location.pathname); + searchFullByHash('#q=' + encodeURIComponent(params.searchWord)); + return; + } + if (topicOrNav) { + getElementById('c').src = BASE_URL + + (params.nav ? 'nav' : 'topic') + + topicOrNav + + (params.anchor ? '#' + params.anchor : ''); + window.history.replaceState(null, '', window.location.pathname); + updateDeepLink(); + return; + } + + // ...default start/cover page + remoteRequest(BASE_URL + 'advanced/content.jsp', function(responseText) { + var start = responseText.indexOf('title="Topic View" src=\''); + if (start > 0) { + var end = responseText.indexOf("'", start + 24); + var element = createElement(null, 'p'); + element.innerHTML = responseText.substring(start + 24, end); + getElementById('c').src = BASE_URL + + 'topic/' + + (element.textContent ? element.textContent : element.innerText); + updateDeepLink(); + } + }); + + } + + function syncToc() { + var newLocation = getElementById('c').contentWindow.location; + var toc = getElementById('t'); + if (toc.s && toc.s.h == newLocation.href) return; + var newLocationHrefWithoutQueryAndHash = newLocation.protocol + '//' + newLocation.host + newLocation.pathname; + var newLocationHash = newLocation.hash; + var liMatch; + var liWhithoutHashMatch; + toc.v(function(li) { + if (newLocationHrefWithoutQueryAndHash != li.b) return 1; + if (!newLocationHash || newLocationHash == li.a) { + liMatch = li; + return 0; + } + if (!liWhithoutHashMatch) { + liWhithoutHashMatch = li; + } + return 1; + }); + if (liMatch) { + toc.x(liMatch, toc, 1); + } else if (liWhithoutHashMatch) { + toc.x(liWhithoutHashMatch, toc, 1); + } else { + toc.y(newLocation.href, toc, 1); + } + } + + function updateDeepLink(query) { + var hash; + if (query) { + hash = 'q=' + query; + } else { + try { + var src = getElementById('c').contentDocument.location.href; + if (BASE_URL != src.substring(0, BASE_URL.length)) return; + var current = src.substring(BASE_URL.length); + if ( 'nav/' == current.substring(0, 4) + || 'topic/' == current.substring(0, 6) + || 'rtopic/' == current.substring(0, 7) + || 'ntopic/' == current.substring(0, 7) + || 'nftopic/' == current.substring(0, 8)) { + hash = current; + } + } catch(e) {} + } + try { + var url = hash ? '#' + hash : window.location.href.replace(/^([^#\?]*(?:\?([^#\?]*))?)(#.*)?$/, '$1'); + window.history.replaceState(null, '', url); + } catch(e) {} + } + function isQueryHash(hash) { + return hash && ( (hash.length > 1 && hash.substring(0, 2) == 'q=') + || (hash.length > 2 && hash.substring(0, 3) == '#q=')); + } + + function createSlider(tocSidebarToggleButton, toolbar, headSpacerElement) { + + // create slider element + var slider = createElement(); + slider.id = 's'; + getElementById('m').insertBefore(slider, getElementById('c')); + var sliderWidth = slider.getBoundingClientRect().width; + var sliderHalfWidth = sliderWidth > 0 ? sliderWidth / 2 : 0; + + // create overlay required for smooth slider drag'n'drop + var overlay = createOverlay(); + + // TOC sidebar with its style and width + var tocWidth; + var tocSidebar = getElementById('t'); + var tocSidebarStyle = tocSidebar.style; + var headSpacerElementStyle = headSpacerElement.style; + var toolbarWidth = toolbar.getBoundingClientRect().width; + var tocSidebarMinimumWidth = TOC_SIDEBAR_MINIMUM_WIDTH > toolbarWidth ? TOC_SIDEBAR_MINIMUM_WIDTH : toolbarWidth; + + // slider movement + function move(e) { + tocWidth = (e.touches ? e.touches[0].clientX : e.pageX) - sliderHalfWidth; + if (tocWidth < 0) { + tocWidth = 0; + } + tocSidebarStyle.width = tocWidth + 'px'; + updateSpacerWidth(); + preventDefault(e); + } + function moveEnd(e) { + if (e.touches) { + addOrRemoveEventListener(0, 'touchmove', move, 1); + addOrRemoveEventListener(0, 'touchcancel', moveEnd); + addOrRemoveEventListener(0, 'touchend', moveEnd); + } else { + addOrRemoveEventListener(0, 'mousemove', move); + addOrRemoveEventListener(0, 'mouseup', moveEnd); + } + overlay.o(); + tocSidebarStyle.userSelect = ''; + if (tocWidth < tocSidebarMinimumWidth) { + var oldWidth = getCookie('toc-width'); + tocWidth = oldWidth ? oldWidth : TOC_SIDEBAR_DEFAULT_WIDTH; + toggleTocSidebar(); + } + setCookie(TOC_SIDEBAR_WIDTH_COOKIE_NAME, tocWidth); + preventDefault(e); + stopPropagation(e); + } + function moveStart(e) { + if (e.which && e.which != 1) return; + if (e.touches) { + addOrRemoveEventListener(1, 'touchend', moveEnd); + addOrRemoveEventListener(1, 'touchcancel', moveEnd); + addOrRemoveEventListener(1, 'touchmove', move, 1); + } else { + addOrRemoveEventListener(1, 'mouseup', moveEnd); + addOrRemoveEventListener(1, 'mousemove', move); + } + overlay.a(); + setClassName(tocSidebar, ''); + tocSidebarStyle.transition = ''; + headSpacerElementStyle.transition = ''; + preventDefault(e); + stopPropagation(e); + } + var documentElement = document.documentElement; + function addOrRemoveEventListener(add, event, fn, passive) { + if (add) { + documentElement.addEventListener(event, fn, passive ? { passive: false } : false); + } else { + documentElement.removeEventListener(event, fn, passive ? { passive: false } : false); + } + } + function updateSpacerWidth(hideToc, tocWidth, withTransition) { + if (withTransition) { + headSpacerElementStyle.transition = 'width .25s ease-in'; + } + var spacerWidth = (tocWidth || tocSidebar.getBoundingClientRect().right) + + sliderWidth + - toolbar.getBoundingClientRect().right; + var displayWidth = hideToc || spacerWidth < 0 ? 0 : spacerWidth; + headSpacerElementStyle.width = displayWidth + 'px'; + var widthPostfix = displayWidth >= LOGO_ICON_WIDTH ? (displayWidth >= LOGO_FULL_WIDTH ? 'f' : 1) : 0; + setClassName(headSpacerElement, 'k' + (embeddedMode ? 'e' : '') + widthPostfix); + } + + addEvent(slider, 'mousedown', moveStart); + addEvent(slider, 'touchstart', moveStart); + + // TOC sidebar toggling + function toggleTocSidebar(e, initialize, asSmallScreenAutoCloseFn) { + if (!asSmallScreenAutoCloseFn) { + preventDefault(e); + } + var isSmall = isSmallScreen(); + var currentClass = getClassName(tocSidebar); + if (asSmallScreenAutoCloseFn && (!isSmall || currentClass != 'show')) return; + var hideToc = isSmall ? currentClass == 'show' : tocWidth > 0; + if (initialize) { + tocWidth = -getCookie(TOC_SIDEBAR_WIDTH_COOKIE_NAME, TOC_SIDEBAR_DEFAULT_WIDTH); + hideToc = isSmall || tocWidth > 0; + } else { + tocSidebarStyle.transition = 'width .25s ease-in'; + headSpacerElementStyle.transition = 'margin-right .25s ease-in'; + } + setClassName(tocSidebar, isSmall ? (hideToc ? '' : 'show') : (hideToc ? 'hide' : '')); + if (initialize || !isSmall) { + tocWidth = -tocWidth; + if (!initialize) { + setCookie(TOC_SIDEBAR_WIDTH_COOKIE_NAME, tocWidth); + } + } + if (initialize || !isSmall) { + tocSidebarStyle.width = (hideToc ? 0 : tocWidth > tocSidebarMinimumWidth + ? tocWidth + : TOC_SIDEBAR_DEFAULT_WIDTH) + + 'px'; + } + tocSidebarStyle.userSelect = hideToc ? 'none' : ''; + if (!hideToc && tocSidebar.f) { + tocSidebar.f(); + } + if (!isSmall) { + updateSpacerWidth(hideToc, tocWidth, 1); + } + } + toggleTocSidebar(0, 1); + addEvent(slider, 'dblclick', toggleTocSidebar); + addEvent(tocSidebarToggleButton, 'click', toggleTocSidebar); + + // function to close TOC if screen is small + return function(e) { + toggleTocSidebar(e, 0, 1); + }; + + } + + function tocContentProvider(node, processChildrenFn) { + var callbackUrl = BASE_URL + 'advanced/tocfragment' + + (node + ? (node.toc ? '?toc=' + node.toc : '') + + (node.path ? '&path=' + node.path : '') + + (node.topic ? '?errorSuppress=true&topic=' + node.topic : '') + + (node.expand ? '?errorSuppress=true&expandPath=' + node.expand : '') + : ''); + remoteRequest(callbackUrl, function(responseText) { + var nodes = tocXmlNodes(parseXml(responseText), node ? node.toc : 0, node ? node.path : 0); + var children; + for (var i = 0; i < nodes.length; i++) { + var n = nodes[i]; + if (n.tagName == 'numeric_path') { + tocContentProvider({expand: getAttribute(n, 'path')}, processChildrenFn); + return; + } + if (n.tagName == 'node') { + children = tocToNodes(nodes, node ? node.toc : 0, node ? node.expand : 0); + break; + } + } + if (!node) { + createSearchField(); + initContentPage(); + setFontSize(0, 1); + } + processChildrenFn(children); + }); + } + function tocXmlNodes(xml, toc, path) { + var books = xml.documentElement.childNodes; + if (!toc) return books; + var book; + for (var i = 0; i < books.length; i++) { + book = books[i]; + if (book.tagName == 'node' && toc == book.getAttribute('id')) { + if (!path) return book.childNodes; + break; + } + } + var nodes = book.childNodes; + tocLevelLoop: while (1) { + for (var i = 0; i < nodes.length; i++) { + n = nodes[i]; + if (n.tagName != 'node') continue; + var id = n.getAttribute('id'); + if (path == id) return n.childNodes; + if ( id + && path.length > id.length + && path.substring(0, id.length + 1) == id + '_') { + nodes = n.childNodes; + continue tocLevelLoop; + } + } + break; + } + return []; + } + function tocToNodes(xmlChildren, toc, expandPath) { + var children = []; + for (var i = 0; i < xmlChildren.length; i++) { + var n = xmlChildren[i]; + if (n.tagName != 'node') continue; + var currentToc = toc ? toc : getAttribute(n, 'id'); + children.push({ + n/*node*/: { + toc: currentToc, + path: toc ? getAttribute(n, 'id') : 0, + t: getAttribute(n, 'title'), + h: BASE_URL + getAttribute(n, 'href').substring(3), + i: n.getAttribute('image'), + y: expandPath, + l/*is leaf*/: getAttribute(n, 'is_leaf') + }, + l/*is leaf*/: getAttribute(n, 'is_leaf'), + c/*children*/: tocToNodes(n.childNodes, currentToc) + }); + } + return children; + } + + function isSmallScreen() { + var clientWidth = document.documentElement.clientWidth || document.body.clientWidth; + return clientWidth <= SMALL_SCREEN_WIDTH; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Search: search-as-you-type ('t') and full search ('f') + + function createSearchField() { + var currentTocLi; + + // create overlay required for closing proposals drop-down even when clicking into the content iframe + var overlay = createOverlay(); + addEvent(overlay, 'click', hideProposals); + + // area (containing scope drop-down, search field and button) + // "searchFieldAreaWrapper" as workaround for sub-pixel problem in Firefox (1px border might become 0.8px border + // to align real pixels on high-DPI to CSS px), otherwise proposals drop-down might not correctly aligned with + // search field area + var searchFieldAreaWrapper = createElement(getElementById('h'), 0, 'q0'); + var searchFieldArea = createElement(createElement(searchFieldAreaWrapper, 0, 'q1'), 'form', 'q'); + + if (embeddedMode) { + createButton(searchFieldAreaWrapper, BOOKMARKS_ICON, BOOKMARKS_DESCRIPTION, function() { + bookmarksPage.s(!bookmarksPage.o); + }); + } + + var searchFieldAreaHasFocus; + var searchFieldAreaContainsQuery; + var proposals; + function updateSearchFieldAreaClass() { + setClassName(searchFieldArea, + 'q' + (proposals.style.display == 'block' ? 'm' : (searchFieldAreaHasFocus ? 'f' : '')) + + (searchFieldAreaContainsQuery ? ' qa' : '') + ); + } + createMenu(); + + // scopes drop-down + var scopeButtonWrapper = createElement(searchFieldArea, 0, 's0'); + + var booksButton = createElement(scopeButtonWrapper, 'button', 's'); + setAttribute(booksButton, 'type', 'button'); + booksButtonText = createElement(booksButton, 'span'); + var dropDownHandle = createElement(booksButton, 'span', 'de'); + setInnerHtml(dropDownHandle, TREE_HANDLE); + var booksDropDown = createElement(scopeButtonWrapper, 0, 'u'); + booksDropDown.s = function(show) { + var isOpen = booksDropDown.style.display == 'block'; + if (!isOpen == !show) return; + booksDropDown.style.display = show ? 'block' : 'none'; + if (!show) return; + setInnerHtml(booksDropDown); + var booksDropDownUl = createElement(booksDropDown, 'ul', 'r'); + var menuItems = []; + var dataItems = []; + var currentBook; + var currentChapter; + for (var tocLi = currentTocLi; tocLi && tocLi.n; tocLi = tocLi.p) { + if (!tocLi.n.path) { + currentBook = tocLi.n.t; + if (!currentChapter) { + currentChapter = currentBook; + } + } else if (!currentChapter && !tocLi.n.l) { + currentChapter = tocLi.n.t; + } + } + menuItems.push(createElement(booksDropDownUl, 'li', searchScope.l == 0 ? 'x': 0, SEARCH_SCOPE_LABEL_NONE)); + dataItems.push([0]); + menuItems.push(createElement(booksDropDownUl, 'li', searchScope.l == 1 ? 'x': 0, SEARCH_SCOPE_LABEL_BOOK + (currentBook ? ': ' + currentBook : ''))); + dataItems.push([1]); + menuItems.push(createElement(booksDropDownUl, 'li', searchScope.l == 2 ? 'x': 0, SEARCH_SCOPE_LABEL_CHAPTER + (currentChapter ? ': ' + currentChapter : ''))); + dataItems.push([2]); +// menuItems.push(createElement(booksDropDownUl, 'li', searchScope.l == 3 ? 'x': 0, SEARCH_SCOPE_LABEL_TOPIC)); +// dataItems.push([3]); + remoteRequest(BASE_URL + 'advanced/workingSetManager.jsp?t=' + Date.now(), function(menuItems, dataItems) { + return function(responseText) { + var delimiter = 'l '; + var scopeIndex = 0; + SEARCH_SCOPE_ALL_PATTERN.lastIndex = 0; + for (var match; (match = SEARCH_SCOPE_ALL_PATTERN.exec(responseText)) != null;) { + var label = decodeHtml(match[1]); + if (label.substring(0, 1) == '\u200B') continue; + var className = delimiter + (searchScope.l == 4 && searchScope.s == label ? 'x' : ''); + menuItems.push(createElement(booksDropDownUl, 'li', className, label)); + delimiter = ''; + dataItems.push([4, label, scopeIndex]); + scopeIndex++; + } + menuItems.push(createElement(booksDropDownUl, 'li', 'l y', 'Scopes...')); + dataItems.push([5]); + toMenu(booksButton, menuItems, dataItems, setSearchScope); + } + }(menuItems, dataItems)); + } + booksDropDown.s(); + var scopeOverlay = createOverlay(4); + addEvent(scopeOverlay, 'click', function() { booksDropDown.s(); scopeOverlay.o(); }); + addEvent(booksButton, 'mousedown', function(e) { + var isOpen = booksDropDown.style.display == 'block'; + try { + booksButton.focus(); + } catch(e) {} + booksDropDown.s(!isOpen); + if (isOpen) { + scopeOverlay.o(); + } else { + scopeOverlay.a(); + } + preventDefault(e); + stopPropagation(e); + }); + addEvent(booksButton, 'click', function(e) {stopPropagation(e)}); + addEvent(booksButton, 'focus', function() { + booksDropDown.hasFocus = true; + booksDropDown.s(1); + scopeOverlay.a(); + }); +// addEvent(booksButton, 'blur', function() { +// booksDropDown.hasFocus = false; +// setTimeout(function() {if (!booksDropDown.hasFocus) booksDropDown.style.display = 'none'}, 200); +// }); + if (!embeddedMode && BOOK_SCOPE_BY_DEFAULT && searchScope.l == 0 && getCookie(BOOK_SCOPE_COOKIE) != 'init') { + setCookie(BOOK_SCOPE_COOKIE, 'init'); + setTimeout(function(){ if(setSearchScope) setSearchScope([1]); }, 420); + } else { + updateScopeButtonLabel(); + } + + setSearchScope = function(scopeData) { + booksDropDown.s(); + scopeOverlay.o(); + if (scopeData[0] == 5) { + scopesPage.s(1); + return; + } + if (searchScope.l != scopeData[0]) { + var scopeToRemove; + if (searchScope.l == 1 || searchScope.l == 2 || searchScope.l == 4) { + scopeToRemove = '%E2%80%8B' + (searchScope.l > 1 ? '%E2%80%8B' : ''); + for (var i = 0; searchScope.l == 4 && i <= searchScope.t; i++) { + scopeToRemove += '%E2%80%8B'; + } + } + var scopeToAdd; + if (scopeData[0] == 1 || scopeData[0] == 2 || scopeData[0] == 4) { + scopeToAdd = '%E2%80%8B' + (scopeData[0] > 1 ? '%E2%80%8B' : ''); + for (var i = 0; scopeData[0] == 4 && i <= scopeData[2]; i++) { + scopeToAdd += '%E2%80%8B'; + } + } + if (scopeToRemove || scopeToAdd) { + remoteRequest( BASE_URL + + 'workingSetState.jsp?operation=' + + (scopeToRemove && scopeToAdd ? 'edit' : (scopeToRemove ? 'remove' : 'add')) + + (scopeToRemove && scopeToAdd ? '&oldName=' + scopeToRemove : '') + + '&workingSet=' + + (!scopeToAdd ? scopeToRemove : scopeToAdd) + + '&t=' + Date.now()); + } + } + searchScope.l = scopeData[0]; + searchScope.s = searchScope.l == 4 ? scopeData[1] : 0; + searchScope.t = searchScope.l == 4 ? scopeData[2] : 0; + updateScopeButtonLabel(); + } + updateScopeByToc = function(li) { + currentTocLi = li; + updateScopeButtonLabel(); + } + function updateScopeButtonLabel() { + setInnerHtml(booksButtonText, ''); + if (searchScope.l == 0 || (searchScope.l < 3 && !currentTocLi)) return; + var newButtonlabel; + for (var tocLi = currentTocLi; searchScope.l < 3 && tocLi && tocLi.n; tocLi = tocLi.p) { + if (!tocLi.n.path || (searchScope.l == 2 && !tocLi.n.l)) { + newButtonlabel = tocLi.n.t; + break; + } + } + if (searchScope.l == 4) { + newButtonlabel = searchScope.s; + } + if (newButtonlabel) { + booksButtonText.appendChild(document.createTextNode(BOOK_NAME_SHORTENER(newButtonlabel))); + } + } + + // search field + var wrap = createElement(searchFieldArea, 0, 'q2'); + wrap.style.position = 'relative'; + var searchField = createElement(wrap, 'input'); + searchField.id = 'q'; + searchField.type = 'text'; + searchField.alt = SEARCH_FIELD_DESCRIPTION; + searchField.title = SEARCH_FIELD_DESCRIPTION; + searchField.autocomplete = 'off'; + searchField.placeholder = SEARCH_FIELD_PLACEHOLDER; + addEvent(searchField, 'input', search); // for IE 8 do also on 'propertychange' + addEvent(searchField, 'focus', search); + + var searchButton = createElement(searchFieldArea, 'button', 'b'); + setInnerHtml(searchButton, SEARCH_ICON); + addEvent(searchFieldArea, 'submit', function(e) {preventDefault(e); search(e, 1);}); + + // hint + var hintField = createElement(wrap, 'input', 'qh'); + if (searchField.nextSibling) { + searchField.parentNode.insertBefore(hintField, searchField.nextSibling); + } + searchField.style.position = 'relative'; + hintField.style.position = 'absolute'; + hintField.style.left = 0; + hintField.style.top = 0; + hintField.style.height = '100%'; + hintField.style.width = '100%'; + hintField.style.background = 'transparent'; + hintField.style.borderColor = 'transparent'; + hintField.setAttribute('disabled', 'disabled'); + wrap.appendChild(searchField); + searchField.style.background = 'url("data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw%3D%3D") repeat'; + + var searchFieldAreaElements = [booksButton, searchField, searchButton]; + for (var i = 0; i < searchFieldAreaElements.length; i++) { + addEvent(searchFieldAreaElements[i], 'focus', function() { + searchFieldAreaHasFocus = 1; + updateSearchFieldAreaClass(); + }); + addEvent(searchFieldAreaElements[i], 'blur', function() { + searchFieldAreaHasFocus = 0; + updateSearchFieldAreaClass(); + }); + } + + // proposals drop-down + proposals = createElement(searchFieldArea, 0, 'p'); + addEvent(proposals, 'click', function(e) {stopPropagation(e)}); + function showProposals() { + proposals.style.display = 'block'; + overlay.a(); + updateSearchFieldAreaClass(); + } + function hideProposals() { + proposals.style.display = 'none'; + overlay.o(); + updateSearchFieldAreaClass(); + } + hideProposals(); + + // focus search field + searchField.focus(); + + function search(e, fullSearch) { + var noPendingQueries = !currentSearch[getSearchTypeId(fullSearch)]; + + // get search word, query, URL and remember query to detect stale responses + var searchWord = searchField.value + + // trim + .replace(/(^\s+|\s+$)/ig, '') + + // TODO if Eclipse bug 351077 (https://bugs.eclipse.org/351077), remove following line + .replace(/\-([^\-\s]*$)/ig, ' $1'); + + var tocScope = currentTocLi ? currentTocLi.n : currentTocLi; + for (var tocLi = currentTocLi; searchScope.l < 3 && tocLi && tocLi.n; tocLi = tocLi.p) { + if (!tocLi.n.path || (searchScope.l == 2 && !tocLi.n.l)) { + tocScope = tocLi.n; + break; + } + } + var query = searchWord.length + ? ( encodeURIComponent(searchWord.toLowerCase()) + + ((searchScope.l == 1 || searchScope.l == 2) && tocScope && tocScope.toc ? '&toc=' + encodeURIComponent(tocScope.toc) : '') + + (searchScope.l == 2 && tocScope && tocScope.path ? '&path=' + tocScope.path : '') + + (searchScope.l == 4 && searchScope.s ? '&scope=' + encodeURIComponent(searchScope.s) : '')) + : ''; + var url = SEARCH_BASE_URL + + query.replace(/(\&|$)/, (fullSearch ? '' : '*') + '$1') + + '&maxHits=' + + (fullSearch ? SEARCH_HITS_MAX : SEARCH_AS_YOU_TYPE_PROPOSAL_MAX) + + (query.indexOf('&toc=') < 0 ? '' : '&quickSearch=true&quickSearchType=QuickSearchToc'); + currentSearch[getSearchTypeId(fullSearch)] = query; + if (fullSearch) { + currentSearch['t'] = 0; + hideProposals(); + if (bookmarksPage) bookmarksPage.s(); + if (scopesPage) scopesPage.s(); + updateDeepLink(query); + } else { + + // hide hint + hintField.value = ''; + + } + + // empty search? + searchFieldAreaContainsQuery = searchWord.length; + updateSearchFieldAreaClass(); + if (!searchWord.length) { + if (!fullSearch) { + hideProposals(); + } + return; + } + + // init UI + if (fullSearch) { + if (searchPage.o && query == searchPage.q) return; + searchPage.s(1); + if (query == searchPage.q) { + window.frames.c.location = url; + return; + } + setInnerHtml(searchPage, 'Searching...'); + searchPage.scrollTop = 0; + } else if (query == proposals.q) { + preventDefault(e); + showProposals(); + return; + } + + // cached? + var cache = SEARCH_CACHE[getSearchTypeId(fullSearch)]; + for (var i = 0; i < cache.length; i++) { + var r = cache[i]; + if (query == r.q) { + if (fullSearch) { + window.frames.c.location = url; + } + renderResults(fullSearch, r.r, r.b, query, searchWord, r.s); + return; + } + } + + // submit query to server + if (fullSearch) { + window.frames.c.location = url; + return; + } + var currentSearchScope = { + /* level */ l: searchScope.l, + /* (custom) scope name */ s: searchScope.s, + /* path */ p: 0 + }; + if (searchScope.l > 0 && searchScope.l < 3 && currentTocLi) { + currentSearchScope.p = [currentTocLi.n]; + for (var parentLi = currentTocLi.p; searchScope.l > 1 && parentLi && parentLi.n; parentLi = parentLi.p) { + currentSearchScope.p.unshift(parentLi.n); + } + } + var callbackFn = callbackFor(fullSearch, query, searchWord, currentSearchScope); + if (noPendingQueries) { + remoteRequest(url, callbackFn, getSearchTypeId(fullSearch)); + } else { + setTimeout(function() { + + // remote request if and only if not staled/outdated + if (query == currentSearch[getSearchTypeId(fullSearch)]) + remoteRequest(url, callbackFn, getSearchTypeId(fullSearch)); + + }, SEARCH_DELAY_IN_MILLISECOND); + } + } + + renderFullSearch = function(queryPart, data) { + var query = queryPart.substring(0, queryPart.indexOf('&maxHits=')); + updateDeepLink(query); + if (query == searchPage.q) return; + var valuePairs = queryPart.split('&'); + var searchWord; + var toc; + var path; + var scope; + for (var i = 0; i < valuePairs.length; i++) { + if (!searchWord && valuePairs[i].indexOf('=') < 0) { + searchWord = decodeURIComponent(valuePairs[i]); + } else if (valuePairs[i].substring(0, 4) == 'toc=') { + toc = decodeURIComponent(valuePairs[i].substring(4)); + } else if (valuePairs[i].substring(0, 5) == 'path=') { + path = decodeURIComponent(valuePairs[i].substring(5)); + } else if (valuePairs[i].substring(0, 6) == 'scope=') { + scope = decodeURIComponent(valuePairs[i].substring(6)); + } + } + var currentSearchScope = { + /* level */ l: scope ? 4 : (toc ? (path ? 2 : 1) : 0), + /* (custom) scope name */ s: scope, + /* path */ p: 0 + }; + var scopeFound = !toc; + if (toc && currentTocLi && currentTocLi.n) { + var nodes = [currentTocLi.n]; + for (var parentLi = currentTocLi.p; parentLi && parentLi.n; parentLi = parentLi.p) { + if (path) { + nodes.unshift(parentLi.n); + } else { + nodes = [parentLi.n]; + } + } + if (toc == nodes[0].toc && (!path || path == nodes[nodes.length - 1].path)) { + currentSearchScope.p = nodes; + scopeFound = 1; + } + } + if (!scopeFound) { + remoteRequest( BASE_URL + 'advanced/tocfragment?toc=' + encodeURIComponent(toc) + + (path ? '&path=' + path : ''), (function(toc, path, currentSearchScope, searchWord, query, data) { + return function(responseText) { + var nodePath = []; + var books = parseXml(responseText).documentElement.childNodes; + var book; + for (var i = 0; i < books.length; i++) { + book = books[i]; + if (book.tagName == 'node' && toc == book.getAttribute('id')) { + nodePath.push({t: book.getAttribute('title'), toc: toc}); + break; + } + } + var nodes = book.childNodes; + tocLevelLoop: while (path) { + for (var i = 0; i < nodes.length; i++) { + n = nodes[i]; + if (n.tagName != 'node') continue; + var id = n.getAttribute('id'); + if (path == id) { + nodePath.push({t: n.getAttribute('title'), toc: toc, path: id}); + break tocLevelLoop; + }; + if ( id + && path.length > id.length + && path.substring(0, id.length + 1) == id + '_') { + nodes = n.childNodes; + nodePath.push({t: n.getAttribute('title'), toc: toc, path: id}); + continue tocLevelLoop; + } + } + break; + } + currentSearchScope.p = nodePath; + (callbackFor(1, query, searchWord, currentSearchScope))(data); + };})(toc, path, currentSearchScope, searchWord, query, data)); + return; + } + (callbackFor(1, query, searchWord, currentSearchScope))(data); + } + + function callbackFor(fullSearch, query, searchWord, searchScope) { + return function(data) { + + // indexing in progress? + var match = SEARCH_RESULTS_INDEXING_PATTERN.exec(data); + if (match != null) { + if (fullSearch) { + setInnerHtml(searchPage, 'Indexing... ' + match[1] + '%'); + } + return; + } + + // parse HTML for results + var element = createElement(); + var hasBreadcrumbs = 0; + var results = []; + + SEARCH_RESULTS_PATTERN.lastIndex = 0; + for (var match; (match = SEARCH_RESULTS_PATTERN.exec(data)) != null;) { + var items = []; + for (var i = 2; i < 8; i++) { + element.innerHTML = match[i]; + items.push((element.textContent ? element.textContent : element.innerText).replace(/^\s+|\s+$/g,'').replace(/\s+/g,' ')); + } + var breadcrumb = []; + if (match[6]) { + SEARCH_RESULTS_BREADCRUMB_SNIPPET_PATTERN.lastIndex = 0; + for (var breadcrumbMatch; (breadcrumbMatch = SEARCH_RESULTS_BREADCRUMB_SNIPPET_PATTERN.exec(match[6])) != null;) { + for (var i = 1; i < 3; i++) { + element.innerHTML = breadcrumbMatch[i]; + breadcrumb.push((element.textContent ? element.textContent : element.innerText).replace(/^\s+|\s+$/g,'').replace(/\s+/g,' ')); + } + } + hasBreadcrumbs = 1; + } + var hrefFollowedByTitle = 'href' == match[1]; + results.push({ + /* title */ t: items[3], + /* description */ d: items[5], + /* href */ h: items[hrefFollowedByTitle ? 0 : 2].substring(8), + /* breadcrumb */ b: match[6] ? breadcrumb : [0, items[hrefFollowedByTitle ? 2 : 0]] + }); + } + + // cache parsed results + var queryResult = { + /* results */ r: results, + /* has breadcrumbs */ b: hasBreadcrumbs, + /* query */ q: query, + /* search scope */ s: searchScope + } + var cache = SEARCH_CACHE[getSearchTypeId(fullSearch)]; + var cacheIndexId = getSearchTypeId(fullSearch) + 'i'; + var cacheSize = fullSearch ? SEARCH_FULL_SEARCH_CACHE_SIZE : SEARCH_AS_YOU_TYPE_CACHE_SIZE; + SEARCH_CACHE[cacheIndexId] = (SEARCH_CACHE[cacheIndexId] + 1) % cacheSize; + if (cache.length < cacheSize) { + cache.push(queryResult); + } else { + cache[SEARCH_CACHE[cacheIndexId]] = queryResult; + } + + renderResults(fullSearch, results, hasBreadcrumbs, query, searchWord, searchScope); + } + + } + + function renderResults(fullSearch, results, hasBreadcrumbs, query, searchWord, searchScope) { + + // staled? + if (!fullSearch && query != currentSearch[getSearchTypeId(fullSearch)]) return; + + // show results + var items = []; + var data = []; + var filters = []; + var filterValues = []; + function applyFilters(e) { + var includeFilters = []; + var excludeFilters = []; + for (var i = 0; i < filters.length; i++) { + var f = filters[i]; + if (!f.checked && !f.indeterminate) excludeFilters.push(filterValues[i]); + if (f.checked && !f.indeterminate) includeFilters.push(filterValues[i]); + } + for (var i = 0; i < items.length; i++) { + items[i].style.display = arrayContainsPrefix(includeFilters, data[i]) + && !arrayContainsPrefix(excludeFilters, data[i]) + ? 'block' + : 'none'; + } + stopPropagation(e); + } + + var searchScopeLabel = searchScope.l > 3 ? searchScope.s : ''; + if (searchScope.p) { + for (var i = 0; i < searchScope.p.length; i++) { + if (i > 0) { + searchScopeLabel += ' > '; + } + searchScopeLabel += searchScope.p[i].t; + } + } + var parentElement = fullSearch ? searchPage : proposals; + setInnerHtml(parentElement, ''); + parentElement.q = query; + parentElement.l = 'Search' + (searchScope.l > 0 ? ' (' + searchScopeLabel+ ')': '') + ': ' + searchWord; + if (fullSearch) { + document.title = searchPage.l + ' - ' + title; + + // no results? + if (!results.length) { + var noResults = createElement(searchPage, 0, 'r0', 'No results found for '); + createElement(noResults, 'strong', 0, searchWord); + return; + } + + // filter tree + var filterTree = asTree(results, [], 9, true); + // TODO correction of tree for deeper scopes (below) might be done in "asTree" or "asTree" might be better simplified + if (searchScope.p && filterTree.length == 1 && filterTree[0].isNode) { + var afterCutOff = searchScope.p.length * 2 - filterTree[0].name.length; + if (afterCutOff < 0) { + filterTree[0].name = filterTree[0].name.slice(searchScope.p.length * 2); + } else if (afterCutOff >= 0) { + filterTree = filterTree[0].children; + if (afterCutOff > 0 && filterTree.length > 0 && filterTree[0].isNode) { + if (filterTree[0].name.length <= afterCutOff) { + filterTree = filterTree[0].children; + } else { + filterTree[0].name = filterTree[0].name.slice(afterCutOff); + } + } + } + } + createTree(searchPage, + + // content provider + function(node, processChildrenFn) { + if (!node) { + processChildrenFn([{ n/*ode*/: {children: filterTree}, l/*eaf*/: 0 }], 1); + return; + } + var children = []; + for (var i = 0; i < (node.isNode ? node.children.length : filterTree.length); i++) { + var childNode = node ? node.children[i] : filterTree[i]; + if (childNode.isNode) { + var isLeaf = 1; + for (var j = 0; j < childNode.children.length; j++) { + if (childNode.children[j].isNode) { + isLeaf = 0; + break; + } + } + children.push({ n/*ode*/: childNode, l/*eaf*/: isLeaf }); + childNode.p/*arent*/ = node; + } + } + processChildrenFn(children); + }, + + // label provider + function(li, node) { + var isRoot = !node.isNode; + + // checkbox + var checkboxWithLabel = createElement(li); + var checkbox = createElement(checkboxWithLabel, 'input'); + checkbox.type = 'checkbox'; + checkbox.checked = node.p ? node.p.x.checked : true; + node.x = checkbox; + if (node.p) { + checkbox.parentCheckbox = node.p.x; + } + checkbox.numberOfResults = isRoot ? results.length : node.count; + filters.push(checkbox); + filterValues.push(isRoot ? '' : toValue(node.l.concat(node.name))); + + // label + var labelText = ''; + if (isRoot) { + checkbox.style.display = 'none'; + labelText = 'Results ' + (searchScope && searchScope.l > 0 ? 'in ' : ''); + } else { + addEvent(checkbox, 'click', (function(liCheck, li) { + return function() { + selectSubtree(li, liCheck.checked); + updateParentsChecks(liCheck); + applyFilters(); + }; + })(checkbox, li)); + for (var i = 0; i < node.name.length; i+=2) { + labelText += (i == 0 ? '' : ' > ') + node.name[i+1]; + } + } + var label = createElement(checkboxWithLabel, 'span', node.isNode ? 0 : 't', labelText + ' '); + if (isRoot && searchScope && searchScope.l > 0 ) { + createElement(label, 'span', 'tl', searchScopeLabel + ' '); + } + createElement(label, 'span', 'count', checkbox.numberOfResults); + addEvent(label, 'click', (function(checkbox, li) { + return function() { + var root; + for (root = checkbox; root.parentCheckbox; root = root.parentCheckbox); + for (var i = 0; i < 5; i++) { + root = getParentElement(root); + if (root.tagName == 'UL') break; + } + selectSubtree(root, false); + selectSubtree(li, true); + updateParentsChecks(checkbox); + applyFilters(); + }; + })(checkbox, li)); + + return checkboxWithLabel; + }, + 0); + + } + var resultList = createElement(parentElement, 'ol', 'j'); + if (!fullSearch) { + + // no results? + if (!results.length) return; + + // hint + var wordBeginRegEx = queryToRegEx(query); + var newHints = {}; + for (var i = 0; i < results.length && searchWord.length < 36; i++) { + var match = wordBeginRegEx.exec(results[i].t/*title*/); + if (match) { + var pHint = match[0].toLowerCase(); + newHints[pHint] = (newHints[pHint] ? newHints[pHint] : 0) + + (1 + (results.length - i) / results.length) / results.length; + } + match = wordBeginRegEx.exec(results[i].d/*description*/); + if (match) { + var pHint = match[0].toLowerCase(); + newHints[pHint] = (newHints[pHint] ? newHints[pHint] : 0) + + (0.7 + (results.length - i) / results.length) / results.length; + } + } + var allHints = []; + for (var i in newHints) { + if (newHints[i] < 1.8 / results.length) continue; + allHints.push(newHints[i].toFixed(7) + i); + } + allHints.sort().reverse(); + hintField.value = allHints.length > 0 + ? searchField.value + wordBeginRegEx.exec(allHints[0].substring(9))[1] + : ''; + + // query proposals + for (var i = 0; i < allHints.length && i < 3; i++) { + var hintText = allHints[i].substring(9); + var li = createElement(resultList, 'li'); + var button = createElement(li, 'button'); + var spacerElementStyle = createElement(button, 'span').style; + spacerElementStyle.display = 'inline-block'; + spacerElementStyle.width = booksButton.offsetWidth + 'px'; + createElement(button, 'span', null, hintText.substring(0, searchWord.length)); + createElement(button, 'strong', null, hintText.substring(searchWord.length)); + items.push(li); + data.push([hintText]); + } + + } + + function toValue(path) { + var result = ''; + for (var i = 0; i < path.length; i++) result += (i > 0 ? '\n' : '') + path[i]; + return result; + } + + // list results + for (var i = 0; i < results.length; i++) { + var node = results[i]; + var li = createElement(resultList, 'li'); + var a = createElement(li, 'a'); + a.href = BASE_URL + 'topic' + node.h/*href*/; + a.target = 'c'; + var titleAndLocation = createElement(a, 0, 'm'); + + // title + addHighlightedText(createElement(titleAndLocation, 0, 'v'), node.t/*title*/, searchWord); + + // show book title only for no book/chapter scope + if (!fullSearch && !searchScope.p) { + createElement(titleAndLocation, 0, 'w', node.b/*breadcrumb*/[1]); + } + + // breadcrumb + if (fullSearch && hasBreadcrumbs && node.b/*breadcrumb*/) { + var location = createElement(titleAndLocation, 0, 'w'); + for (var j = searchScope && searchScope.p ? searchScope.p.length * 2 : 0; j < node.b/*breadcrumb*/.length; j+=2) { + createElement(location, 'span', 0, node.b/*breadcrumb*/[j+1]); + if (j < node.b/*breadcrumb*/.length-2) { + createElement(location, 'span', 0, ' > '); + } + } + } + + // description + addHighlightedText(createElement(a, 0, 'n'), node.d/*description*/, searchWord); + + // UI element and corresponding data + items.push(li); + if (fullSearch) { + var resultofStart = node.h/*href*/.indexOf('?resultof='); + var hrefNormed = '../topic' + (resultofStart < 0 ? node.h/*href*/ : node.h/*href*/.substring(0, resultofStart)); + data.push(toValue(node.b/*breadcrumb*/.slice().concat(hrefNormed).concat(node.t/*title*/))); + } else { + data.push([node.t/*title*/, node.h/*href*/]); + } + + } + + // add key support (and show proposals) + if (fullSearch) { +// toMenu(searchField, items, results, function(d) { +// getElementById('c').src = BASE_URL + 'topic' + d.h/*href*/; +// }, +// 0, +// 0, +// function(item, data, viaMouse) { +// if (!viaMouse) { +// scrollIntoViewIfNeeded(searchPage, item); +// } +// }, +// 1); + } else { + + // key support + toMenu(searchField, items, data, function(d) { + + // apply hint + if (d.length < 2) { + hintField.value = ''; + searchField.value = d; + search(); + return; + } + + // show search result + var searchWord = d[0]; + var toc; + var tocStart = searchWord.indexOf('&toc='); + if (tocStart > 0) { + toc = decodeURIComponent(searchWord.substring(tocStart + 5)); + searchWord = searchWord.substring(0, tocStart); + } + if (searchSearchWord(searchWord + '*', toc, d[1], false, true)) return; + getElementById('c').src = BASE_URL + 'topic' + d[1]; + hideProposals(); + + }, + function(d, key) { + + // empty search field? + if (!searchField.value) return false; + + // ignore RIGHT (key: 39) if cursor not at the end + if ( key == 39 + && searchField + && searchField.selectionStart + && searchField.value + && searchField.value.length != searchField.selectionStart) + return false; + + if (d && d.length > 0 && d[0].length < 2) { + searchField.value = d[0][0]; + search(); + return true; + } + return false; + }, + hideProposals, + function(a, b) { + if (b.length < 2 || a.armed) return; + a.armed = true; + var iFrame = createElement(a, 'iframe', 'f'); + iFrame.frameBorder = 0; + + // TODO handle absolute paths + iFrame.src = BASE_URL + 'topic' + b[1]; + }); + + // show proposals + showProposals(); + + } + + // done (no pending queries) + if (query == currentSearch[getSearchTypeId(fullSearch)]) { + currentSearch[getSearchTypeId(fullSearch)] = 0; + } + + } + + function getSearchTypeId(fullSearch) { + return fullSearch ? 'f' : 't'; + } + + function asTree(results, path, depth) { + if (depth < 1) return results; + var tree = []; + var grouped = {}; + for (var i = 0; i < results.length; i++) { + var r = results[i]; + r.p = i; + r.q = r.b/*breadcrumb*/.slice(); + var resultofStart = r.h/*href*/.indexOf('?resultof='); + r.q.push('../topic' + (resultofStart < 0 ? r.h/*href*/ : r.h/*href*/.substring(0, resultofStart))); + r.q.push(r.t/*title*/); + + // child? + if (!r.b/*breadcrumb*/ || r.b/*breadcrumb*/.length <= path.length) { + tree.push(r); + continue; + } + + // not child -> contained in a subtree + var key = r.b/*breadcrumb*/[path.length] + '\n' + r.b/*breadcrumb*/[path.length+1]; + if (!grouped[key]) { + var node = { + isNode: true, + name: [r.b/*breadcrumb*/[path.length], r.b/*breadcrumb*/[path.length+1]], + l/*ocation*/: path.slice(), + children: [r] + }; + grouped[key] = node; + tree.push(node); + } else { + grouped[key].children.push(r); + } + + } + + // calculate count and set the root (if it exists) + for (var i = 0; i < tree.length; i++) { + var r = tree[i]; + if (r.isNode) { + r.count = r.children.length + (r.root ? 1 : 0); + continue; + } + var rootOfGroup = grouped[r.q[r.q.length-2] + '\n' + r.t/*title*/]; + if (rootOfGroup) { + rootOfGroup.children.push(r); + rootOfGroup.count++; + tree.splice(i,1); + i--; + } + } + + // compact and recursion + for (var i = 0; i < tree.length; i++) { + var r = tree[i]; + if (!r.isNode) continue; + compact(path, r); + r.children = asTree(r.children, r.children[0].q.slice(0, path.length + r.name.length), depth - 1); + } + + return tree; + } + function compact(path, r) { + for (var i = path.length+2; r.children.length > 0 && !r.root; i+=2) { + if (r.children.length == 1 && r.children[0].b/*breadcrumb*/.length == i) return; + for (var j = 0; j < r.children.length; j++) { + var p0 = r.children[0].q; + var p = r.children[j].q; + if ( p.length < i+1 + || p[i] != p0[i] + || p[i+1] != p0[i+1]) return; + } + var p0 = r.children[0].q; + r.name.push(p0[i]); + r.name.push(p0[i+1]); + } + } + + function queryToRegEx(query) { + query = query.indexOf('&') < 0 + ? query + : query.substr(0, query.indexOf('&')); + query = decodeURIComponent(query.replace(/\+/g, '%20')); + query = query.replace(/([\.\?\*\+\-\(\)\[\]\{\}\\])/g, '\\$1'); + return new RegExp("\\b(?:" + query + ")((?:\\w+|\\W+\\w+))", "i"); + } + + function searchSearchWord(searchWord, toc, href, path, isSearchWordDecoded) { + try { + + // No SearchFrame or no NavFrame? -> exception handling + var root = parent.parent.parent; + var searchFrame = root.HelpToolbarFrame.SearchFrame; + var navFrame = root.HelpFrame.NavFrame; + + var scopeElement = searchFrame.document.getElementById('scope'); + var searchInput = searchFrame.document.getElementById('searchWord'); + if ( searchInput + && scopeElement + && scopeElement.firstChild.nodeValue == 'All topics') { + + // no scope -> update top left search input field only + searchInput.value = searchWord; + + } else { + + // disable scope and update top left search input field + searchFrame.location.replace( BASE_URL + + 'scopeState.jsp?workingSet=&searchWord=' + + encodeURIComponent(searchWord)); + + } + var newNavUrl = BASE_URL + + 'advanced/nav.jsp?e=h&tab=search&searchWord=' // 'e=h' for tracking (to distinguish normal queries from queries done with this script) + + (isSearchWordDecoded ? searchWord : encodeURIComponent(searchWord)); + if (toc) newNavUrl += '&quickSearch=true&quickSearchType=QuickSearchToc&toc=' + encodeURIComponent(toc); + if (path) newNavUrl += '&path=' + path; + navFrame.location.replace(newNavUrl); + + // topic (use 'setTimeout()' otherwise in Internet Explorer + // 'Go Back' does not work sometimes) + if (href) { + setTimeout(function(){window.location.href = BASE_URL + 'topic' + href}, 9); + } + + return true; + } catch(e) { + return false; + } + } + + // filter tree functions + function selectSubtree(element, checkStatus) { + for (var i = 0; i < element.children.length; i++) { + var n = element.children[i]; + if ('UL' == n.tagName || 'LI' == n.tagName || 'DIV' == n.tagName) { + selectSubtree(n, checkStatus); + } else if ('INPUT' == n.tagName) { + n.indeterminate = false; + n.checked = checkStatus; + n.notAllChecked = false; + } + } + } + function getChildrenChecks(checkbox) { + if (!checkbox || !checkbox.parentElement || !checkbox.parentElement.parentElement || 'LI' != checkbox.parentElement.parentElement.tagName) return []; + var li = checkbox.parentElement.parentElement; + var children = []; + for (var i = 0; i < li.children.length; i++) { + var n1 = li.children[i]; + if ('UL' != n1.tagName) continue; + for (var j = 0; j < n1.children.length; j++) { + var n2 = n1.children[j]; + if ('LI' != n2.tagName) continue; + for (var k = 0; k < n2.children.length; k++) { + var n3 = n2.children[k]; + for (var l = 0; l < n3.children.length; l++) { + var n4 = n3.children[l]; + if ('INPUT' == n4.tagName) children.push(n4); + } + } + } + } + return children; + } + function updateParentsChecks(checkbox) { + for (var parentCheckbox = checkbox.parentCheckbox; parentCheckbox; parentCheckbox = parentCheckbox.parentCheckbox) { + var checkedNumberOfResults = 0; + var uncheckedNumberOfResults = 0; + var uncheckedAll = true; + var notAllChecked = false; + var totalNumberOfResults = 0; + var indeterminateChildren = 0; + var children = getChildrenChecks(parentCheckbox); + for (var i = 0; i < children.length; i++) { + var n = children[i]; + if (n.notAllChecked) notAllChecked = true; + if (n.indeterminate) { + indeterminateChildren++; + uncheckedAll = false; + notAllChecked = true; + } else if (n.checked) { + checkedNumberOfResults += n.numberOfResults; + totalNumberOfResults += n.numberOfResults; + uncheckedAll = false; + } else { + uncheckedNumberOfResults += n.numberOfResults; + totalNumberOfResults += n.numberOfResults; + notAllChecked = true; + } + } + if (checkedNumberOfResults == parentCheckbox.numberOfResults && !notAllChecked) { + parentCheckbox.indeterminate = false; + parentCheckbox.checked = true; + parentCheckbox.notAllChecked = false; + } else if ( uncheckedNumberOfResults == parentCheckbox.numberOfResults + || (parentCheckbox.indeterminate && uncheckedAll)) { + parentCheckbox.indeterminate = false; + parentCheckbox.checked = false; + } else if ( totalNumberOfResults == parentCheckbox.numberOfResults + || ( !parentCheckbox.indeterminate + && !parentCheckbox.checked + && (indeterminateChildren || checkedNumberOfResults || notAllChecked))){ + parentCheckbox.indeterminate = true; + } else { + parentCheckbox.notAllChecked = notAllChecked; + } + } + } + + function toMenu(master, items, data, chooseFn, applyFn, cancelFn, armFn) { + //var isNotInputField = master.nodeName != 'INPUT'; + var cursorIndex = 0; + var isInit = 0; + master.onkeydown = function(e) { + e = e || window.event; + var key = e.keyCode || e.charCode; + + if ( cursorIndex > 0 + && getClassName(items[cursorIndex-1]) == items[cursorIndex-1].z) { + cursorIndex = 0; + } + + // RIGHT (key: 39) or TAB without SHIFT (key: 9) to apply + if (applyFn && (key == 39 || (key == 9 && !e.shiftKey))) { + if (applyFn(data, key)) preventDefault(e); + } + + // ESC to cancel + if (cancelFn && key == 27) { + cancelFn(); + preventDefault(e); + return; + } + + // ENTER to choose + if (key == 13 && cursorIndex > 0) { + preventDefault(e); + stopPropagation(e); + setClassName(items[cursorIndex-1], items[cursorIndex-1].z); + chooseFn(data[cursorIndex-1]); + cursorIndex = 0; + return; + } + + // select by UP and DOWN + if (key != 40 && key != 38) return; + preventDefault(e); + var isDown = key == 40; + if (cursorIndex > 0) { + setClassName(items[cursorIndex-1], items[cursorIndex-1].z); + } + cursorIndex = cursorIndex < 1 + ? (isDown ? 1 : items.length) + : (cursorIndex + (isDown ? 1 : -1)) % (items.length + 1); + if (cursorIndex > 0) { + var item = items[cursorIndex-1]; + setClassName(item, item.z + ' z'); + if (armFn) armFn(item, data[cursorIndex-1], 0); + } + } + for (var i = 0; i < items.length; i++) { + items[i].z = getClassName(items[i]); + items[i].onmousedown = function() {setTimeout(function() {if (master && !master.hasFocus) master.focus()}, 42)}; + items[i].onmouseup = items[i].ontouchend = function(a, b) {return function(e) {preventDefault(e); if (!a.canceled) {chooseFn(b); setClassName(a, a.z); cursorIndex = 0}}}(items[i], data[i]); + items[i].onmouseover = items[i].ontouchstart = function(a, b, c) {return function() {if (!isInit) return; if (cursorIndex > 0) setClassName(items[cursorIndex-1], items[cursorIndex-1].z); setClassName(a, a.z + ' z'); cursorIndex = b; a.canceled = ''; if (armFn && b > 0) armFn(a, c, 1)}}(items[i], i+1, data[i]); + items[i].onmouseout = function(a) {return function() {setClassName(a, a.z)}}(items[i]); + } + setTimeout(function() {isInit = 1; }, 142); + } + + function addHighlightedText(element, text, searchWord) { + var searchWordLowerCase = searchWord.toLowerCase(); + var textLowerCase = text.toLowerCase(); + var hIndex = textLowerCase.indexOf(searchWordLowerCase); + if (hIndex < 0) { + element.appendChild(document.createTextNode(text)); + return; + } + + var lastEnd = 0; + while (hIndex >=0) { + element.appendChild(document.createTextNode(text.substring(lastEnd, hIndex))); + lastEnd = hIndex + searchWord.length; + if ( hIndex == 0 + || text.substring(hIndex - 1, hIndex).replace(/\w/, "").length != 0) { + var strong = createElement(element, 'strong'); + strong.appendChild(document.createTextNode(text.substring(hIndex, lastEnd))); + } else { + element.appendChild(document.createTextNode(text.substring(hIndex, lastEnd))); + } + hIndex = textLowerCase.indexOf(searchWordLowerCase, lastEnd); + } + element.appendChild(document.createTextNode(text.substring(lastEnd))); + } + + } + + function searchFullByHash(hash) { + var url = SEARCH_BASE_URL + hash.substring(3) + '&maxHits=' + SEARCH_HITS_MAX + + (hash.indexOf('&toc=') < 0 ? '' : '&quickSearch=true&quickSearchType=QuickSearchToc'); + window.frames.c.location = url; + + // fill search field + var searchWord = getParams(hash)['#q']; + if (searchWord && searchWord != getElementById('q').value) { + getElementById('q').value = searchWord; + } + + // TODO set search socpe? + + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Menu + + function createMenu() { + + // menu + var menu = createOverlay(9, 1); + var overlay = createOverlay(8); + menu.id = 'a'; + menuStyle = menu.style; + menu.a = function(e) { preventDefault(e); overlay.a(); menuStyle.width = '270px'; }; + menu.o = function(e) { preventDefault(e); overlay.o(); menuStyle.width = '0'; }; + menu.o(); + addEvent(overlay, 'click', menu.o); + function createMenuItem(label, description, fn, id, href, parent) { + var item = createElement(parent ? parent : menu, fn ? 'button' : 'a', 'b', label); + item.href = href ? (BASE_URL + href) : '#'; + item.target = 'c'; + item.title = description; + if (id) { + item.id = id; + } + addEvent(item, 'click', function(e) { + if (fn) { preventDefault(e); fn(e); } + if (href) { getElementById('c').contentDocument.location.href = BASE_URL + href; } + if (!parent) menu.o(); + }); + return item; + } + + // "x" button + var closeMenuButton = createElement(createElement(menu, 0, 'e'), 'a', 'b'); + closeMenuButton.href = '#'; + addEvent(closeMenuButton, 'click', menu.o); + closeMenuButton.alt = MENU_CLOSE_ICON_DESCRIPTION; + closeMenuButton.title = MENU_CLOSE_ICON_DESCRIPTION; + setInnerHtml(closeMenuButton, MENU_CLOSE_ICON); + + // "Highlight search terms" dummy + function HighlightConnector() {}; + HighlightConnector.prototype.setButtonState = function(/*name, state*/) { + // dummy for highlight() in org.eclipse.help.webapp/advanced/highlight.js + }; + window.ContentToolbarFrame = new HighlightConnector(); + var highlight = createMenuItem(0, 'Toggle search term highlighting', toggleHighlight, 'ah'); + createElement(highlight, 'span', 'hl', 'Highlight'); + createElement(highlight, 'span', 'hs', ' '); + createElement(highlight, 'span', 'ht', 'search term'); + toggleHighlight(0, 1); + + // "Font: - +" + if (MENU_FONT_SIZING) { + var fontSizer = createElement(menu); + fontSizer.id = 'af'; + createElement(fontSizer, 'span', 0, 'Font:'); + createMenuItem('\u2013', 'Decrease font size', function() { setFontSize(0); }, 'afm', 0, fontSizer); + createMenuItem('+', 'Increase font size', function() { setFontSize(1); }, 'afp', 0, fontSizer); + } + + // "Bookmarks..." + if (embeddedMode) { + createMenuItem('Bookmarks...', 'Bookmark current topic and manage existing bookmarks', function() { + bookmarksPage.s(1); + }); + } + + // "Search scopes..." + createMenuItem('Search scopes...', 'Manage search scopes', function() { + scopesPage.s(1); + }); + + // "Print topic..." + createMenuItem('Print topic...', 'Print topic without its subtopics', function() { + try { + getElementById('c').contentWindow.print(); + } catch (e) { + } + }, 'ap'); + + // "Print chapter..." + createMenuItem('Print chapter...', 'Print topic including subtopics', printChapter, 'app'); + + // "Help" + if (MENU_HELP) { + createMenuItem('Help', 'How to use help', 0, 'ai', 'topic/org.eclipse.help.base/doc/help_home.html'); + } + + // "About" + if (MENU_ABOUT) { + createMenuItem('About', 'Configuration details', 0, 'aa', 'about.html'); + } + + // show menu button + var menuButton = createElement(getElementById('h'), 'a', 'b'); + menuButton.href = '#'; + menuButton.alt = MENU_ICON_DESCRIPTION; + menuButton.title = MENU_ICON_DESCRIPTION; + setInnerHtml(menuButton, MENU_ICON); + addEvent(menuButton, 'click', menu.a); + + } + + function toggleHighlight(_event, initalize) { + + var enableHighlighting = 'false' == getCookie('highlight'); + if (initalize) { + enableHighlighting = !enableHighlighting; + } else { + setCookie('highlight', enableHighlighting ? 'true' : 'false'); + var contentFrameWindow = getElementById('c').contentWindow; + if (contentFrameWindow && contentFrameWindow.highlight && contentFrameWindow.toggleHighlight) { + contentFrameWindow.toggleHighlight(); + contentFrameWindow.highlight(); + } + } + setClassName(getElementById('ah'), enableHighlighting ? 'b x' : 'b'); + } + + function setFontSize(increase, initalize, updateContentFrameOnly) { + if (!MENU_FONT_SIZING) return; + var newFontSize; + var contentFrameDocument = getElementById('c').contentWindow.document; + var contentFrameDocumentElement = contentFrameDocument.documentElement || contentFrameDocument.body; + var toc = document.getElementById('t'); + var tocStyle = getComputedStyle(toc, null).getPropertyValue('font-size'); + var tocFontSize = parseFloat(tocStyle); + if (initalize) { + newFontSize = getCookie('font-size'); + } else if (increase && !initalize && tocFontSize < 64) { + newFontSize = (tocFontSize + 3); + } else if (!increase && !initalize && tocFontSize > 12) { + newFontSize = (tocFontSize - 3); + } + if (!newFontSize) return; + contentFrameDocumentElement.style.fontSize = newFontSize + 'px'; + if (!updateContentFrameOnly) { + toc.style.fontSize = newFontSize + 'px'; + searchPage.style.fontSize = newFontSize + 'px'; + } + setCookie('font-size', newFontSize, 365); + } + + function printChapter() { + var contentElement = getElementById('c'); + var contentWindow = contentElement.contentWindow; + var topicHref = contentWindow.location.href; + if (!topicHref) return; + var dummy = document.createElement('a'); + dummy.href = BASE_URL + 'x'; + var topic = topicHref.substring(dummy.href.length - 2); + if (topic.length > 7 && '/topic/' == topic.substring(0, 7)) topic = topic.substring(6); + else if (topic.length > 5 && '/nav/' == topic.substring(0, 5)) topic = '/..' + topic; + else if (topic.length > 8 && ('/rtopic/' == topic.substring(0, 8) || '/ntopic/' == topic.substring(0, 8))) topic = topic.substring(7); + var w = contentWindow.innerWidth || contentWindow.document.body.clientWidth; + var h = contentWindow.innerHeight || contentWindow.document.body.clientHeight; + var x = window.screenX; + var y = window.screenY; + for (var e = contentElement; !!e; e = e.offsetParent) { + if (e.tagName == "BODY") { + var xScroll = e.scrollLeft || document.documentElement.scrollLeft; + var yScroll = e.scrollTop || document.documentElement.scrollTop; + x += (e.offsetLeft - xScroll + e.clientLeft); + y += (e.offsetTop - yScroll + e.clientTop); + } else { + x += (e.offsetLeft - e.scrollLeft + e.clientLeft); + y += (e.offsetTop - e.scrollTop + e.clientTop); + } + } + var anchor = ''; + var anchorStart = topic.indexOf('#'); + if (anchorStart > 0) { + anchor = '&anchor=' + topic.substr(anchorStart + 1); + topic = topic.substr(0, anchorStart); + } + var query = ''; + var queryStart = topic.indexOf('?'); + if (queryStart > 0) { + query = '&' + topic.substr(queryStart + 1); + topic = topic.substr(0, queryStart); + } + window.open(BASE_URL + 'advanced/print.jsp?topic=' + topic + query + anchor, 'printWindow', 'directories=yes,location=no,menubar=yes,resizable=yes,scrollbars=yes,status=yes,titlebar=yes,toolbar=yes,width=' + w + ',height=' + h + ',left=' + x + ',top=' + y); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Tree + + function createTree(element, contentProvider, labelProvider, selectable) { + var root = createElement(element, 0, 'tree'); + function createNode(parent, node) { + contentProvider(node, createNodeChildrenFn(parent, node)); + }; + function createNodeChildrenFn(parent) { + return function(children, open) { + var ul = createElement(parent, 'ul'); + for (var i = 0; i < children.length; i++) { + var li = createElement(ul, 'li', 'closed'); + li.p = parent; + var child = children[i]; + if (!child.l) { + + // c(hildren): yes + li.c = 1; + + // i(nit) function to load children + li.i = (function(li, node) { + return function() { + createNode(li, node); + li.i = 0; + }; + })(li, child.n); + + // handle (to toggle subtree) + var handle = createElement(li, 'span', 'h'); + handle.innerHTML = TREE_HANDLE; + addEvent(handle, 'click', (function(li) { + return function(e) { + toggleLi(li); + stopPropagation(e); + + // focus next element (to avoid losing focus since the handle cannot be focused) + try { + li.childNodes[1].focus(); + } catch(e) {} + + }; + })(li)); + + } + var label = labelProvider(li, child.n); + setClassName(label, 'l'); + if (selectable) { + addEvent(label, 'click', (function(li) { + return function(e) { + if (element.s === li) return; + element.x(li); + stopPropagation(e); + }; + })(li)); + } + addEvent(label, 'dblclick', (function(li) { + return function(e) { + toggleLi(li); + stopPropagation(e); + } + })(li)); + if (open || getClassName(li) == 'open') toggleLi(li); + } + } + }; + element.x = function(li, scrollArea, closeSiblings) { + for (var n = element.s; isLi(n); n = n.p) { + n.xx = 0; + n.x = 0; + updateLiClasses(n); + } + element.s = li; + if (!li) return; + li.xx = 1; + updateLiClasses(li); + for (var n = li.p; isLi(n); n = n.p) { + if (!n.o) { + toggleLi(n); + } + n.x = 1; + updateLiClasses(n); + } + + // close siblings of the selected node and its ancestors + for (var current = li; closeSiblings && current.tagName == 'LI'; current = current.p) { + var ul = getParentElement(current); + for (var i = 0; i < ul.childNodes.length; i++) { + var n = ul.childNodes[i]; + if (current !== n && n.o) { + toggleLi(n); + } + } + } + + // scroll label into view if needed + for (var i = 0; i < li.childNodes.length; i++) { + var n = li.childNodes[i]; + if (n.tagName == 'A' || n.tagName == 'BUTTON') { + scrollIntoViewIfNeeded(scrollArea, n); + break; + } + } + + // update search field book scope + if (updateScopeByToc && li.toc) { + updateScopeByToc(li); + } + + }; + element.y = function(href, scrollArea, closeSiblings) { + contentProvider({topic: href}, function(children) { + if (!children || children.length != 1 || !children[0].n || !children[0].n.y) { + + // deselect current selection + element.x(0); + + return; + } + var expandPath = children[0].n.y.split('_'); + var parentLi = root; + var parentNode = {}; + var childNodes = children; + for (var i = 0; i < expandPath.length; i++) { + var nr = parseInt(expandPath[i]); + if (parentLi.i) { + (createNodeChildrenFn(parentLi, parentNode))(childNodes); + parentLi.i = 0; + } + var ul = 0; + for (var j = 0; j < parentLi.childNodes.length; j++) { + var n = parentLi.childNodes[j]; + if (n.tagName == 'UL') { + ul = n; + break; + } + } + var li = 0; + var liNr = 0; + for (var j = 0; j < ul.childNodes.length; j++) { + var n = ul.childNodes[j]; + if (n.tagName == 'LI') { + if (liNr == nr) { + li = n; + break; + } + liNr++; + } + } + if (i == expandPath.length - 1) { + element.x(li, scrollArea, closeSiblings); + } + parentLi = li; + var child = childNodes[i == 0 ? 0 : nr]; + parentNode = child.n; + childNodes = child.c; + } + }); + } + createNode(root); + + // handling via the keys up, down, left, right, home and end + addEvent(element, 'keydown', function(e) { + var keyCode = e.keyCode || window.event.keyCode; + if (keyCode < 35 || keyCode > 40) return; + + // compute focused tree node + var li; + for (li = e.target || e.srcElement; li && li !== root; ) { + if (isLi(li)) break; + li = getParentElement(li); + } + if (!li) return; + + // left/right + if (keyCode == 37 || keyCode == 39) { + if (keyCode == 37 ^ !li.o) { + toggleLi(li); + } else if (keyCode == 37) { + focusTreeNode(li.p); + } else { + focusFirstChildNode(li); + } + + // down + } else if(keyCode == 40) { + + // expanded? -> focus first child, ... + if (li.o) { + focusFirstChildNode(li); + preventDefault(e); + return; + } + + // ...otherwise -> focus next sibling at this or higher level + for (var level = li; isLi(level); level = level.p) { + for (var next = getNextSibling(level); next; next = getNextSibling(next)) { + if (!isLi(next)) continue; + focusTreeNode(next); + preventDefault(e); + return; + } + } + + // up + } else if(keyCode == 38) { + + // previous sibling? -> focus previous sibling, ... + for (var prev = getPreviousSibling(li); prev !== null; prev = getPreviousSibling(prev)) { + if (!isLi(prev)) continue; + focusDeepestVisibleChild(prev); + preventDefault(e); + return; + } + + // ...otherwise -> focus parent + focusTreeNode(li.p); + + // home + } else if(keyCode == 36) { + focusFirstChildNode(root); + + // end + } else if(keyCode == 35) { + focusDeepestVisibleChild(root); + + } + preventDefault(e); + + }); + element.f = function() { + focusTreeNode(element.s); + }; + if (selectable) { + addEvent(element, 'click', element.f); + } + + // visitor pattern: visit the nearby nodes first (first the selected node with its subtree deep-first, then the + // parent, then the siblings with their subtrees and repeating for each higher level with the parent (if any) + // and the siblings, without the already processed node and its subtree) + element.v = function(vistorFn) { + var todoSiblingsAndAncestorsOf = element.s; + var todoSubtrees = element.s ? [element.s] : toArray(root.childNodes[0].childNodes); + while (todoSubtrees.length || todoSiblingsAndAncestorsOf) { + var next; + + // subtree done? -> go one level up + if (!todoSubtrees.length) { + var sibling = todoSiblingsAndAncestorsOf; + while (sibling = getNextSibling(sibling)) { + todoSubtrees.unshift(sibling); + } + sibling = todoSiblingsAndAncestorsOf; + while (sibling = getPreviousSibling(sibling)) { + todoSubtrees.unshift(sibling); + } + if (todoSiblingsAndAncestorsOf.p && todoSiblingsAndAncestorsOf.p.tagName == 'LI') { + next = todoSiblingsAndAncestorsOf = todoSiblingsAndAncestorsOf.p; + } else { + todoSiblingsAndAncestorsOf = 0; + continue; + } + } else { + next = todoSubtrees.pop(); + + // add children of next (if any) + for (var i = 0; i < next.childNodes.length; i++) { + var n = next.childNodes[i]; + if (n.tagName != 'UL') continue; + for (var j = n.childNodes.length - 1; j >= 0; j--) { + var m = n.childNodes[j]; + if (isLi(m)) { + todoSubtrees.push(m); + } + } + } + + } + + // call visitor + if (!vistorFn(next)) return; + + } + + } + function toArray(nodeList) { + var result = []; + for (var i = 0; i < nodeList.length; i++) { + result.push(nodeList[i]); + } + return result; + } + + function toggleLi(li) { + if (!li.c) return; + if (li.i) { + li.i(); + } + li.o = !li.o; + updateLiClasses(li); + } + function isLi(element) { + return element && element.tagName == 'LI' + } + function updateLiClasses(li) { + setClassName(li, (li.o ? 'open': 'closed') + (li.xx ? ' xx' : '') + (li.x ? ' x' : '')) + } + function focusFirstChildNode(li) { + for (var i = 0; i < li.childNodes.length; i++) { + var n = li.childNodes[i]; + if (n.tagName != 'UL') continue; + for (var j = 0; j < n.childNodes.length; j++) { + var m = n.childNodes[j]; + if (isLi(m)) { + focusTreeNode(m); + return; + } + } + } + } + function focusDeepestVisibleChild(li) { + for (var i = 0; li.o && i < li.childNodes.length; i++) { + var n = li.childNodes[i]; + if (n.tagName != 'UL') continue; + for (var j = n.childNodes.length - 1; j >= 0; j--) { + var m = n.childNodes[j]; + if (!isLi(m)) continue; + focusDeepestVisibleChild(m); + return; + } + } + focusTreeNode(li); + } + function focusTreeNode(li) { + if (!li) return; + for (var i = 0; i < li.childNodes.length; i++) { + var n = li.childNodes[i]; + if (n.tagName != 'A' && n.tagName != 'BUTTON') continue; + try { + n.focus(); + } catch(e) {} + return; + } + } + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Overlay + + function createOverlay(zIndex, withoutStyles) { + var overlay = createElement(); + var header = getElementById('h'); + getParentElement(header).insertBefore(overlay, header); + if (!withoutStyles) { + var overlayStyle = overlay.style; + overlayStyle.display = 'none'; + overlayStyle.zIndex = zIndex ? zIndex : 1; + overlayStyle.position = 'absolute'; + overlayStyle.height = '100%'; + overlayStyle.width = '100%'; + } + overlay.a = function() { overlayStyle.display = 'block'; }; + overlay.o = function() { overlayStyle.display = 'none'; }; + return overlay; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Utility functions (polyfill/retrofit functions see below) + + function addEvent(element, type, fn) { + if (element.addEventListener) { + element.addEventListener(type, fn, false); + } else if (element.attachEvent) { + element['e' + type + fn] = fn; + element[type + fn] = function() { + element['e' + type + fn](window.event); + } + element.attachEvent('on' + type, element[type + fn]); + } + } + + function decodeHtml(htmlString) { + if (!htmlString) return htmlString; + var element = createElement(); + setInnerHtml(element, htmlString); + return element.textContent || element.innerText; + } + + function getElementById(id) { + return document.getElementById(id); + } + + function getParentElement(element) { + return element.parentElement; + } + + function getPreviousSibling(element) { + return element.previousSibling; + } + + function getNextSibling(element) { + return element.nextSibling; + } + + function getClassName(element) { + return element.className; + } + + function setClassName(element, value) { + element.className = value; + return element; + } + + function getAttribute(element, attribute) { + return element.getAttribute(attribute); + } + + function setAttribute(element, attribute, value) { + element.setAttribute(attribute, value); + return element; + } + + function setInnerHtml(element, innerHtml) { + element.innerHTML = innerHtml ? innerHtml : ''; + } + + function preventDefault(e) { + e = e || window.event; + if (!e) return; + try { + if (e.preventDefault) e.preventDefault(); + e.returnValue = false; + } catch(e) {} + } + + function stopPropagation(e) { + e = e || window.event; + if (!e) return; + e.cancelBubble = true; + if (e.stopPropagation) e.stopPropagation(); + } + + function getParams(queryPart) { + var params = {}; + queryPart.replace(/(?:^|&+)([^=&]+)=([^&]*)/gi, + function(_match, group1Param, group2Value) { params[group1Param] = decodeURIComponent(group2Value); }); + return params; + } + + function getCookie(cookieName, defaultValue) { + var name = cookieName + "="; + var decodedCookie = decodeURIComponent(document.cookie); + var allCookies = decodedCookie.split(';'); + for (var i = 0; i < allCookies.length; i++) { + var cookie = allCookies[i]; + while (cookie.charAt(0) == ' ') { + cookie = cookie.substring(1); + } + if (cookie.indexOf(name) == 0) { + return cookie.substring(name.length, cookie.length); + } + } + return defaultValue; + } + + function setCookie(cookieName, value) { + var d = new Date(); + d.setTime(d.getTime() + (365 * 24 * 60 * 60 * 1000)); + var expires = 'expires=' + d.toUTCString(); + document.cookie = cookieName + '=' + value + ';' + expires + ';path=/;samesite=strict'; + } + + var openRequests = {}; + function remoteRequest(url, callbackFn, cancelId) { + var request = new XMLHttpRequest(); + if (callbackFn) request.onreadystatechange = function() { + if (request.readyState == 4 && request.status == 200) callbackFn(request.responseText); + } + request.open('GET', url); + request.send(); + if (cancelId) { + if (openRequests[cancelId] && openRequests[cancelId].abort) openRequests[cancelId].abort(); + openRequests[cancelId] = request; + } + } + + var parseXml; + if (typeof window.DOMParser != 'undefined') { + parseXml = function(xmlStr) { + return (new window.DOMParser()).parseFromString(xmlStr, 'text/xml'); + }; + } else if ( typeof window.ActiveXObject != 'undefined' + && new window.ActiveXObject('Microsoft.XMLDOM')) { + parseXml = function(xmlStr) { + var xmlDoc = new window.ActiveXObject('Microsoft.XMLDOM'); + xmlDoc.async = 'false'; + xmlDoc.loadXML(xmlStr); + return xmlDoc; + }; + } + + function scrollIntoViewIfNeeded(scrollArea, element) { + if (!scrollArea) return; + try { + var scrollAreaBoundaries = scrollArea.getBoundingClientRect(); + var elementBoundaries = element.getBoundingClientRect(); + if ( elementBoundaries.top >= scrollAreaBoundaries.top + && elementBoundaries.bottom <= scrollAreaBoundaries.bottom) return; + + scrollArea.scrollTop += scrollArea.c + + // show element in upper third + ? (( (elementBoundaries.bottom - scrollAreaBoundaries.bottom) + + (elementBoundaries.top - scrollAreaBoundaries.top) * 2) / 3) + + // scroll as less as possible + : (elementBoundaries.bottom <= scrollAreaBoundaries.bottom + ? elementBoundaries.top - scrollAreaBoundaries.top + : elementBoundaries.bottom - scrollAreaBoundaries.bottom); + + } catch (e) {} + } + + function arrayContainsPrefix(array, value) { + for (var i = 0; i < array.length; i++) { + if (array[i].length <= value.length && value.substring(0, array[i].length) == array[i]) return true; + } + return false; + } + + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + // + // Polyfill/retrofit utility functions + + function createElement(parent, name, className, text) { + var element = document.createElement(name ? name : 'div'); + if (parent) { + try { + parent.appendChild(element); + } catch(e) { + + // POLYFILL IE<=8 + // HTML5 semantic tags (https://www.w3schools.com/html/html5_browsers.asp) + // polyfill adding child element to the empty HTML 5 semantic element 'aside' (for IE 8 and lower) + if (parent.tagName == 'ASIDE') { + var pp = parent.parentElement; + var rest = []; + for (var i = 0; i < pp.childNodes.length; i++) { + if (parent === pp.childNodes[i]) { + if (div) continue; + var div = document.createElement('div'); + for (var j = 0; j < parent.attributes.length; j++) { + var attribute = parent.attributes[j]; + if ('' + parent.getAttribute(attribute.name) != '' + div.getAttribute(attribute.name) + && !('' + parent.getAttribute(attribute.name) == 'null' + && '' + div.getAttribute(attribute.name) == '')) { + setAttribute(div, attribute.name, attribute.value); + } + } + pp.removeChild(parent); + div.appendChild(element); + rest.push(div); + i--; + continue; + } + if (rest && pp.childNodes[i].tagName == '/' + parent.tagName) { + pp.removeChild(pp.childNodes[i]); + i--; + continue; + } + if (!rest) continue; + rest.push(pp.removeChild(pp.childNodes[i])); + i--; + } + for (var i = 0; i < rest.length; i++) pp.appendChild(rest[i]); + } else throw e; + + } + } + if (className) { + setClassName(element, className); + } + if (text) { + element.appendChild(document.createTextNode(text)); + } + return element; + } + +}(window, document)); diff --git a/org.eclipse.help.webapp/plugin.xml b/org.eclipse.help.webapp/plugin.xml index 6935a878d..3ff916510 100644 --- a/org.eclipse.help.webapp/plugin.xml +++ b/org.eclipse.help.webapp/plugin.xml @@ -36,6 +36,11 @@ base-name="/advanced" httpcontextId="help"> </resource> + <resource + alias="/m" + base-name="/m" + httpcontextId="help"> + </resource> <serviceSelector filter="(other.info=org.eclipse.help)"> </serviceSelector> |