Initialize static value library property - javascript

I am trying to turn my code into a working library. (My first)
Currently you simply call GridNav() and it sets up and executes.
I am currently refactoring out all the necessary variables
The trouble I am having is the animations property. I want users to be able to over ride the property.
(function(window, document, $, undefined) {
'use strict';
var fixOutOfBoundCordinates = function(pos, max) {
if (pos < 1) {
pos = max;
} else if (pos > max) {
pos = 1
}
return pos;
};
var calculateDestination = function(position, direction, columns) {
var directions = {
1: [-1,-1],
2: [0,-1],
3: [1,-1],
4: [-1,0],
6: [1,0],
7: [-1,1],
8: [0,1],
9: [1,1]
};
direction = directions[direction];
var y = Math.ceil(position/columns);
var x = position - ((y-1) * columns);
x = fixOutOfBoundCordinates((x+direction[0]), columns);
y = fixOutOfBoundCordinates((y+direction[1]), columns);
return (x + ((y-1)*columns)) -1;
};
var GridNav = function(params) {
return new Library(params);
};
var Library = function(params) {
//var $main = document.querySelectorAll( '#pt-main' ),
var $main = $('#pt-main'),
$pages = $main.children( 'section.pt-page' ),
$iterate = $( '.panel' ),
pagesCount = $pages.length,
isAnimating = false,
endCurrPage = false,
endNextPage = false,
animEndEventNames = {
'WebkitAnimation' : 'webkitAnimationEnd',
'OAnimation' : 'oAnimationEnd',
'msAnimation' : 'MSAnimationEnd',
'animation' : 'animationend'
},
// animation end event name
animEndEventName = animEndEventNames[ 'animation'],
keys = {
BACKSPACE: 8,
DOWN: 40,
ENTER: 13,
LEFT: 37,
UP: 38,
RIGHT: 39,
SPACE: 32,
PAGE_DOWN: 34,
PAGE_UP: 33
};
function nextPage(outpage, direction ) {
if( isAnimating ) {
return false;
}
var cols = $main.data("col");
var inpage = calculateDestination(outpage, direction, cols);
isAnimating = true;
var $currPage = $pages.eq( outpage-1 ),
// Done early so element visible during animation
$nextPage = $pages.eq( inpage ).addClass( 'pt-page-current' ),
outClass = '', inClass = '';
$currPage.addClass( Library.animation[direction]["outClass"] ).on( animEndEventName, function() {
$currPage.off( animEndEventName );
endCurrPage = true;
if( endNextPage ) {
onEndAnimation( $currPage, $nextPage );
}
} );
$nextPage.addClass( Library.animation[direction]["inClass"] ).on( animEndEventName, function() {
$nextPage.off( animEndEventName );
endNextPage = true;
if( endCurrPage ) {
onEndAnimation( $currPage, $nextPage );
}
} );
}
function onEndAnimation( $outpage, $inpage ) {
endCurrPage = false;
endNextPage = false;
resetPage( $outpage, $inpage );
isAnimating = false;
}
function resetPage( $outpage, $inpage ) {
$outpage.attr( 'class', $outpage.data( 'originalClassList' ) );
$inpage.attr( 'class', $inpage.data( 'originalClassList' ) + ' pt-page-current' );
}
$pages.each( function() {
var $page = $( this );
$page.data( 'originalClassList', $page.attr( 'class' ) );
} );
// Use start class as begining
var start = $pages.index($pages.filter(".start"));
if (start == -1) {
start = Math.ceil(pagesCount/2)-1
} else {
}
$pages.eq(start).addClass('pt-page-current');
$( "body" ).keyup(function(event) {
var key = event.which;
var cur;
if ( key == keys.DOWN || key == keys.PAGE_DOWN ) {
cur = $('section.pt-page').filter(".pt-page-current").index() + 1;
nextPage( cur, 8);
}
if ( key == keys.UP || key == keys.PAGE_UP ) {
cur = $('section.pt-page').filter(".pt-page-current").index() + 1;
nextPage( cur, 2);
}
if ( key == keys.RIGHT || key == keys.SPACE || key == keys.ENTER ) {
cur = $('section.pt-page').filter(".pt-page-current").index() + 1;
nextPage( cur, 6);
}
if ( key == keys.LEFT || key == keys.BACKSPACE ) {
cur = $('section.pt-page').filter(".pt-page-current").index() + 1;
nextPage( cur, 4);
}
});
$iterate.on( 'click', function() {
var cur = $('section.pt-page').filter(".pt-page-current").index() + 1;
var direction = $iterate.index($(this)) + 1;
if (direction > 4) {
direction+= 1;
}
nextPage(cur, direction);
} );
return this;
};
Library.animation = {
1:
// Move UP and Left
{outClass: 'pt-page-moveToBottomRight',inClass: 'pt-page-moveFromTopLeft'},
2:
// Move UP
{outClass: 'pt-page-moveToBottom', inClass: 'pt-page-moveFromTop'},
3:
// Move UP and Right
{outClass: 'pt-page-moveToBottomLeft', inClass: 'pt-page-moveFromTopRight'},
4:
// Move Left
{outClass: 'pt-page-moveToRight', inClass: 'pt-page-moveFromLeft'},
6:
// Move Right
{outClass: 'pt-page-moveToLeft', inClass: 'pt-page-moveFromRight'},
7:
// Move Down and Left
{outClass: 'pt-page-moveToTopRight', inClass: 'pt-page-moveFromBottomLeft'},
8:
// Move Down
{outClass: 'pt-page-moveToTop', inClass: 'pt-page-moveFromBottom'},
9:
// Move Down and Right
{outClass: 'pt-page-moveToTopLeft', inClass: 'pt-page-moveFromBottomRight'}
};
//define globally if it doesn't already exist
if(!window.GridNav){
window.GridNav = GridNav;
}
else{
console.log("Library already defined.");
}
})(window, document, jQuery);
Here it is working:
codepen
Any other library recommendations/tips welcome.
Was sent here from code review

Related

Back to previous page same scroll position with lazy load images on page using jquery

Working on scroll position when user goes back to the previous page then same scroll position of the product will show. I have tried like this
$('.test').on('click', function(e){
stateData = {
path: window.location.href,
scrollTop: $(window).scrollTop()
};
const url = new URL(window.location);
window.history.replaceState(stateData, '', url);
stateData = {
path: window.location.href,
scrollTop: 0
};
window.history.pushState(stateData, '', url);
e.preventDefault();;
});
But here I'm not getting the exact position due to lazy load images to scroll moving at the bottom of the page. Can anyone suggest to me how to handle it?
In my view, the reason you are not able to reach the exact position is due to image rendering later thus changing the height of the screen. What if you add a skeleton for all the images to be loaded, that can help you to make the screen height similar to that of the one you have when everything is loaded.
Maybe someone can figure out how to use https://developer.mozilla.org/en-US/docs/Web/API/History/scrollRestoration .. but my basic idea is to remember the scrollTop for each page.
This means we have to include a value for each URL, upon returning to a page we then need load the lazy items that establish the proper scrollTop.
I have used this MDN example and this debounce script as base and will only provide the necessary code changes - I included a number of images on "first_page" and "second_page". I used to provide link to a working example, but this was taken offline.
js/lazyload.js
'use strict';
// supports older browsers - no arrow functions, spread syntax or such *sigh*
var pLAZY = ( function(){
var version = "1.1.1"
, loadOffset = 512
, animatable = [ "IMG", "IFRAME" ] // tags that may/should be animated, e.g. *not* SOURCE
, loadingClasses = [] // [ 'animated', 'rubberBand' ] // change/disable animation style here
, lazyItems = {}
, lazyHandler_scroll = null
, lazyHandler_resize = null
, forceLoad_timer = null
;
function _debug(){ if( arguments.length ){ debug = parseInt( arguments[0] ); } return debug; }
function _version(){ return version; }
function docScroll(){ return window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop || 0; } // cross-browser
function winHeight(){ return window.innerHeight || document.documentElement.clientHeight; }
function findLazyItems(){
lazyItems = {};
var candidates = document.querySelectorAll( '.lazyload,img[data-src],iframe[data-src],source[data-srcset]' );
for( var idx = 0; idx < candidates.length; idx++ ){
var domEl = candidates[idx]
, curTop = domEl.offsetTop // if you fail to ensure lazy loaded items do not force a reflow lower items may be loaded sooner
;
if( lazyItems.hasOwnProperty( curTop ) ){
lazyItems[curTop].push( domEl );
}else{
lazyItems[curTop] = [ domEl ];
}
}
}
function loadLazy(item){
var dsrc = item.getAttribute( 'data-src' )
, dset = item.getAttribute( 'data-srcset' )
, changed = false
, worked = false
;
if( dsrc && dsrc.length ){
item.setAttribute( 'src', dsrc );
item.removeAttribute( 'data-src' );
changed = true;
}
if( dset && dset.length ){
item.setAttribute( 'srcset', dset );
item.removeAttribute( 'data-srcset' );
changed = true;
}
if( changed ){
worked = true;
if( animatable.includes( item.nodeName ) ){
for( var lcx = 0; lcx < loadingClasses.length; lcx++ ){
item.classList.add( loadingClasses[lcx] );
}
}
}
return worked;
}
function checkShowItems(){
var curVisBar = docScroll() + winHeight() + loadOffset // winHeight, not docHeight!!
, worked = false
, str_pagePos = "0"
;
for( str_pagePos in lazyItems ){ // pagePos is a DOM.position().top value
var pagePos = parseInt( str_pagePos );
var lazy = lazyItems[ pagePos ]; // the items are constantly refreshed, double-effort is considered negligible
if( pagePos <= curVisBar ){
for( var idx = 0; idx < lazy.length; idx++ ){
if( loadLazy( lazy[idx] ) ) worked = true;
}
}
}
if( worked ){
findLazyItems();
if( Object.keys( lazyItems ).length == 0 ){
window.removeEventListener( 'scroll', lazyHandler_scroll );
window.removeEventListener( 'resize', lazyHandler_resize ); // this is not required anymore either
}
}
}
function waitForLoad(index,callback){
let count = 0
, done = 0
, str_pagePos = "0"
, worked = false
;
for( str_pagePos in lazyItems ){
var lazy = lazyItems[ parseInt( str_pagePos ) ];
for( let lx = 0; lx < lazy.length; lx++ ){
if( count <= index ){
if( lazy[lx].complete ) done++;
count++;
}
}
}
if( done == index ){
window.clearInterval( forceLoad_timer );
forceLoad_timer = null;
checkShowItems();
if(callback) callback();
}//else{ console.log( "%d/%d images done.", done, index ); }
}
function forceLoad(index,callback){
let count = 0
, str_pagePos = "0"
, worked = false
;
for( str_pagePos in lazyItems ){
var lazy = lazyItems[ parseInt( str_pagePos ) ];
for( let lx = 0; lx < lazy.length; lx++ ){
if( count <= index ){
if( loadLazy( lazy[lx] ) ) worked = true;
count++;
}
}
}
forceLoad_timer = window.setInterval( function(){ waitForLoad(index,callback); }, 50 );
}
function lowestSeenImage(){
var curVisBar = docScroll() + winHeight() + loadOffset // winHeight, not docHeight!!
, str_pagePos = "0"
, count = 0
;
for( str_pagePos in lazyItems ){
var pagePos = parseInt( str_pagePos );
var lazy = lazyItems[ pagePos ];
if( pagePos <= curVisBar ){
count += lazy.length;
}
}
return count;
}
function duringResizing(){
findLazyItems();
checkShowItems();
}
function hookListener( evtName, callback ){
var throttleTimer = arguments.length > 2 ? parseInt( arguments[2] ) : 250;
if( "Cowboy" in window ){
window.addEventListener( evtName, Cowboy.throttle( throttleTimer, callback ) );
}else{
//console.log( "without the Ben 'Cowboy' Almann throttle plugin we may choke the responsiveness of the user interface" );
window.addEventListener( evtName, callback );
}
}
function initialise(){
findLazyItems();
var seeTBjquery = ( ( "jQuery" in window ) && $.isFunction( $.throttle ) )
, seeTBplain = ( ( "Cowboy" in window ) && ( "throttle" in window.Cowboy ) )
;
if( seeTBjquery ){
lazyHandler_scroll = $.throttle( 250, checkShowItems );
lazyHandler_resize = $.throttle( 250, duringResizing );
}else{
if( seeTBplain ){
lazyHandler_scroll = Cowboy.throttle( 250, checkShowItems );
lazyHandler_resize = Cowboy.throttle( 250, duringResizing );
}else{
lazyHandler_scroll = checkShowItems;
lazyHandler_resize = duringResizing;
}
}
window.addEventListener( 'scroll', lazyHandler_scroll );
window.addEventListener( 'resize', lazyHandler_resize );
checkShowItems();
}
if( ! window.hasOwnProperty( "_pLAZY_disable_autoInit" ) ){
document.addEventListener( "DOMContentLoaded", function(){
initialise();
} );
}
return {
"debug": _debug
, "version": _version
, "findLazyItems": findLazyItems
, "checkShowItems": checkShowItems
, "forceLoad": forceLoad
, "lowestSeenImage": lowestSeenImage
, "duringResizing": duringResizing
, "hookListener": hookListener
, "initialize": initialise // american spelling
, "initialise": initialise
};
} )();
js/ajax_nav.js
"use strict";
const ajaxRequest = new (function () {
function closeReq () {
oLoadingBox.parentNode && document.body.removeChild(oLoadingBox);
bIsLoading = false;
}
function abortReq () {
if (!bIsLoading) { return; }
oReq.abort();
closeReq();
}
function ajaxError () {
alert("Unknown error.");
}
// NOT PART OF MDN EXAMPLE::
function restoreScroll(){
if( oPageInfo.url in oPageInfo.scrollTop ){
window.scrollTo( 0, oPageInfo.scrollTop[oPageInfo.url] ); // always left-most
}
}
function loadAndScroll(){
if( "pLAZY" in window ) pLAZY.forceLoad( oPageInfo.lowestSeenImage[oPageInfo.url], restoreScroll );
}
// ::NOT PART OF MDN EXAMPLE
function ajaxLoad () {
var vMsg, nStatus = this.status;
switch (nStatus) {
case 200:
vMsg = JSON.parse(this.responseText);
document.title = oPageInfo.title = vMsg.page;
document.getElementById(sTargetId).innerHTML = vMsg.content;
if (bUpdateURL) {
history.pushState(oPageInfo, oPageInfo.title, oPageInfo.url);
bUpdateURL = false;
// NOT PART OF MDN EXAMPLE::
if( "pLAZY" in window ){
window.pLAZY.initialise();
if( oPageInfo.lowestSeenImage[oPageInfo.url] > -1 ){
loadAndScroll();
}else{
restoreScroll();
}
}//else console.log("without pLAZY no scrollPos restore possible");
ajaxRequest.rebuildLinks();
// ::NOT PART OF MDN EXAMPLE
}
break;
default:
vMsg = nStatus + ": " + (oHTTPStatus[nStatus] || "Unknown");
switch (Math.floor(nStatus / 100)) {
/*
case 1:
// Informational 1xx
console.log("Information code " + vMsg);
break;
case 2:
// Successful 2xx
console.log("Successful code " + vMsg);
break;
case 3:
// Redirection 3xx
console.log("Redirection code " + vMsg);
break;
*/
case 4:
/* Client Error 4xx */
alert("Client Error #" + vMsg);
break;
case 5:
/* Server Error 5xx */
alert("Server Error #" + vMsg);
break;
default:
/* Unknown status */
ajaxError();
}
}
closeReq();
}
function filterURL (sURL, sViewMode) {
return sURL.replace(rSearch, "") + ("?" + sURL.replace(rHost, "&").replace(rView, sViewMode ? "&" + sViewKey + "=" + sViewMode : "").slice(1)).replace(rEndQstMark, "");
}
function getPage (sPage) {
if (bIsLoading) { return; }
oReq = new XMLHttpRequest();
bIsLoading = true;
oReq.onload = ajaxLoad;
oReq.onerror = ajaxError;
// NOT IN MDN EXAMPLE ::
oPageInfo.scrollTop[oPageInfo.url] = document.documentElement.scrollTop;
oPageInfo.lowestSeenImage[oPageInfo.url] = ( "pLAZY" in window ) ? pLAZY.lowestSeenImage() : 0;
// :: NOT IN MDN EXAMPLE
if (sPage) { oPageInfo.url = filterURL(sPage, null); }
oReq.open("get", filterURL(oPageInfo.url, "json"), true);
oReq.send();
oLoadingBox.parentNode || document.body.appendChild(oLoadingBox);
}
function requestPage (sURL) {
if (history.pushState) {
bUpdateURL = true;
getPage(sURL);
} else {
/* Ajax navigation is not supported */
location.assign(sURL);
}
}
function processLink () {
if (this.className === sAjaxClass) {
requestPage(this.href);
return false;
}
return true;
}
function init () {
oPageInfo.title = document.title;
for (var oLink, nIdx = 0, nLen = document.links.length; nIdx < nLen; document.links[nIdx++].onclick = processLink);
}
const
/* customizable constants */
sTargetId = "ajax-content", sViewKey = "view_as", sAjaxClass = "ajax-nav",
/* not customizable constants */
rSearch = /\?.*$/, rHost = /^[^\?]*\?*&*/, rView = new RegExp("&" + sViewKey + "\\=[^&]*|&*$", "i"), rEndQstMark = /\?$/,
oLoadingBox = document.createElement("div"), oCover = document.createElement("div"), oLoadingImg = new Image(),
oPageInfo = {
title: null,
scrollTop: {}, // NOT PART OF MDN EXAMPLE
lowestSeenImage: {}, // NOT PART OF MDN EXAMPLE
url: location.href
}, oHTTPStatus = /* http://www.iana.org/assignments/http-status-codes/http-status-codes.xml */ {
100: "Continue",
101: "Switching Protocols",
102: "Processing",
200: "OK",
201: "Created",
202: "Accepted",
203: "Non-Authoritative Information",
204: "No Content",
205: "Reset Content",
206: "Partial Content",
207: "Multi-Status",
208: "Already Reported",
226: "IM Used",
300: "Multiple Choices",
301: "Moved Permanently",
302: "Found",
303: "See Other",
304: "Not Modified",
305: "Use Proxy",
306: "Reserved",
307: "Temporary Redirect",
308: "Permanent Redirect",
400: "Bad Request",
401: "Unauthorized",
402: "Payment Required",
403: "Forbidden",
404: "Not Found",
405: "Method Not Allowed",
406: "Not Acceptable",
407: "Proxy Authentication Required",
408: "Request Timeout",
409: "Conflict",
410: "Gone",
411: "Length Required",
412: "Precondition Failed",
413: "Request Entity Too Large",
414: "Request-URI Too Long",
415: "Unsupported Media Type",
416: "Requested Range Not Satisfiable",
417: "Expectation Failed",
422: "Unprocessable Entity",
423: "Locked",
424: "Failed Dependency",
425: "Unassigned",
426: "Upgrade Required",
427: "Unassigned",
428: "Precondition Required",
429: "Too Many Requests",
430: "Unassigned",
431: "Request Header Fields Too Large",
500: "Internal Server Error",
501: "Not Implemented",
502: "Bad Gateway",
503: "Service Unavailable",
504: "Gateway Timeout",
505: "HTTP Version Not Supported",
506: "Variant Also Negotiates (Experimental)",
507: "Insufficient Storage",
508: "Loop Detected",
509: "Unassigned",
510: "Not Extended",
511: "Network Authentication Required"
};
var
oReq, bIsLoading = false, bUpdateURL = false;
oLoadingBox.id = "ajax-loader";
oCover.onclick = abortReq;
oLoadingImg.src = "data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==";
oCover.appendChild(oLoadingImg);
oLoadingBox.appendChild(oCover);
onpopstate = function (oEvent) {
bUpdateURL = false;
oPageInfo.title = oEvent.state.title;
oPageInfo.scrollTop = oEvent.state.scrollTop; // NOT PART OF MDN EXAMPLE
oPageInfo.lowestSeenImage = oEvent.state.lowestSeenImage; // NOT PART OF MDN EXAMPLE
oPageInfo.url = oEvent.state.url;
getPage();
};
window.addEventListener ? addEventListener("load", init, false) : window.attachEvent ? attachEvent("onload", init) : (onload = init);
// Public methods
this.open = requestPage;
this.stop = abortReq;
this.rebuildLinks = init;
})();
Perhaps save the scroll position in localStorage and then go to it when the page is reloaded?
as seen here
Check if the key exists (and if it does - scroll to it) right after DOMContentLoaded, i think, otherwise it will wait for all the images to load.
You can also choose to save the scroll position after every scroll, and not only on unload

Autocomplete Textbox fetching suggestions from database?

I have created 2 autosuggest textboxes in which one is fetching data from server-side and another is fetching data from client-side.But server-side textbox is not working.Can anyone point out the error in my code.
Following is my HTML Code:
<html>
<link type="text/css" rel="stylesheet" media="all" href="jquery-ui-1.8.9.custom.css" />
<script type="text/javascript" src="jquery-1.4.4.min.js"></script>
<script type="text/javascript" src="jquery-ui-1.8.9.custom.min.js"></script>
<script type="text/javascript" src="languages.js"></script>
<body>
<label for="languagesClientInput">Select Language(client-side): </label>
<input id="languagesClientInput" />
<br />
<label for="languagesServerInput">Select Language (server-side): </label>
<input id="languagesServerInput" />
</body>
</html>
Following is Js Code:
$(function() {
var languages = [
"C",
"C++",
"Core Java",
"Advance Java",
"PHP",
".NET",
"XML",
"HTML",
"Javascript",
"jQuery",
"JSON",
"Ajax",
"C#",
"ABC ALGOL",
"ADA",
"ABLE",
"COBOL",
"BLUE",
"Pearl",
"Python",
"Oracle",
"Haskell",
"BASIC",
"BeanShell",
"Bliss",
"BETA",
"GOTRAN",
"FORTRAN",
"Focal",
"Genie",
"GOAL",
"GROOVY",
"JOSS",
"JEAN",
"JOVIAL",
"JOY",
"Maple",
"MATLAB",
"MORTAN",
"MUMPS",
"Miranda",
"NetRexx",
"NPL",
"NXT-G"
];
$("#languagesClientInput").autocomplete( { source: languages });
$("#languagesServerInput").autocomplete( { source: "languages.php" });
});
Following is Php Code:
<?php
$searchTerm = $_GET['term'];
$languages = array(
"C",
"C++",
"Core Java",
"Advance Java",
"PHP",
".NET",
"XML",
"HTML",
"Javascript",
"jQuery",
"JSON",
"Ajax",
"C#",
"ABC ALGOL",
"ADA",
"ABLE",
"COBOL",
"BLUE",
"Pearl",
"Python",
"Oracle",
"Haskell",
"BASIC",
"BeanShell",
"Bliss",
"BETA",
"GOTRAN",
"FORTRAN",
"Focal",
"Genie",
"GOAL",
"GROOVY",
"JOSS",
"JEAN",
"JOVIAL",
"JOY",
"Maple",
"MATLAB",
"MORTAN",
"MUMPS",
"Miranda",
"NetRexx",
"NPL",
"NXT-G"
);
function filter($language) {
global $searchTerm;
return stripos($language, $searchTerm) !== false;
}
print(json_encode(array_values(array_filter($languages, "filter"))));
?>
The array_filter function fails. If you remove it you will get the data you need.
I actually think you have made a mistake there by writing array_filter instead of just filter to call the function you wrote above.
Run this command in MySQL database;
CREATE TABLE `tag` (
`id` int(20) NOT NULL auto_increment,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=10 ;
INSERT INTO `tag` (`id`, `name`) VALUES
(1, 'php'),
(2, 'php frameword'),
(3, 'php tutorial'),
(4, 'jquery'),
(5, 'ajax'),
(6, 'mysql'),
(7, 'wordpress'),
(8, 'wordpress theme'),
(9, 'xml');
index.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Auto Complete Input box</title>
<link rel="stylesheet" type="text/css" href="jquery.autocomplete.css" />
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="jquery.autocomplete.js"></script>
<script>
$(document).ready(function(){
$("#tag").autocomplete("autocomplete.php", {
selectFirst: true
});
});
</script>
</head>
<body>
<label>Tag:</label>
<input name="tag" type="text" id="tag" size="20"/>
</body>
</html>
autocomplete.php
<?php
$q=$_GET['q'];
$my_data=mysql_real_escape_string($q);
$mysqli=mysqli_connect('localhost','root','','autofield') or die("Database Error");
$sql="SELECT name FROM tag WHERE name LIKE '%$my_data%' ORDER BY name";
$result = mysqli_query($mysqli,$sql) or die(mysqli_error());
if($result)
{
while($row=mysqli_fetch_array($result))
{
echo $row['name']."\n";
}
}
?>
jquery.autocomplete.css
.ac_results {
padding: 0px;
border: 1px solid black;
background-color: white;
overflow: hidden;
z-index: 99999;
}
.ac_results ul {
width: 100%;
list-style-position: outside;
list-style: none;
padding: 0;
margin: 0;
}
.ac_results li {
margin: 0px;
padding: 2px 5px;
cursor: default;
display: block;
/*
if width will be 100% horizontal scrollbar will apear
when scroll mode will be used
*/
/*width: 100%;*/
font: menu;
font-size: 12px;
/*
it is very important, if line-height not setted or setted
in relative units scroll will be broken in firefox
*/
line-height: 16px;
overflow: hidden;
}
.ac_loading {
background: white url('indicator.gif') right center no-repeat;
}
.ac_odd {
background-color: #eee;
}
.ac_over {
background-color: #0A246A;
color: white;
}
jquery.autocomplete.js
/*
* jQuery Autocomplete plugin 1.1
*
* Copyright (c) 2009 Jörn Zaefferer
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
* Revision: $Id: jquery.autocomplete.js 15 2009-08-22 10:30:27Z joern.zaefferer $
*/
;(function($) {
$.fn.extend({
autocomplete: function(urlOrData, options) {
var isUrl = typeof urlOrData == "string";
options = $.extend({}, $.Autocompleter.defaults, {
url: isUrl ? urlOrData : null,
data: isUrl ? null : urlOrData,
delay: isUrl ? $.Autocompleter.defaults.delay : 10,
max: options && !options.scroll ? 10 : 150
}, options);
// if highlight is set to false, replace it with a do-nothing function
options.highlight = options.highlight || function(value) { return value; };
// if the formatMatch option is not specified, then use formatItem for backwards compatibility
options.formatMatch = options.formatMatch || options.formatItem;
return this.each(function() {
new $.Autocompleter(this, options);
});
},
result: function(handler) {
return this.bind("result", handler);
},
search: function(handler) {
return this.trigger("search", [handler]);
},
flushCache: function() {
return this.trigger("flushCache");
},
setOptions: function(options){
return this.trigger("setOptions", [options]);
},
unautocomplete: function() {
return this.trigger("unautocomplete");
}
});
$.Autocompleter = function(input, options) {
var KEY = {
UP: 38,
DOWN: 40,
DEL: 46,
TAB: 9,
RETURN: 13,
ESC: 27,
COMMA: 188,
PAGEUP: 33,
PAGEDOWN: 34,
BACKSPACE: 8
};
// Create $ object for input element
var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
var timeout;
var previousValue = "";
var cache = $.Autocompleter.Cache(options);
var hasFocus = 0;
var lastKeyPressCode;
var config = {
mouseDownOnSelect: false
};
var select = $.Autocompleter.Select(options, input, selectCurrent, config);
var blockSubmit;
// prevent form submit in opera when selecting with return key
$.browser.opera && $(input.form).bind("submit.autocomplete", function() {
if (blockSubmit) {
blockSubmit = false;
return false;
}
});
// only opera doesn't trigger keydown multiple times while pressed, others don't work with keypress at all
$input.bind(($.browser.opera ? "keypress" : "keydown") + ".autocomplete", function(event) {
// a keypress means the input has focus
// avoids issue where input had focus before the autocomplete was applied
hasFocus = 1;
// track last key pressed
lastKeyPressCode = event.keyCode;
switch(event.keyCode) {
case KEY.UP:
event.preventDefault();
if ( select.visible() ) {
select.prev();
} else {
onChange(0, true);
}
break;
case KEY.DOWN:
event.preventDefault();
if ( select.visible() ) {
select.next();
} else {
onChange(0, true);
}
break;
case KEY.PAGEUP:
event.preventDefault();
if ( select.visible() ) {
select.pageUp();
} else {
onChange(0, true);
}
break;
case KEY.PAGEDOWN:
event.preventDefault();
if ( select.visible() ) {
select.pageDown();
} else {
onChange(0, true);
}
break;
// matches also semicolon
case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
case KEY.TAB:
case KEY.RETURN:
if( selectCurrent() ) {
// stop default to prevent a form submit, Opera needs special handling
event.preventDefault();
blockSubmit = true;
return false;
}
break;
case KEY.ESC:
select.hide();
break;
default:
clearTimeout(timeout);
timeout = setTimeout(onChange, options.delay);
break;
}
}).focus(function(){
// track whether the field has focus, we shouldn't process any
// results if the field no longer has focus
hasFocus++;
}).blur(function() {
hasFocus = 0;
if (!config.mouseDownOnSelect) {
hideResults();
}
}).click(function() {
// show select when clicking in a focused field
if ( hasFocus++ > 1 && !select.visible() ) {
onChange(0, true);
}
}).bind("search", function() {
// TODO why not just specifying both arguments?
var fn = (arguments.length > 1) ? arguments[1] : null;
function findValueCallback(q, data) {
var result;
if( data && data.length ) {
for (var i=0; i < data.length; i++) {
if( data[i].result.toLowerCase() == q.toLowerCase() ) {
result = data[i];
break;
}
}
}
if( typeof fn == "function" ) fn(result);
else $input.trigger("result", result && [result.data, result.value]);
}
$.each(trimWords($input.val()), function(i, value) {
request(value, findValueCallback, findValueCallback);
});
}).bind("flushCache", function() {
cache.flush();
}).bind("setOptions", function() {
$.extend(options, arguments[1]);
// if we've updated the data, repopulate
if ( "data" in arguments[1] )
cache.populate();
}).bind("unautocomplete", function() {
select.unbind();
$input.unbind();
$(input.form).unbind(".autocomplete");
});
function selectCurrent() {
var selected = select.selected();
if( !selected )
return false;
var v = selected.result;
previousValue = v;
if ( options.multiple ) {
var words = trimWords($input.val());
if ( words.length > 1 ) {
var seperator = options.multipleSeparator.length;
var cursorAt = $(input).selection().start;
var wordAt, progress = 0;
$.each(words, function(i, word) {
progress += word.length;
if (cursorAt <= progress) {
wordAt = i;
return false;
}
progress += seperator;
});
words[wordAt] = v;
// TODO this should set the cursor to the right position, but it gets overriden somewhere
//$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
v = words.join( options.multipleSeparator );
}
v += options.multipleSeparator;
}
$input.val(v);
hideResultsNow();
$input.trigger("result", [selected.data, selected.value]);
return true;
}
function onChange(crap, skipPrevCheck) {
if( lastKeyPressCode == KEY.DEL ) {
select.hide();
return;
}
var currentValue = $input.val();
if ( !skipPrevCheck && currentValue == previousValue )
return;
previousValue = currentValue;
currentValue = lastWord(currentValue);
if ( currentValue.length >= options.minChars) {
$input.addClass(options.loadingClass);
if (!options.matchCase)
currentValue = currentValue.toLowerCase();
request(currentValue, receiveData, hideResultsNow);
} else {
stopLoading();
select.hide();
}
};
function trimWords(value) {
if (!value)
return [""];
if (!options.multiple)
return [$.trim(value)];
return $.map(value.split(options.multipleSeparator), function(word) {
return $.trim(value).length ? $.trim(word) : null;
});
}
function lastWord(value) {
if ( !options.multiple )
return value;
var words = trimWords(value);
if (words.length == 1)
return words[0];
var cursorAt = $(input).selection().start;
if (cursorAt == value.length) {
words = trimWords(value)
} else {
words = trimWords(value.replace(value.substring(cursorAt), ""));
}
return words[words.length - 1];
}
// fills in the input box w/the first match (assumed to be the best match)
// q: the term entered
// sValue: the first matching result
function autoFill(q, sValue){
// autofill in the complete box w/the first match as long as the user hasn't entered in more data
// if the last user key pressed was backspace, don't autofill
if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
// fill in the value (keep the case the user has typed)
$input.val($input.val() + sValue.substring(lastWord(previousValue).length));
// select the portion of the value not typed by the user (so the next character will erase)
$(input).selection(previousValue.length, previousValue.length + sValue.length);
}
};
function hideResults() {
clearTimeout(timeout);
timeout = setTimeout(hideResultsNow, 200);
};
function hideResultsNow() {
var wasVisible = select.visible();
select.hide();
clearTimeout(timeout);
stopLoading();
if (options.mustMatch) {
// call search and run callback
$input.search(
function (result){
// if no value found, clear the input box
if( !result ) {
if (options.multiple) {
var words = trimWords($input.val()).slice(0, -1);
$input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
}
else {
$input.val( "" );
$input.trigger("result", null);
}
}
}
);
}
};
function receiveData(q, data) {
if ( data && data.length && hasFocus ) {
stopLoading();
select.display(data, q);
autoFill(q, data[0].value);
select.show();
} else {
hideResultsNow();
}
};
function request(term, success, failure) {
if (!options.matchCase)
term = term.toLowerCase();
var data = cache.load(term);
// recieve the cached data
if (data && data.length) {
success(term, data);
// if an AJAX url has been supplied, try loading the data now
} else if( (typeof options.url == "string") && (options.url.length > 0) ){
var extraParams = {
timestamp: +new Date()
};
$.each(options.extraParams, function(key, param) {
extraParams[key] = typeof param == "function" ? param() : param;
});
$.ajax({
// try to leverage ajaxQueue plugin to abort previous requests
mode: "abort",
// limit abortion to this input
port: "autocomplete" + input.name,
dataType: options.dataType,
url: options.url,
data: $.extend({
q: lastWord(term),
limit: options.max
}, extraParams),
success: function(data) {
var parsed = options.parse && options.parse(data) || parse(data);
cache.add(term, parsed);
success(term, parsed);
}
});
} else {
// if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
select.emptyList();
failure(term);
}
};
function parse(data) {
var parsed = [];
var rows = data.split("\n");
for (var i=0; i < rows.length; i++) {
var row = $.trim(rows[i]);
if (row) {
row = row.split("|");
parsed[parsed.length] = {
data: row,
value: row[0],
result: options.formatResult && options.formatResult(row, row[0]) || row[0]
};
}
}
return parsed;
};
function stopLoading() {
$input.removeClass(options.loadingClass);
};
};
$.Autocompleter.defaults = {
inputClass: "ac_input",
resultsClass: "ac_results",
loadingClass: "ac_loading",
minChars: 1,
delay: 400,
matchCase: false,
matchSubset: true,
matchContains: false,
cacheLength: 10,
max: 100,
mustMatch: false,
extraParams: {},
selectFirst: true,
formatItem: function(row) { return row[0]; },
formatMatch: null,
autoFill: false,
width: 0,
multiple: false,
multipleSeparator: ", ",
highlight: function(value, term) {
return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
},
scroll: true,
scrollHeight: 180
};
$.Autocompleter.Cache = function(options) {
var data = {};
var length = 0;
function matchSubset(s, sub) {
if (!options.matchCase)
s = s.toLowerCase();
var i = s.indexOf(sub);
if (options.matchContains == "word"){
i = s.toLowerCase().search("\\b" + sub.toLowerCase());
}
if (i == -1) return false;
return i == 0 || options.matchContains;
};
function add(q, value) {
if (length > options.cacheLength){
flush();
}
if (!data[q]){
length++;
}
data[q] = value;
}
function populate(){
if( !options.data ) return false;
// track the matches
var stMatchSets = {},
nullData = 0;
// no url was specified, we need to adjust the cache length to make sure it fits the local data store
if( !options.url ) options.cacheLength = 1;
// track all options for minChars = 0
stMatchSets[""] = [];
// loop through the array and create a lookup structure
for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
var rawValue = options.data[i];
// if rawValue is a string, make an array otherwise just reference the array
rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
var value = options.formatMatch(rawValue, i+1, options.data.length);
if ( value === false )
continue;
var firstChar = value.charAt(0).toLowerCase();
// if no lookup array for this character exists, look it up now
if( !stMatchSets[firstChar] )
stMatchSets[firstChar] = [];
// if the match is a string
var row = {
value: value,
data: rawValue,
result: options.formatResult && options.formatResult(rawValue) || value
};
// push the current match into the set list
stMatchSets[firstChar].push(row);
// keep track of minChars zero items
if ( nullData++ < options.max ) {
stMatchSets[""].push(row);
}
};
// add the data items to the cache
$.each(stMatchSets, function(i, value) {
// increase the cache size
options.cacheLength++;
// add to the cache
add(i, value);
});
}
// populate any existing data
setTimeout(populate, 25);
function flush(){
data = {};
length = 0;
}
return {
flush: flush,
add: add,
populate: populate,
load: function(q) {
if (!options.cacheLength || !length)
return null;
/*
* if dealing w/local data and matchContains than we must make sure
* to loop through all the data collections looking for matches
*/
if( !options.url && options.matchContains ){
// track all matches
var csub = [];
// loop through all the data grids for matches
for( var k in data ){
// don't search through the stMatchSets[""] (minChars: 0) cache
// this prevents duplicates
if( k.length > 0 ){
var c = data[k];
$.each(c, function(i, x) {
// if we've got a match, add it to the array
if (matchSubset(x.value, q)) {
csub.push(x);
}
});
}
}
return csub;
} else
// if the exact item exists, use it
if (data[q]){
return data[q];
} else
if (options.matchSubset) {
for (var i = q.length - 1; i >= options.minChars; i--) {
var c = data[q.substr(0, i)];
if (c) {
var csub = [];
$.each(c, function(i, x) {
if (matchSubset(x.value, q)) {
csub[csub.length] = x;
}
});
return csub;
}
}
}
return null;
}
};
};
$.Autocompleter.Select = function (options, input, select, config) {
var CLASSES = {
ACTIVE: "ac_over"
};
var listItems,
active = -1,
data,
term = "",
needsInit = true,
element,
list;
// Create results
function init() {
if (!needsInit)
return;
element = $("<div/>")
.hide()
.addClass(options.resultsClass)
.css("position", "absolute")
.appendTo(document.body);
list = $("<ul/>").appendTo(element).mouseover( function(event) {
if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
$(target(event)).addClass(CLASSES.ACTIVE);
}
}).click(function(event) {
$(target(event)).addClass(CLASSES.ACTIVE);
select();
// TODO provide option to avoid setting focus again after selection? useful for cleanup-on-focus
input.focus();
return false;
}).mousedown(function() {
config.mouseDownOnSelect = true;
}).mouseup(function() {
config.mouseDownOnSelect = false;
});
if( options.width > 0 )
element.css("width", options.width);
needsInit = false;
}
function target(event) {
var element = event.target;
while(element && element.tagName != "LI")
element = element.parentNode;
// more fun with IE, sometimes event.target is empty, just ignore it then
if(!element)
return [];
return element;
}
function moveSelect(step) {
listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
movePosition(step);
var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
if(options.scroll) {
var offset = 0;
listItems.slice(0, active).each(function() {
offset += this.offsetHeight;
});
if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
} else if(offset < list.scrollTop()) {
list.scrollTop(offset);
}
}
};
function movePosition(step) {
active += step;
if (active < 0) {
active = listItems.size() - 1;
} else if (active >= listItems.size()) {
active = 0;
}
}
function limitNumberOfItems(available) {
return options.max && options.max < available
? options.max
: available;
}
function fillList() {
list.empty();
var max = limitNumberOfItems(data.length);
for (var i=0; i < max; i++) {
if (!data[i])
continue;
var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
if ( formatted === false )
continue;
var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
$.data(li, "ac_data", data[i]);
}
listItems = list.find("li");
if ( options.selectFirst ) {
listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
active = 0;
}
// apply bgiframe if available
if ( $.fn.bgiframe )
list.bgiframe();
}
return {
display: function(d, q) {
init();
data = d;
term = q;
fillList();
},
next: function() {
moveSelect(1);
},
prev: function() {
moveSelect(-1);
},
pageUp: function() {
if (active != 0 && active - 8 < 0) {
moveSelect( -active );
} else {
moveSelect(-8);
}
},
pageDown: function() {
if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
moveSelect( listItems.size() - 1 - active );
} else {
moveSelect(8);
}
},
hide: function() {
element && element.hide();
listItems && listItems.removeClass(CLASSES.ACTIVE);
active = -1;
},
visible : function() {
return element && element.is(":visible");
},
current: function() {
return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
},
show: function() {
var offset = $(input).offset();
element.css({
width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
top: offset.top + input.offsetHeight,
left: offset.left
}).show();
if(options.scroll) {
list.scrollTop(0);
list.css({
maxHeight: options.scrollHeight,
overflow: 'auto'
});
if($.browser.msie && typeof document.body.style.maxHeight === "undefined") {
var listHeight = 0;
listItems.each(function() {
listHeight += this.offsetHeight;
});
var scrollbarsVisible = listHeight > options.scrollHeight;
list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
if (!scrollbarsVisible) {
// IE doesn't recalculate width when scrollbar disappears
listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
}
}
}
},
selected: function() {
var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
return selected && selected.length && $.data(selected[0], "ac_data");
},
emptyList: function (){
list && list.empty();
},
unbind: function() {
element && element.remove();
}
};
};
$.fn.selection = function(start, end) {
if (start !== undefined) {
return this.each(function() {
if( this.createTextRange ){
var selRange = this.createTextRange();
if (end === undefined || start == end) {
selRange.move("character", start);
selRange.select();
} else {
selRange.collapse(true);
selRange.moveStart("character", start);
selRange.moveEnd("character", end);
selRange.select();
}
} else if( this.setSelectionRange ){
this.setSelectionRange(start, end);
} else if( this.selectionStart ){
this.selectionStart = start;
this.selectionEnd = end;
}
});
}
var field = this[0];
if ( field.createTextRange ) {
var range = document.selection.createRange(),
orig = field.value,
teststring = "<->",
textLength = range.text.length;
range.text = teststring;
var caretAt = field.value.indexOf(teststring);
field.value = orig;
this.selection(caretAt, caretAt + textLength);
return {
start: caretAt,
end: caretAt + textLength
}
} else if( field.selectionStart !== undefined ){
return {
start: field.selectionStart,
end: field.selectionEnd
}
}
};
})(jQuery);
Download jquery.min.js and rename it to jquery.js
Now open index.php in your localhost/server and test it. Follow the similar method with your wordlist.
You don't need the array_values function. This worked for me:
// languages.php
<?php
function filter($language) {
global $searchTerm;
return stripos( $language, $searchTerm ) !== false;
}
$searchTerm = $_GET['term'];
$languages = array(
"C",
"C++",
"Core Java",
"Advance Java",
"PHP",
".NET",
"XML",
"HTML",
"Javascript",
"jQuery",
"JSON",
"Ajax",
"C#",
"ABC ALGOL",
"ADA",
"ABLE",
"COBOL",
"BLUE",
"Pearl",
"Python",
"Oracle",
"Haskell",
"BASIC",
"BeanShell",
"Bliss",
"BETA",
"GOTRAN",
"FORTRAN",
"Focal",
"Genie",
"GOAL",
"GROOVY",
"JOSS",
"JEAN",
"JOVIAL",
"JOY",
"Maple",
"MATLAB",
"MORTAN",
"MUMPS",
"Miranda",
"NetRexx",
"NPL",
"NXT-G"
);
$jsonData = array_filter( $languages, "filter" );
print( json_encode( $jsonData ) );
?>

jQuery SVG animate : scroll and fadeIn

I'm trying to implement a codrops plugin I found and it's working quite well but I have trouble when it comes to improve it.
http://tympanus.net/codrops/2013/12/30/svg-drawing-animation/
My page is divided into three sections, when you click on one of the three sections on the menu, the content fades in.
http://alexandrebeaumont.com/so/layout.png
When you scroll down my animations load perfectly but I would like them to be reloaded each time you click on the menu because now, if I scroll in one section, the animations are launched in everyone of them...
EDIT I pasted the whole code since after reading again the script I'm not sure at all what could giver me my solution
(function() {
'use strict';
var docElem = window.document.documentElement;
window.requestAnimFrame = function(){
return (
window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback){
window.setTimeout(callback, 1000 / 60);
}
);
}();
window.cancelAnimFrame = function(){
return (
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.oCancelAnimationFrame ||
window.msCancelAnimationFrame ||
function(id){
window.clearTimeout(id);
}
);
}();
function SVGEl( el ) {
this.el = el;
this.image = this.el.previousElementSibling;
this.current_frame = 0;
this.total_frames = 60;
this.path = new Array();
this.length = new Array();
this.handle = 0;
this.init();
}
SVGEl.prototype.init = function() {
var self = this;
[].slice.call( this.el.querySelectorAll( 'path' ) ).forEach( function( path, i ) {
self.path[i] = path;
var l = self.path[i].getTotalLength();
self.length[i] = l;
self.path[i].style.strokeDasharray = l + ' ' + l;
self.path[i].style.strokeDashoffset = l;
} );
};
SVGEl.prototype.render = function() {
if( this.rendered ) return;
this.rendered = true;
this.draw();
};
SVGEl.prototype.draw = function() {
var self = this,
progress = this.current_frame/this.total_frames;
if (progress > 1) {
window.cancelAnimFrame(this.handle);
this.showImage();
} else {
this.current_frame++;
for(var j=0, len = this.path.length; j<len;j++){
this.path[j].style.strokeDashoffset = Math.floor(this.length[j] * (1 - progress));
}
this.handle = window.requestAnimFrame(function() { self.draw(); });
}
};
SVGEl.prototype.showImage = function() {
classie.add( this.image, 'show' );
classie.add( this.el, 'hide' );
};
function getViewportH() {
var client = docElem['clientHeight'],
inner = window['innerHeight'];
if( client < inner )
return inner;
else
return client;
}
function scrollY() {
return window.pageYOffset || docElem.scrollTop;
}
// http://stackoverflow.com/a/5598797/989439
function getOffset( el ) {
var offsetTop = 0, offsetLeft = 0;
do {
if ( !isNaN( el.offsetTop ) ) {
offsetTop += el.offsetTop;
}
if ( !isNaN( el.offsetLeft ) ) {
offsetLeft += el.offsetLeft;
}
} while( el = el.offsetParent )
return {
top : offsetTop,
left : offsetLeft
};
}
function inViewport( el, h ) {
var elH = el.offsetHeight,
scrolled = scrollY(),
viewed = scrolled + getViewportH(),
elTop = getOffset(el).top,
elBottom = elTop + elH,
// if 0, the element is considered in the viewport as soon as it enters.
// if 1, the element is considered in the viewport only when it's fully inside
// value in percentage (1 >= h >= 0)
h = h || 0;
return (elTop + elH * h) <= viewed && (elBottom) >= scrolled;
}
function init() {
var svgs = Array.prototype.slice.call( document.querySelectorAll( '#main svg' ) ),
svgArr = new Array(),
didScroll = false,
resizeTimeout;
// the svgs already shown...
svgs.forEach( function( el, i ) {
var svg = new SVGEl( el );
svgArr[i] = svg;
setTimeout(function( el ) {
return function() {
if( inViewport( el.parentNode ) ) {
svg.render();
}
};
}( el ), 250 );
} );
var scrollHandler = function() {
if( !didScroll ) {
didScroll = true;
setTimeout( function() { scrollPage(); }, 60 );
}
},
scrollPage = function() {
svgs.forEach( function( el, i ) {
if( inViewport( el.parentNode, 0.5 ) ) {
svgArr[i].render();
}
});
didScroll = false;
},
resizeHandler = function() {
function delayed() {
scrollPage();
resizeTimeout = null;
}
if ( resizeTimeout ) {
clearTimeout( resizeTimeout );
}
resizeTimeout = setTimeout( delayed, 200 );
};
window.addEventListener( 'scroll', scrollHandler, false );
window.addEventListener( 'resize', resizeHandler, false );
}
init();
})();
Can't something like this be working ?
$('.trigger').click(function(){
init();
})

JavaScript Preventing User Text Selection

Something in this Curtains.js plug-in is preventing user text selection on my page. When I comment it out, I'm able to select text, when I put it back in, I'm not. Can someone identify it and tell me how to fix it? I'm at my wit's end.
<script>
/*
* Curtain.js - Create an unique page transitioning system
* ---
* Version: 2
* Copyright 2011, Victor Coulon (http://victorcoulon.fr)
* Released under the MIT Licence
*/
(function ( $, window, document, undefined ) {
var pluginName = 'curtain',
defaults = {
scrollSpeed: 400,
bodyHeight: 0,
linksArray: [],
mobile: false,
scrollButtons: {},
controls: null,
curtainLinks: '.curtain-links',
enableKeys: true,
easing: 'swing',
disabled: false,
nextSlide: function() {},
prevSlide: function() {}
};
// The actual plugin constructor
function Plugin( element, options ) {
var self = this;
// Public attributes
this.element = element;
this.options = $.extend( {}, defaults, options) ;
this._defaults = defaults;
this._name = pluginName;
this._ignoreHashChange = false;
this.init();
}
Plugin.prototype = {
init: function () {
var self = this;
// Cache element
this.$element = $(this.element);
this.$li = $(this.element).find('>li');
this.$liLength = this.$li.length;
self.$windowHeight = $(window).height();
self.$elDatas = {};
self.$document = $(document);
self.$window = $(window);
self.webkit = (navigator.userAgent.indexOf('Chrome') > -1 || navigator.userAgent.indexOf("Safari") > -1);
$.Android = (navigator.userAgent.match(/Android/i));
$.iPhone = ((navigator.userAgent.match(/iPhone/i)) || (navigator.userAgent.match(/iPod/i)));
$.iPad = ((navigator.userAgent.match(/iPad/i)));
$.iOs4 = (/OS [1-4]_[0-9_]+ like Mac OS X/i.test(navigator.userAgent));
if($.iPhone || $.iPad || $.Android || self.options.disabled){
this.options.mobile = true;
this.$li.css({position:'relative'});
this.$element.find('.fixed').css({position:'absolute'});
}
if(this.options.mobile){
this.scrollEl = this.$element;
} else if($.browser.mozilla || $.browser.msie) {
this.scrollEl = $('html');
} else {
this.scrollEl = $('body');
}
if(self.options.controls){
self.options.scrollButtons['up'] = $(self.options.controls).find('[href="#up"]');
self.options.scrollButtons['down'] = $(self.options.controls).find('[href="#down"]');
if(!$.iOs4 && ($.iPhone || $.iPad)){
self.$element.css({
position:'fixed',
top:0,
left:0,
right:0,
bottom:0,
'-webkit-overflow-scrolling':'touch',
overflow:'auto'
});
$(self.options.controls).css({position:'absolute'});
}
}
// When all image is loaded
var callbackImageLoaded = function(){
self.setDimensions();
self.$li.eq(0).addClass('current');
self.setCache();
if(!self.options.mobile){
if(self.$li.eq(1).length)
self.$li.eq(1).nextAll().addClass('hidden');
}
self.setEvents();
self.setLinks();
self.isHashIsOnList(location.hash.substring(1));
};
if(self.$element.find('img').length)
self.imageLoaded(callbackImageLoaded);
else
callbackImageLoaded();
},
// Events
scrollToPosition: function (direction){
var position = null,
self = this;
if(self.scrollEl.is(':animated')){
return false;
}
if(direction === 'up' || direction == 'down'){
// Keyboard event
var $next = (direction === 'up') ? self.$current.prev() : self.$current.next();
// Step in the current panel ?
if(self.$step){
if(!self.$current.find('.current-step').length){
self.$step.eq(0).addClass('current-step');
}
var $nextStep = (direction === 'up') ? self.$current.find('.current-step').prev('.step') : self.$current.find('.current-step').next('.step');
if($nextStep.length) {
position = (self.options.mobile) ? $nextStep.position().top + self.$elDatas[self.$current.index()]['data-position'] : $nextStep.position().top + self.$elDatas[self.$current.index()]['data-position'];
}
}
position = position || ((self.$elDatas[$next.index()] === undefined) ? null : self.$elDatas[$next.index()]['data-position']);
if(position !== null){
self.scrollEl.animate({
scrollTop: position
}, self.options.scrollSpeed, self.options.easing);
}
} else if(direction === 'top'){
self.scrollEl.animate({
scrollTop:0
}, self.options.scrollSpeed, self.options.easing);
} else if(direction === 'bottom'){
self.scrollEl.animate({
scrollTop:self.options.bodyHeight
}, self.options.scrollSpeed, self.options.easing);
} else {
var index = $("#"+direction).index(),
speed = Math.abs(self.currentIndex-index) * (this.options.scrollSpeed*4) / self.$liLength;
self.scrollEl.animate({
scrollTop:self.$elDatas[index]['data-position'] || null
}, (speed <= self.options.scrollSpeed) ? self.options.scrollSpeed : speed, this.options.easing);
}
},
scrollEvent: function() {
var self = this,
docTop = self.$document.scrollTop();
if(docTop < self.currentP && self.currentIndex > 0){
// Scroll to top
self._ignoreHashChange = true;
if(self.$current.prev().attr('id'))
self.setHash(self.$current.prev().attr('id'));
self.$current
.removeClass('current')
.css( (self.webkit) ? {'-webkit-transform': 'translateY(0px) translateZ(0)'} : {marginTop: 0} )
.nextAll().addClass('hidden').end()
.prev().addClass('current').removeClass('hidden');
self.setCache();
self.options.prevSlide();
} else if(docTop < (self.currentP + self.currentHeight)){
// Animate the current pannel during the scroll
if(self.webkit)
self.$current.css({'-webkit-transform': 'translateY('+(-(docTop-self.currentP))+'px) translateZ(0)' });
else
self.$current.css({marginTop: -(docTop-self.currentP) });
// If there is a fixed element in the current panel
if(self.$fixedLength){
var dataTop = parseInt(self.$fixed.attr('data-top'), 10);
if(docTop + self.$windowHeight >= self.currentP + self.currentHeight){
self.$fixed.css({
position: 'fixed'
});
} else {
self.$fixed.css({
position: 'absolute',
marginTop: Math.abs(docTop-self.currentP)
});
}
}
// If there is a step element in the current panel
if(self.$stepLength){
$.each(self.$step, function(i,el){
if(($(el).position().top+self.currentP) <= docTop+5 && $(el).position().top + self.currentP + $(el).height() >= docTop+5){
if(!$(el).hasClass('current-step')){
self.$step.removeClass('current-step');
$(el).addClass('current-step');
return false;
}
}
});
}
if(self.parallaxBg){
self.$current.css({
'background-position-y': docTop * self.parallaxBg
});
}
if(self.$fade.length){
self.$fade.css({
'opacity': 1-(docTop/ self.$fade.attr('data-fade'))
});
}
if(self.$slowScroll.length){
self.$slowScroll.css({
'margin-top' : (docTop / self.$slowScroll.attr('data-slow-scroll'))
});
}
} else {
// Scroll bottom
self._ignoreHashChange = true;
if(self.$current.next().attr('id'))
self.setHash(self.$current.next().attr('id'));
self.$current.removeClass('current')
.addClass('hidden')
.next('li').addClass('current').next('li').removeClass('hidden');
self.setCache();
self.options.nextSlide();
}
},
scrollMobileEvent: function() {
var self = this,
docTop = self.$element.scrollTop();
if(docTop+10 < self.currentP && self.currentIndex > 0){
// Scroll to top
self._ignoreHashChange = true;
if(self.$current.prev().attr('id'))
self.setHash(self.$current.prev().attr('id'));
self.$current.removeClass('current').prev().addClass('current');
self.setCache();
self.options.prevSlide();
} else if(docTop+10 < (self.currentP + self.currentHeight)){
// If there is a step element in the current panel
if(self.$stepLength){
$.each(self.$step, function(i,el){
if(($(el).position().top+self.currentP) <= docTop && (($(el).position().top+self.currentP) + $(el).outerHeight()) >= docTop){
if(!$(el).hasClass('current-step')){
self.$step.removeClass('current-step');
$(el).addClass('current-step');
}
}
});
}
} else {
// Scroll bottom
self._ignoreHashChange = true;
if(self.$current.next().attr('id'))
self.setHash(self.$current.next().attr('id'));
self.$current.removeClass('current').next().addClass('current');
self.setCache();
self.options.nextSlide();
}
},
// Setters
setDimensions: function(){
var self = this,
levelHeight = 0,
cover = false,
height = null;
self.$windowHeight = self.$window.height();
this.$li.each(function(index) {
var $self = $(this);
cover = $self.hasClass('cover');
if(cover){
$self.css({height: self.$windowHeight, zIndex: 999-index})
.attr('data-height',self.$windowHeight)
.attr('data-position',levelHeight);
self.$elDatas[$self.index()] = {
'data-height': parseInt(self.$windowHeight,10),
'data-position': parseInt(levelHeight, 10)
};
levelHeight += self.$windowHeight;
} else{
height = ($self.outerHeight() <= self.$windowHeight) ? self.$windowHeight : $self.outerHeight();
$self.css({minHeight: height, zIndex: 999-index})
.attr('data-height',height)
.attr('data-position',levelHeight);
self.$elDatas[$self.index()] = {
'data-height': parseInt(height, 10),
'data-position': parseInt(levelHeight, 10)
};
levelHeight += height;
}
if($self.find('.fixed').length){
var top = $self.find('.fixed').css('top');
$self.find('.fixed').attr('data-top', top);
}
});
if(!this.options.mobile)
this.setBodyHeight();
},
setEvents: function() {
var self = this;
$(window).on('resize', function(){
self.setDimensions();
});
if(self.options.mobile) {
self.$element.on('scroll', function(){
self.scrollMobileEvent();
});
} else {
self.$window.on('scroll', function(){
self.scrollEvent();
});
}
if(self.options.enableKeys) {
self.$document.on('keydown', function(e){
if(e.keyCode === 38 || e.keyCode === 37) {
self.scrollToPosition('up');
e.preventDefault();
return false;
}
if(e.keyCode === 40 || e.keyCode === 39){
self.scrollToPosition('down');
e.preventDefault();
return false;
}
// Home button
if(e.keyCode === 36){
self.scrollToPosition('top');
e.preventDefault();
return false;
}
// End button
if(e.keyCode === 35){
self.scrollToPosition('bottom');
e.preventDefault();
return false;
}
});
}
if(self.options.scrollButtons){
if(self.options.scrollButtons.up){
self.options.scrollButtons.up.on('click', function(e){
e.preventDefault();
self.scrollToPosition('up');
});
}
if(self.options.scrollButtons.down){
self.options.scrollButtons.down.on('click', function(e){
e.preventDefault();
self.scrollToPosition('down');
});
}
}
if(self.options.curtainLinks){
$(self.options.curtainLinks).on('click', function(e){
e.preventDefault();
var href = $(this).attr('href');
if(!self.isHashIsOnList(href.substring(1)) && position)
return false;
var position = self.$elDatas[$(href).index()]['data-position'] || null;
if(position){
self.scrollEl.animate({
scrollTop:position
}, self.options.scrollSpeed, self.options.easing);
}
return false;
});
}
self.$window.on("hashchange", function(event){
if(self._ignoreHashChange === false){
self.isHashIsOnList(location.hash.substring(1));
}
self._ignoreHashChange = false;
});
},
setBodyHeight: function(){
var h = 0;
for (var key in this.$elDatas) {
var obj = this.$elDatas[key];
h += obj['data-height'];
}
this.options.bodyHeight = h;
$('body').height(h);
},
setLinks: function(){
var self = this;
this.$li.each(function() {
var id = $(this).attr('id') || 0;
self.options.linksArray.push(id);
});
},
setHash: function(hash){
// "HARD FIX"
el = $('[href=#'+hash+']');
el.parent().siblings('li').removeClass('active');
el.parent().addClass('active');
if(history.pushState) {
history.pushState(null, null, '#'+hash);
}
else {
location.hash = hash;
}
},
setCache: function(){
var self = this;
self.$current = self.$element.find('.current');
self.$fixed = self.$current.find('.fixed');
self.$fixedLength = self.$fixed.length;
self.$step = self.$current.find('.step');
self.$stepLength = self.$step.length;
self.currentIndex = self.$current.index();
self.currentP = self.$elDatas[self.currentIndex]['data-position'];
self.currentHeight = self.$elDatas[self.currentIndex]['data-height'];
self.parallaxBg = self.$current.attr('data-parallax-background');
self.$fade = self.$current.find('[data-fade]');
self.$slowScroll = self.$current.find('[data-slow-scroll]');
},
// Utils
isHashIsOnList: function(hash){
var self = this;
$.each(self.options.linksArray, function(i,val){
if(val === hash){
self.scrollToPosition(hash);
return false;
}
});
},
readyElement: function(el,callback){
var interval = setInterval(function(){
if(el.length){
callback(el.length);
clearInterval(interval);
}
},60);
},
imageLoaded: function(callback){
var self = this,
elems = self.$element.find('img'),
len = elems.length,
blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
elems.bind('load.imgloaded',function(){
if (--len <= 0 && this.src !== blank || $(this).not(':visible')){
elems.unbind('load.imgloaded');
callback.call(elems,this);
}
}).each(function(){
if (this.complete || this.complete === undefined){
var src = this.src;
this.src = blank;
this.src = src;
}
});
}
};
$.fn[pluginName] = function ( options ) {
return this.each(function () {
if (!$.data(this, 'plugin_' + pluginName)) {
$.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
}
});
};
})( jQuery, window, document );
</script>
First you would have to tell us how you are trying to select text (mouse, keyboard, touchscreen, etc.)
I bet my bitcoins on keyboard (but I don't have any).
Must be one of those
self.$document.on('keydown', function(e){
...
e.preventDefault();
which don't even document which keys these numbers stand for.
It's e.preventDefault() which prevents the default browser action from being performed.
If you're in Chrome devtools, you can use
monitorEvents(window, 'key')
to make sense of these.
Of course this bit may help a bit:
keyCode: 38
keyIdentifier: "Up"
So the code could be written readably by use of keyIdentifier instead of keyCode.
I don't know how compatible that would be across browsers.
Be warned that keydown keyCode values are different from keypress values (which actually insert real characters). keydown key codes will vary between keyboard layouts and locales.
See http://unixpapa.com/js/key.html for disgust and enlightenment, but mostly disgust.

cancelAnimationFrame or clearTimeout

It seems that I lost myself between cancelAnimationFrame and clearTimeout. Don't know how to stop Snake game. Here is snippet of my code that start animation:
window.addEventListener( "load", function gameLoop() {
globalTimer = setTimeout( function() {
snakeObj.update();
requestAnimationFrame( gameLoop );
}, 100 );
});
in this way I tried to stop my game:
if ( parseInt(this.snakeHead.style.top) == fieldObj.h ||
parseInt(this.snakeHead.style.left) == fieldObj.w ) {
gameObj.stopGame();// stop game message
clearTimeout(globalTimer);
}
but it doesn't work - stop game message appears, but animation continuing.
here all my code:
// field object
var globalTimer;
var fieldObj = {
field: document.getElementById( "field" ),
w: 480,
h: 580
},
gameObj = {
pastCoord: [],
getRandomCoord: function( num ) { // 20 is width of one body segment
return Math.round( Math.floor(( Math.random() * num)) / 20 ) * 20;
},
createSnakeTarget: function() {
var t = document.createElement( "div" );
t.id = "snake-target";
t.style.top = this.getRandomCoord( fieldObj.h ) + "px";
t.style.left = this.getRandomCoord( fieldObj.w ) + "px";
fieldObj.field.appendChild( t );
},
stopGame: function() {
var stopMessage = document.createElement("div");
stopMessage.className = "stop-message";
stopMessage.style.background = "white";
fieldObj.field.appendChild( stopMessage );
//TODO: write message to stopGame
}
};
gameObj.createSnakeTarget();
// snake object
snakeObj = {
snakeHead: document.getElementById("head"),
snakeBody: document.getElementsByClassName( "snake-body" ),// there must be one element
p: {
x: 0, // position x
y: 0 // position y
},
v: {
x: 20, // velocity ( one loop move one unit of snake body)
y: 20
},
keys: {
up: null,
l: null,
r: null,
down: null
},
stepInSnakeBody: 0,// go through snakeBody
stepInPastCoord: 0,// go through pastCoord
addBodySegment: function() {
var seg = document.createElement( "div" );
seg.className = "snake-body";
fieldObj.field.appendChild( seg );
seg.style.top = this.p.x + "px";// receive current position
seg.style.left = this.p.y + "px";
},
update: function() {
var snakeTarget = document.getElementById("snake-target");
if ( this.keys.down ) {
this.p.x += this.v.x;
} else if ( this.keys.up ) {
this.p.x -= this.v.x;
} else if ( this.keys.r ) {
this.p.y += this.v.y;
}else if ( this.keys.l ) {
this.p.y -= this.v.y;
}
this.snakeHead.style.top = this.p.x + "px";
this.snakeHead.style.left = this.p.y + "px";
gameObj.pastCoord.push([this.p.x, this.p.y]);// create and push coord of snake head
//every step index in snakeBody receive coords from pastCoord
this.snakeBody[this.stepInSnakeBody].style.top = gameObj.pastCoord[this.stepInPastCoord][0] + "px";
this.snakeBody[this.stepInSnakeBody].style.left = gameObj.pastCoord[this.stepInPastCoord][1] + "px";
this.stepInSnakeBody++; // increment index every step
this.stepInPastCoord++;
if ( this.stepInSnakeBody === this.snakeBody.length ) {
this.stepInSnakeBody = 0; // when stepInSnakeBody equal length of snake go to zero
//and apply coords
}
// detect collision with target
if ( this.snakeHead.style.top === snakeTarget.style.top &&
this.snakeHead.style.left === snakeTarget.style.left ) {
fieldObj.field.removeChild( snakeTarget );
gameObj.createSnakeTarget();
snakeObj.addBodySegment();
}
if ( parseInt(this.snakeHead.style.top) == fieldObj.h ||
parseInt(this.snakeHead.style.left) == fieldObj.w ) {
gameObj.stopGame();
clearTimeout(globalTimer);
}
}
};
// Crome works only with keydown and keyup
window.addEventListener('keydown', function() {
// before changing direction you have to put previous direction to false
if ( event.keyCode == 38 ) {
snakeObj.keys.up = true;
snakeObj.keys.down = false;
} else if ( event.keyCode == 40 ) {
snakeObj.keys.down = true;
snakeObj.keys.up = false;
} else if ( event.keyCode == 39 ) {
snakeObj.keys.r = true;
snakeObj.keys.up = false;
snakeObj.keys.down = false;
} else if ( event.keyCode == 37 ) {
snakeObj.keys.l = true;
snakeObj.keys.r = false;
snakeObj.keys.up = false;
snakeObj.keys.down = false;
}
}, false);
//TODO: add event hendler to click to some button
window.addEventListener( "load", function gameLoop() {
globalTimer = setTimeout( function() {
snakeObj.update();
requestAnimationFrame( gameLoop );
}, 100 );
});
here is codepen (works in CHROME only ) http://codepen.io/Kuzyo/pen/pamzC
Thanks for the help.
You need to call cancelAnimationFrame.
However you need to make sure you call requestAnimationFrame before update. Currently you're calling stopGame, but then after stopGame/update finishes, requestAnimationFrame schedules another loop so even if your stopGame calls cancelAnimationFrame it will not stop the animation.
An alternative is to use a boolean flag that you check in your gameLoop function.

Categories

Resources