| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 1 | /** |
| 2 | * QueryCreator for Kalamar. |
| 3 | * |
| 4 | * @author Nils Diewald |
| 5 | */ |
| 6 | define(['util'], function () { |
| 7 | "use strict"; |
| 8 | |
| 9 | var loc = KorAP.Locale; |
| 10 | loc.NEW_QUERY = loc.NEW_QUERY || 'New Query'; |
| 11 | |
| Akron | 7ec5f39 | 2017-06-29 15:05:44 +0200 | [diff] [blame^] | 12 | function _getAnnotation (prefix, target) { |
| 13 | var annotation = ''; |
| 14 | |
| 15 | // Complex annotation |
| 16 | if (target.childNodes.length > 1) { |
| 17 | var orGroup = []; |
| 18 | target.childNodes.forEach(function (item) { |
| 19 | if (item.nodeType === 3) |
| 20 | orGroup.push(prefix + item.data); |
| 21 | }); |
| 22 | return '(' + orGroup.sort().join(' | ') + ')'; |
| 23 | } |
| 24 | |
| 25 | // Simple annotation |
| 26 | else { |
| 27 | return prefix + target.innerText; |
| 28 | }; |
| 29 | }; |
| 30 | |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 31 | return { |
| 32 | create : function (matchInfo) { |
| 33 | return Object.create(this)._init(matchInfo); |
| 34 | }, |
| 35 | |
| 36 | // Initialize query creator |
| 37 | _init : function (matchInfo) { |
| 38 | |
| Akron | b46d8e3 | 2017-06-29 14:26:14 +0200 | [diff] [blame] | 39 | // Parameter checks |
| 40 | if (matchInfo === undefined) |
| 41 | throw new Error('Missing parameters'); |
| 42 | else if (!(matchInfo instanceof Node)) |
| 43 | throw new Error('Requires element'); |
| 44 | |
| 45 | // Collect the token sequence in an array |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 46 | this._query = [] |
| Akron | b46d8e3 | 2017-06-29 14:26:14 +0200 | [diff] [blame] | 47 | |
| 48 | // Remember the matchinfo that is the parent of |
| 49 | // the matchtable and the query frafment |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 50 | this._matchInfo = matchInfo; |
| 51 | |
| Akron | e8ea000 | 2017-06-28 18:51:52 +0200 | [diff] [blame] | 52 | // Get the match table |
| 53 | this._matchTable = this._matchInfo.getElementsByClassName('matchtable')[0]; |
| 54 | |
| Akron | b46d8e3 | 2017-06-29 14:26:14 +0200 | [diff] [blame] | 55 | if (this._matchTable === undefined) |
| 56 | throw new Error('Element contains no match table'); |
| 57 | |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 58 | // Listen on the match table |
| Akron | e8ea000 | 2017-06-28 18:51:52 +0200 | [diff] [blame] | 59 | this._matchTable.addEventListener( |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 60 | "click", this.clickOnAnno.bind(this), false |
| 61 | ); |
| 62 | |
| 63 | // Initialize element |
| 64 | this._element = document.createElement('p'); |
| 65 | this._element.className = 'queryfragment'; |
| 66 | |
| 67 | // Prepend info text |
| 68 | this._element.appendChild(document.createElement('span')) |
| 69 | .appendChild(document.createTextNode(loc.NEW_QUERY + ':')); |
| 70 | |
| 71 | // Append query fragment part |
| 72 | this._queryFragment = this._element.appendChild( |
| 73 | document.createElement('span') |
| 74 | ); |
| 75 | |
| Akron | 99713ef | 2017-06-28 18:19:28 +0200 | [diff] [blame] | 76 | // Event when the query fragment is clicked |
| 77 | this._element.addEventListener('click', this.toQueryBar.bind(this), 1); |
| 78 | |
| 79 | // Get some basic information - see tutorial.js |
| 80 | // It may be better to consultate a global object like KorAP.Hint, however ... |
| 81 | this._ql = document.getElementById("ql-field"); |
| 82 | this._q = document.getElementById("q-field") |
| 83 | |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 84 | this._shown = false; |
| 85 | return this; |
| 86 | }, |
| 87 | |
| Akron | 7ec5f39 | 2017-06-29 15:05:44 +0200 | [diff] [blame^] | 88 | |
| Akron | 99713ef | 2017-06-28 18:19:28 +0200 | [diff] [blame] | 89 | // Realease a click event on the annotation table |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 90 | clickOnAnno : function (event) { |
| 91 | |
| 92 | // Listen for clicks on table cells |
| 93 | if (event.target !== event.currentTarget) { |
| 94 | |
| 95 | // Get target event |
| 96 | var target = event.target; |
| 97 | |
| 98 | if (target.tagName == 'TD') { |
| 99 | |
| Akron | 78655d1 | 2017-06-28 18:56:57 +0200 | [diff] [blame] | 100 | if (target.innerText == '') |
| 101 | return; |
| 102 | |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 103 | // Check foundry and layer |
| 104 | var head = target.parentNode.getElementsByTagName('th'); |
| 105 | var foundry = head[0].innerText; |
| 106 | var layer = head[1].innerText; |
| 107 | |
| 108 | // Check index position: |
| 109 | var i = -2; |
| 110 | var sib = target; |
| 111 | while((sib = sib.previousSibling) != null) { |
| 112 | if (sib.nodeType === 1) |
| 113 | i++; |
| 114 | }; |
| 115 | |
| Akron | 2bd146c | 2017-06-28 19:10:36 +0200 | [diff] [blame] | 116 | |
| 117 | var prefix = foundry + '/' + layer + '='; |
| 118 | var annotation = ''; |
| 119 | |
| Akron | 7ec5f39 | 2017-06-29 15:05:44 +0200 | [diff] [blame^] | 120 | // Get annotation value from cell |
| 121 | var annotation = _getAnnotation(prefix, target); |
| Akron | 2bd146c | 2017-06-28 19:10:36 +0200 | [diff] [blame] | 122 | |
| 123 | // Add term |
| 124 | this.toggleInToken(target, i, annotation); |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 125 | } |
| 126 | |
| 127 | // Get orth values |
| 128 | else if (target.tagName == 'TH') { |
| 129 | |
| 130 | // The head is in the top row |
| 131 | if (target.parentNode.parentNode.tagName == 'THEAD') { |
| 132 | |
| 133 | var i = -2; |
| 134 | var sib = target; |
| 135 | while ((sib = sib.previousSibling) != null) { |
| 136 | if (sib.nodeType === 1) |
| 137 | i++; |
| 138 | }; |
| 139 | |
| 140 | // Target is an orth |
| 141 | if (i >= 0) { |
| 142 | this.toggleInToken(target, i, 'orth=' + target.innerText); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | // The head refers to the complete row |
| 147 | else { |
| 148 | |
| 149 | // Check foundry and layer |
| 150 | var head = target.parentNode.getElementsByTagName('th'); |
| 151 | var foundry = head[0].innerText; |
| 152 | var layer = head[1].innerText; |
| 153 | var prefix = foundry + '/' + layer + '='; |
| 154 | |
| 155 | // Iterate over all siblings |
| 156 | var i = 0; |
| 157 | var sib = target; |
| 158 | while ((sib = sib.nextSibling) != null) { |
| 159 | if (sib.nodeType !== 1 || sib.tagName === 'TH') |
| 160 | continue; |
| Akron | 7ec5f39 | 2017-06-29 15:05:44 +0200 | [diff] [blame^] | 161 | |
| 162 | // Get annotation value from cell |
| 163 | var annotation = _getAnnotation(prefix, sib); |
| 164 | |
| 165 | // Add annotation to string |
| 166 | this.addToToken(i, annotation); |
| 167 | |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 168 | sib.className = 'chosen'; |
| 169 | i++; |
| 170 | }; |
| 171 | }; |
| 172 | }; |
| 173 | }; |
| 174 | |
| 175 | event.stopPropagation(); |
| 176 | }, |
| 177 | |
| 178 | // Add term to token |
| 179 | addToToken : function (index, term) { |
| 180 | |
| 181 | var token = this._query[index]; |
| 182 | |
| 183 | // Initialize token |
| 184 | if (token === undefined) { |
| 185 | token = this._query[index] = []; |
| 186 | }; |
| 187 | |
| 188 | // Push to token array |
| 189 | token.push(term); |
| 190 | |
| 191 | // Make terms unique |
| 192 | this._query[index] = token.filter( |
| 193 | function (e, i, arr) { |
| 194 | return arr.lastIndexOf(e) === i; |
| 195 | } |
| 196 | ); |
| 197 | |
| 198 | this.show(); |
| 199 | }, |
| 200 | |
| 201 | // Remove term from token |
| 202 | removeFromToken : function (index, term) { |
| 203 | var token = this._query[index]; |
| 204 | |
| 205 | if (token === undefined) |
| 206 | return; |
| 207 | |
| 208 | token.splice(token.indexOf(term), 1); |
| 209 | |
| 210 | if (token.length > 0) |
| 211 | this._query[index] = token; |
| 212 | else |
| 213 | this._query[index] = undefined; |
| 214 | |
| 215 | this.show(); |
| 216 | }, |
| 217 | |
| Akron | b46d8e3 | 2017-06-29 14:26:14 +0200 | [diff] [blame] | 218 | |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 219 | // Get element representing annotation line |
| 220 | element : function () { |
| 221 | return this._element; |
| 222 | }, |
| 223 | |
| Akron | b46d8e3 | 2017-06-29 14:26:14 +0200 | [diff] [blame] | 224 | |
| 225 | // Check if the query fragment is shown |
| 226 | shown : function () { |
| 227 | return this._shown; |
| 228 | }, |
| 229 | |
| 230 | |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 231 | // Show annotation fragment |
| 232 | show : function () { |
| 233 | |
| 234 | var str = this.toString(); |
| 235 | |
| 236 | // Fragment is empty |
| 237 | if (str === '') { |
| 238 | |
| 239 | // Hide element |
| 240 | if (this._shown === true) { |
| 241 | this._matchInfo.removeChild(this._element); |
| 242 | this._shown = false; |
| 243 | } |
| 244 | } |
| 245 | |
| 246 | // Fragment is defined |
| 247 | else { |
| 248 | |
| 249 | if (this._shown === false) { |
| Akron | e8ea000 | 2017-06-28 18:51:52 +0200 | [diff] [blame] | 250 | this._matchInfo.insertBefore(this._element, this._matchTable.nextSibling); |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 251 | this._shown = true; |
| 252 | }; |
| 253 | |
| 254 | // set value |
| 255 | this._queryFragment.innerText = str; |
| 256 | }; |
| 257 | }, |
| 258 | |
| 259 | // Add term to token if not yet chosen, otherwise remove |
| 260 | toggleInToken : function (node, index, term) { |
| 261 | if (node.className == 'chosen') { |
| 262 | this.removeFromToken(index, term); |
| 263 | node.className = ''; |
| 264 | } |
| 265 | else { |
| 266 | this.addToToken(index, term); |
| 267 | node.className = 'chosen'; |
| 268 | }; |
| 269 | }, |
| 270 | |
| 271 | |
| 272 | // Stringify annotation |
| 273 | toString : function () { |
| 274 | var str = ''; |
| 275 | this._query.forEach(function (token) { |
| 276 | if (token !== undefined) { |
| 277 | str += '[' + token.sort().join(" & ") + ']'; |
| 278 | }; |
| 279 | }); |
| 280 | return str; |
| 281 | }, |
| 282 | |
| Akron | 99713ef | 2017-06-28 18:19:28 +0200 | [diff] [blame] | 283 | // Add query fragment to query bar |
| 284 | toQueryBar : function (e) { |
| 285 | |
| Akron | b46d8e3 | 2017-06-29 14:26:14 +0200 | [diff] [blame] | 286 | if (this._ql === undefined || this._q === undefined) |
| 287 | return; |
| 288 | |
| Akron | 99713ef | 2017-06-28 18:19:28 +0200 | [diff] [blame] | 289 | // Set query language field |
| 290 | var qlf = this._ql.options; |
| 291 | for (var i in qlf) { |
| 292 | if (qlf[i].value == 'poliqarp') { |
| 293 | qlf[i].selected = true; |
| 294 | break; |
| 295 | }; |
| 296 | }; |
| 297 | |
| 298 | // Insert to query bar |
| 299 | this._q.value = this.toString(); |
| 300 | |
| 301 | // Scroll to top |
| 302 | window.scrollTo(0, 0); |
| Akron | fbf3a78 | 2017-06-28 17:34:28 +0200 | [diff] [blame] | 303 | } |
| 304 | }; |
| 305 | }); |