| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  | module.exports = { | 
					
						
							| 
									
										
										
										
											2017-03-25 21:07:34 +00:00
										 |  |  |   /** | 
					
						
							|  |  |  |    * Transform the values of an object via a mapper function | 
					
						
							|  |  |  |    * @param {Object} obj | 
					
						
							|  |  |  |    * @param {Function} mapper | 
					
						
							|  |  |  |    */ | 
					
						
							| 
									
										
										
										
											2017-01-26 14:03:53 +00:00
										 |  |  |   mapObject(obj, mapper) { | 
					
						
							|  |  |  |     const mappedObj = {}; | 
					
						
							|  |  |  |     Object.keys(obj).forEach(key => { mappedObj[key] = mapper(key, obj[key]); }); | 
					
						
							|  |  |  |     return mappedObj; | 
					
						
							| 
									
										
										
										
											2017-03-25 21:07:34 +00:00
										 |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Parses the attributes from a string taken from an HTML element start tag | 
					
						
							|  |  |  |    * E.g. ` a="one" b="two" ` | 
					
						
							|  |  |  |    * @param {string} str | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   parseAttributes(str) { | 
					
						
							|  |  |  |     const attrMap = {}; | 
					
						
							| 
									
										
										
										
											2017-04-01 21:34:10 +03:00
										 |  |  |     let index = 0; | 
					
						
							| 
									
										
										
										
											2017-03-25 21:07:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     skipSpace(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     while(index < str.length) { | 
					
						
							|  |  |  |       takeAttribute(); | 
					
						
							|  |  |  |       skipSpace(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function takeAttribute() { | 
					
						
							|  |  |  |       const key = takeKey(); | 
					
						
							|  |  |  |       skipSpace(); | 
					
						
							|  |  |  |       if (tryEquals()) { | 
					
						
							|  |  |  |         skipSpace(); | 
					
						
							|  |  |  |         const quote = tryQuote(); | 
					
						
							|  |  |  |         attrMap[key] = takeValue(quote); | 
					
						
							|  |  |  |         // skip the closing quote or whitespace
 | 
					
						
							|  |  |  |         index++; | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         attrMap[key] = true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function skipSpace() { | 
					
						
							|  |  |  |       while(index < str.length && /\s/.test(str[index])) { | 
					
						
							|  |  |  |         index++; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function tryEquals() { | 
					
						
							|  |  |  |       if (str[index] === '=') { | 
					
						
							|  |  |  |         index++; | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function takeKey() { | 
					
						
							|  |  |  |       let startIndex = index; | 
					
						
							|  |  |  |       while(index < str.length && /[^\s=]/.test(str[index])) { | 
					
						
							|  |  |  |         index++; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return str.substring(startIndex, index); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function tryQuote() { | 
					
						
							|  |  |  |       const quote = str[index]; | 
					
						
							| 
									
										
										
										
											2017-04-01 21:34:10 +03:00
										 |  |  |       if (['"', '\''].indexOf(quote) !== -1) { | 
					
						
							| 
									
										
										
										
											2017-03-25 21:07:34 +00:00
										 |  |  |         index++; | 
					
						
							|  |  |  |         return quote; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function takeValue(quote) { | 
					
						
							|  |  |  |       let startIndex = index; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (quote) { | 
					
						
							|  |  |  |         while(index < str.length && str[index] !== quote) { | 
					
						
							|  |  |  |           index++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (index >= str.length) { | 
					
						
							|  |  |  |           throw new Error(`Unterminated quoted attribute value in \`${str}\`. Starting at ${startIndex}. Expected a ${quote} but got "end of string".`); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         while(index < str.length && /\S/.test(str[index])) { | 
					
						
							|  |  |  |           index++; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |       return str.substring(startIndex, index); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return attrMap; | 
					
						
							| 
									
										
										
										
											2017-04-26 17:58:57 +01:00
										 |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   renderAttributes(attrMap) { | 
					
						
							| 
									
										
										
										
											2017-05-10 14:44:43 +01:00
										 |  |  |     return Object.keys(attrMap).map(key => | 
					
						
							|  |  |  |       attrMap[key] === false ? '' : | 
					
						
							|  |  |  |       attrMap[key] === true ? ` ${key}` : | 
					
						
							|  |  |  |       ` ${key}="${attrMap[key].replace(/"/g, '"')}"`).join(''); | 
					
						
							| 
									
										
										
										
											2018-06-04 11:43:15 +01:00
										 |  |  |   }, | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /** | 
					
						
							|  |  |  |    * Merge the specified properties from the source to the target document | 
					
						
							|  |  |  |    * @param {Document} target The document to receive the properties from the source | 
					
						
							|  |  |  |    * @param {Document} source The document from which to get the properties to merge | 
					
						
							|  |  |  |    * @param {string[]} properties A collection of the names of the properties to merge | 
					
						
							|  |  |  |    */ | 
					
						
							|  |  |  |   mergeProperties(target, source, properties) { | 
					
						
							|  |  |  |     properties.forEach(property => { | 
					
						
							|  |  |  |       if (source.hasOwnProperty(property)) { | 
					
						
							|  |  |  |         target[property] = source[property]; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }, | 
					
						
							| 
									
										
										
										
											2017-04-01 21:34:10 +03:00
										 |  |  | }; |