PHP Classes

File: js/complete.ly.1.0.1.js

Recommend this page to a friend!
  Classes of Saro Carvello   PHP Web MVC Framework   js/complete.ly.1.0.1.js   Download  
File: js/complete.ly.1.0.1.js
Role: Auxiliary data
Content type: text/plain
Description: Auxiliary data
Class: PHP Web MVC Framework
MVC framework providing autogenerated MySQL models
Author: By
Last change: Update of js/complete.ly.1.0.1.js
Date: 2 years ago
Size: 18,259 bytes
 

Contents

Class file image Download
/** * complete.ly 1.0.0 * MIT Licensing * Copyright (c) 2013 Lorenzo Puccetti * * This Software shall be used for doing good things, not bad things. * **/ function completely(container, config) { config = config || {}; config.fontSize = config.fontSize || '16px'; config.fontFamily = config.fontFamily || 'sans-serif'; config.promptInnerHTML = config.promptInnerHTML || ''; config.color = config.color || '#333'; config.hintColor = config.hintColor || '#aaa'; config.backgroundColor = config.backgroundColor || '#fff'; config.dropDownBorderColor = config.dropDownBorderColor || '#aaa'; config.dropDownZIndex = config.dropDownZIndex || '100'; // to ensure we are in front of everybody config.dropDownOnHoverBackgroundColor = config.dropDownOnHoverBackgroundColor || '#ddd'; var txtInput = document.createElement('input'); txtInput.type ='text'; txtInput.spellcheck = false; txtInput.style.fontSize = config.fontSize; txtInput.style.fontFamily = config.fontFamily; txtInput.style.color = config.color; txtInput.style.backgroundColor = config.backgroundColor; txtInput.style.width = '100%'; txtInput.style.outline = '0'; txtInput.style.border = '0'; txtInput.style.margin = '0'; txtInput.style.padding = '0'; var txtHint = txtInput.cloneNode(); txtHint.disabled=''; txtHint.style.position = 'absolute'; txtHint.style.top = '0'; txtHint.style.left = '0'; txtHint.style.borderColor = 'transparent'; txtHint.style.boxShadow = 'none'; txtHint.style.color = config.hintColor; txtInput.style.backgroundColor ='transparent'; txtInput.style.verticalAlign = 'top'; txtInput.style.position = 'relative'; var wrapper = document.createElement('div'); wrapper.style.position = 'relative'; wrapper.style.outline = '0'; wrapper.style.border = '0'; wrapper.style.margin = '0'; wrapper.style.padding = '0'; var prompt = document.createElement('div'); prompt.style.position = 'absolute'; prompt.style.outline = '0'; prompt.style.margin = '0'; prompt.style.padding = '0'; prompt.style.border = '0'; prompt.style.fontSize = config.fontSize; prompt.style.fontFamily = config.fontFamily; prompt.style.color = config.color; prompt.style.backgroundColor = config.backgroundColor; prompt.style.top = '0'; prompt.style.left = '0'; prompt.style.overflow = 'hidden'; prompt.innerHTML = config.promptInnerHTML; prompt.style.background = 'transparent'; if (document.body === undefined) { throw 'document.body is undefined. The library was wired up incorrectly.'; } document.body.appendChild(prompt); var w = prompt.getBoundingClientRect().right; // works out the width of the prompt. wrapper.appendChild(prompt); prompt.style.visibility = 'visible'; prompt.style.left = '-'+w+'px'; wrapper.style.marginLeft= w+'px'; wrapper.appendChild(txtHint); wrapper.appendChild(txtInput); var dropDown = document.createElement('div'); dropDown.style.position = 'absolute'; dropDown.style.visibility = 'hidden'; dropDown.style.outline = '0'; dropDown.style.margin = '0'; dropDown.style.padding = '0'; dropDown.style.textAlign = 'left'; dropDown.style.fontSize = config.fontSize; dropDown.style.fontFamily = config.fontFamily; dropDown.style.backgroundColor = config.backgroundColor; dropDown.style.zIndex = config.dropDownZIndex; dropDown.style.cursor = 'default'; dropDown.style.borderStyle = 'solid'; dropDown.style.borderWidth = '1px'; dropDown.style.borderColor = config.dropDownBorderColor; dropDown.style.overflowX= 'hidden'; dropDown.style.whiteSpace = 'pre'; dropDown.style.overflowY = 'scroll'; // note: this might be ugly when the scrollbar is not required. however in this way the width of the dropDown takes into account var createDropDownController = function(elem) { var rows = []; var ix = 0; var oldIndex = -1; var onMouseOver = function() { this.style.outline = '1px solid #ddd'; } var onMouseOut = function() { this.style.outline = '0'; } var onMouseDown = function() { p.hide(); p.onmouseselection(this.__hint); } var p = { hide : function() { elem.style.visibility = 'hidden'; }, refresh : function(token, array) { elem.style.visibility = 'hidden'; ix = 0; elem.innerHTML =''; var vph = (window.innerHeight || document.documentElement.clientHeight); var rect = elem.parentNode.getBoundingClientRect(); var distanceToTop = rect.top - 6; // heuristic give 6px var distanceToBottom = vph - rect.bottom -6; // distance from the browser border. rows = []; for (var i=0;i<array.length;i++) { if (array[i].indexOf(token)!==0) { continue; } var divRow =document.createElement('div'); divRow.style.color = config.color; divRow.onmouseover = onMouseOver; divRow.onmouseout = onMouseOut; divRow.onmousedown = onMouseDown; divRow.__hint = array[i]; divRow.innerHTML = token+'<b>'+array[i].substring(token.length)+'</b>'; rows.push(divRow); elem.appendChild(divRow); } if (rows.length===0) { return; // nothing to show. } if (rows.length===1 && token === rows[0].__hint) { return; // do not show the dropDown if it has only one element which matches what we have just displayed. } if (rows.length<2) return; p.highlight(0); if (distanceToTop > distanceToBottom*3) { // Heuristic (only when the distance to the to top is 4 times more than distance to the bottom elem.style.maxHeight = distanceToTop+'px'; // we display the dropDown on the top of the input text elem.style.top =''; elem.style.bottom ='100%'; } else { elem.style.top = '100%'; elem.style.bottom = ''; elem.style.maxHeight = distanceToBottom+'px'; } elem.style.visibility = 'visible'; }, highlight : function(index) { if (oldIndex !=-1 && rows[oldIndex]) { rows[oldIndex].style.backgroundColor = config.backgroundColor; } rows[index].style.backgroundColor = config.dropDownOnHoverBackgroundColor; // <-- should be config oldIndex = index; }, move : function(step) { // moves the selection either up or down (unless it's not possible) step is either +1 or -1. if (elem.style.visibility === 'hidden') return ''; // nothing to move if there is no dropDown. (this happens if the user hits escape and then down or up) if (ix+step === -1 || ix+step === rows.length) return rows[ix].__hint; // NO CIRCULAR SCROLLING. ix+=step; p.highlight(ix); return rows[ix].__hint;//txtShadow.value = uRows[uIndex].__hint ; }, onmouseselection : function() {} // it will be overwritten. }; return p; } var dropDownController = createDropDownController(dropDown); dropDownController.onmouseselection = function(text) { txtInput.value = txtHint.value = leftSide+text; rs.onChange(txtInput.value); // <-- forcing it. registerOnTextChangeOldValue = txtInput.value; // <-- ensure that mouse down will not show the dropDown now. setTimeout(function() { txtInput.focus(); },0); // <-- I need to do this for IE } wrapper.appendChild(dropDown); container.appendChild(wrapper); var spacer; var leftSide; // <-- it will contain the leftSide part of the textfield (the bit that was already autocompleted) function calculateWidthForText(text) { if (spacer === undefined) { // on first call only. spacer = document.createElement('span'); spacer.style.visibility = 'hidden'; spacer.style.position = 'fixed'; spacer.style.outline = '0'; spacer.style.margin = '0'; spacer.style.padding = '0'; spacer.style.border = '0'; spacer.style.left = '0'; spacer.style.whiteSpace = 'pre'; spacer.style.fontSize = config.fontSize; spacer.style.fontFamily = config.fontFamily; spacer.style.fontWeight = 'normal'; document.body.appendChild(spacer); } // Used to encode an HTML string into a plain text. // taken from http://stackoverflow.com/questions/1219860/javascript-jquery-html-encoding spacer.innerHTML = String(text).replace(/&/g, '&amp;') .replace(/"/g, '&quot;') .replace(/'/g, '&#39;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;'); return spacer.getBoundingClientRect().right; } var rs = { onArrowDown : function() {}, // defaults to no action. onArrowUp : function() {}, // defaults to no action. onEnter : function() {}, // defaults to no action. onTab : function() {}, // defaults to no action. onChange: function() { rs.repaint() }, // defaults to repainting. startFrom: 0, options: [], wrapper : wrapper, // Only to allow easy access to the HTML elements to the final user (possibly for minor customizations) input : txtInput, // Only to allow easy access to the HTML elements to the final user (possibly for minor customizations) hint : txtHint, // Only to allow easy access to the HTML elements to the final user (possibly for minor customizations) dropDown : dropDown, // Only to allow easy access to the HTML elements to the final user (possibly for minor customizations) prompt : prompt, setText : function(text) { txtHint.value = text; txtInput.value = text; }, getText : function() { return txtInput.value; }, hideDropDown : function() { dropDownController.hide(); }, repaint : function() { var text = txtInput.value; var startFrom = rs.startFrom; var options = rs.options; var optionsLength = options.length; // breaking text in leftSide and token. var token = text.substring(startFrom); leftSide = text.substring(0,startFrom); // updating the hint. txtHint.value =''; for (var i=0;i<optionsLength;i++) { var opt = options[i]; if (opt.indexOf(token)===0) { // <-- how about upperCase vs. lowercase txtHint.value = leftSide +opt; break; } } // moving the dropDown and refreshing it. dropDown.style.left = calculateWidthForText(leftSide)+'px'; dropDownController.refresh(token, rs.options); } }; var registerOnTextChangeOldValue; /** * Register a callback function to detect changes to the content of the input-type-text. * Those changes are typically followed by user's action: a key-stroke event but sometimes it might be a mouse click. **/ var registerOnTextChange = function(txt, callback) { registerOnTextChangeOldValue = txt.value; var handler = function() { var value = txt.value; if (registerOnTextChangeOldValue !== value) { registerOnTextChangeOldValue = value; callback(value); } }; // // For user's actions, we listen to both input events and key up events // It appears that input events are not enough so we defensively listen to key up events too. // source: http://help.dottoro.com/ljhxklln.php // // The cost of listening to three sources should be negligible as the handler will invoke callback function // only if the text.value was effectively changed. // // if (txt.addEventListener) { txt.addEventListener("input", handler, false); txt.addEventListener('keyup', handler, false); txt.addEventListener('change', handler, false); } else { // is this a fair assumption: that attachEvent will exist ? txt.attachEvent('oninput', handler); // IE<9 txt.attachEvent('onkeyup', handler); // IE<9 txt.attachEvent('onchange',handler); // IE<9 } }; registerOnTextChange(txtInput,function(text) { // note the function needs to be wrapped as API-users will define their onChange rs.onChange(text); }); var keyDownHandler = function(e) { e = e || window.event; var keyCode = e.keyCode; if (keyCode == 33) { return; } // page up (do nothing) if (keyCode == 34) { return; } // page down (do nothing); if (keyCode == 27) { //escape dropDownController.hide(); txtHint.value = txtInput.value; // ensure that no hint is left. txtInput.focus(); return; } if (keyCode == 39 || keyCode == 35 || keyCode == 9) { // right, end, tab (autocomplete triggered) if (keyCode == 9) { // for tabs we need to ensure that we override the default behaviour: move to the next focusable HTML-element e.preventDefault(); e.stopPropagation(); if (txtHint.value.length == 0) { rs.onTab(); // tab was called with no action. // users might want to re-enable its default behaviour or handle the call somehow. } } if (txtHint.value.length > 0) { // if there is a hint dropDownController.hide(); txtInput.value = txtHint.value; var hasTextChanged = registerOnTextChangeOldValue != txtInput.value registerOnTextChangeOldValue = txtInput.value; // <-- to avoid dropDown to appear again. // for example imagine the array contains the following words: bee, beef, beetroot // user has hit enter to get 'bee' it would be prompted with the dropDown again (as beef and beetroot also match) if (hasTextChanged) { rs.onChange(txtInput.value); // <-- forcing it. } } return; } if (keyCode == 13) { // enter (autocomplete triggered) if (txtHint.value.length == 0) { // if there is a hint rs.onEnter(); } else { var wasDropDownHidden = (dropDown.style.visibility == 'hidden'); dropDownController.hide(); if (wasDropDownHidden) { txtHint.value = txtInput.value; // ensure that no hint is left. txtInput.focus(); rs.onEnter(); return; } txtInput.value = txtHint.value; var hasTextChanged = registerOnTextChangeOldValue != txtInput.value registerOnTextChangeOldValue = txtInput.value; // <-- to avoid dropDown to appear again. // for example imagine the array contains the following words: bee, beef, beetroot // user has hit enter to get 'bee' it would be prompted with the dropDown again (as beef and beetroot also match) if (hasTextChanged) { rs.onChange(txtInput.value); // <-- forcing it. } } return; } if (keyCode == 40) { // down var m = dropDownController.move(+1); if (m == '') { rs.onArrowDown(); } txtHint.value = leftSide+m; return; } if (keyCode == 38 ) { // up var m = dropDownController.move(-1); if (m == '') { rs.onArrowUp(); } txtHint.value = leftSide+m; e.preventDefault(); e.stopPropagation(); return; } // it's important to reset the txtHint on key down. // think: user presses a letter (e.g. 'x') and never releases... you get (xxxxxxxxxxxxxxxxx) // and you would see still the hint txtHint.value =''; // resets the txtHint. (it might be updated onKeyUp) }; if (txtInput.addEventListener) { txtInput.addEventListener("keydown", keyDownHandler, false); } else { // is this a fair assumption: that attachEvent will exist ? txtInput.attachEvent('onkeydown', keyDownHandler); // IE<9 } return rs; }