blob: 8278d17576f60812b98a562fe0584e5858ea5d11 [file] [log] [blame]
Nils Diewald0e6992a2015-04-14 20:13:52 +00001/*
2 * MenuItems may define:
3 *
4 * onclick: action happen on click and enter.
5 * further: action happen on right arrow
6 */
7
8/**
9 * Item in the Dropdown menu
10 */
11define({
12 /**
13 * Create a new MenuItem object.
14 *
15 * @constructor
16 * @this {MenuItem}
17 * @param {Array.<string>} An array object of name, action and
18 * optionally a description
19 */
20 create : function (params) {
21 return Object.create(this)._init(params);
22 },
23
24 /**
25 * Upgrade this object to another object,
26 * while private data stays intact.
27 *
28 * @param {Object] An object with properties.
29 */
30 upgradeTo : function (props) {
31 for (var prop in props) {
32 this[prop] = props[prop];
33 };
34 return this;
35 },
36
Nils Diewald7148c6f2015-05-04 15:07:53 +000037
38 /**
39 * Get or set the content of the meun item.
40 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000041 content : function (content) {
42 if (arguments.length === 1)
43 this._content = document.createTextNode(content);
44 return this._content;
45 },
46
Nils Diewald7148c6f2015-05-04 15:07:53 +000047 /**
48 * Get the lower cased field of the item
49 * (used for analyses).
50 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000051 lcField : function () {
52 return this._lcField;
53 },
54
Nils Diewald7148c6f2015-05-04 15:07:53 +000055
56 /**
57 * Get or set the information for action of this item.
58 */
Nils Diewald0e6992a2015-04-14 20:13:52 +000059 action : function (action) {
60 if (arguments.length === 1)
61 this._action = action;
62 return this._action;
63 },
64
65 /**
66 * Check or set if the item is active
67 *
68 * @param {boolean|null} State of activity
69 */
70 active : function (bool) {
71 var cl = this.element().classList;
72 if (bool === undefined)
73 return cl.contains("active");
74 else if (bool)
75 cl.add("active");
76 else
77 cl.remove("active");
78 },
79
80 /**
81 * Check or set if the item is
82 * at the boundary of the menu
83 * list
84 *
85 * @param {boolean|null} State of activity
86 */
87 noMore : function (bool) {
88 var cl = this.element().classList;
89 if (bool === undefined)
90 return cl.contains("no-more");
91 else if (bool)
92 cl.add("no-more");
93 else
94 cl.remove("no-more");
95 },
96
97 /**
98 * Get the document element of the menu item
99 */
100 element : function () {
101 // already defined
102 if (this._element !== undefined)
103 return this._element;
104
105 // Create list item
106 var li = document.createElement("li");
107
108 // Connect action
109 if (this["onclick"] !== undefined) {
110 li["onclick"] = this.onclick.bind(this);
111 };
112
113 // Append template
114 li.appendChild(this.content());
115
116 return this._element = li;
117 },
118
119 /**
120 * Highlight parts of the item
121 *
122 * @param {string} Prefix string for highlights
123 */
124 highlight : function (prefix) {
Akron6ed13992016-05-23 18:06:05 +0200125
126 // The prefix already matches
127 if (this._prefix === prefix)
128 return;
129
130 // There is a prefix but it doesn't match
131 if (this._prefix !== null) {
132 this.lowlight();
133 }
134
Nils Diewald0e6992a2015-04-14 20:13:52 +0000135 var children = this.element().childNodes;
136 for (var i = children.length -1; i >= 0; i--) {
137 this._highlight(children[i], prefix);
138 };
Akron6ed13992016-05-23 18:06:05 +0200139
140 this._prefix = prefix;
141 },
142
143 /**
144 * Remove highlight of the menu item
145 */
146 lowlight : function () {
147 if (this._prefix === null)
148 return;
149
150 var e = this.element();
151
152 var marks = e.getElementsByTagName("mark");
153 for (var i = marks.length - 1; i >= 0; i--) {
154 // Create text node clone
155 var x = document.createTextNode(
156 marks[i].firstChild.nodeValue
157 );
158
159 // Replace with content
160 marks[i].parentNode.replaceChild(
161 x,
162 marks[i]
163 );
164 };
165
166 // Remove consecutive textnodes
167 e.normalize();
168 this._prefix = null;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000169 },
170
171 // Highlight a certain substring of the menu item
172 _highlight : function (elem, prefix) {
173
174 if (elem.nodeType === 3) {
175
176 var text = elem.nodeValue;
177 var textlc = text.toLowerCase();
178 var pos = textlc.indexOf(prefix);
179 if (pos >= 0) {
180
181 // First element
182 if (pos > 0) {
183 elem.parentNode.insertBefore(
184 document.createTextNode(text.substr(0, pos)),
185 elem
186 );
187 };
188
189 // Second element
190 var hl = document.createElement("mark");
191 hl.appendChild(
192 document.createTextNode(text.substr(pos, prefix.length))
193 );
194 elem.parentNode.insertBefore(hl, elem);
195
196 // Third element
197 var third = text.substr(pos + prefix.length);
198 if (third.length > 0) {
199 var thirdE = document.createTextNode(third);
200 elem.parentNode.insertBefore(
201 thirdE,
202 elem
203 );
204 this._highlight(thirdE, prefix);
205 };
206
207 var p = elem.parentNode;
208 p.removeChild(elem);
209 };
210 }
211 else {
212 var children = elem.childNodes;
213 for (var i = children.length -1; i >= 0; i--) {
214 this._highlight(children[i], prefix);
215 };
216 };
217 },
218
Nils Diewald0e6992a2015-04-14 20:13:52 +0000219 // Initialize menu item
220 _init : function (params) {
221
222 if (params[0] === undefined)
223 throw new Error("Missing parameters");
224
225 this.content(params[0]);
226
227 if (params.length === 2)
228 this._action = params[1];
229
230 this._lcField = ' ' + this.content().textContent.toLowerCase();
Akron6ed13992016-05-23 18:06:05 +0200231 this._highlight = null;
Nils Diewald0e6992a2015-04-14 20:13:52 +0000232
233 return this;
234 },
235
236 /**
237 * Return menu list.
238 */
239 menu : function () {
240 return this._menu;
241 }
242});