Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
0960664f
Commit
0960664f
authored
Feb 05, 2014
by
Antonio Ramirez
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update typeahead.js to latest version
parent
0cde160c
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1198 additions
and
697 deletions
+1198
-697
typeahead.js
extensions/gii/assets/typeahead.js
+1198
-697
No files found.
extensions/gii/assets/typeahead.js
View file @
0960664f
/*!
* typeahead.js 0.
9.3
* https://github.com/twitter/typeahead
* typeahead.js 0.
10.0
* https://github.com/twitter/typeahead
.js
* Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
*/
(
function
(
$
)
{
var
VERSION
=
"0.9.3"
;
var
utils
=
{
var
_
=
{
isMsie
:
function
()
{
var
match
=
/
(
msie
)
([\w
.
]
+
)
/i
.
exec
(
navigator
.
userAgent
);
return
match
?
parseInt
(
match
[
2
],
10
)
:
false
;
return
/
(
msie|trident
)
/i
.
test
(
navigator
.
userAgent
)
?
navigator
.
userAgent
.
match
(
/
(
msie |rv:
)(\d
+
(
.
\d
+
)?)
/i
)[
2
]
:
false
;
},
isBlankString
:
function
(
str
)
{
return
!
str
||
/^
\s
*$/
.
test
(
str
);
...
...
@@ -30,21 +28,12 @@
return
typeof
obj
===
"undefined"
;
},
bind
:
$
.
proxy
,
bindAll
:
function
(
obj
)
{
var
val
;
f
or
(
var
key
in
obj
)
{
$
.
isFunction
(
val
=
obj
[
key
])
&&
(
obj
[
key
]
=
$
.
proxy
(
val
,
obj
)
);
each
:
function
(
collection
,
cb
)
{
$
.
each
(
collection
,
reverseArgs
)
;
f
unction
reverseArgs
(
index
,
value
)
{
return
cb
(
value
,
index
);
}
},
indexOf
:
function
(
haystack
,
needle
)
{
for
(
var
i
=
0
;
i
<
haystack
.
length
;
i
++
)
{
if
(
haystack
[
i
]
===
needle
)
{
return
i
;
}
}
return
-
1
;
},
each
:
$
.
each
,
map
:
$
.
map
,
filter
:
$
.
grep
,
every
:
function
(
obj
,
test
)
{
...
...
@@ -78,6 +67,12 @@
return
counter
++
;
};
}(),
templatify
:
function
templatify
(
obj
)
{
return
$
.
isFunction
(
obj
)
?
obj
:
template
;
function
template
()
{
return
String
(
obj
);
}
},
defer
:
function
(
fn
)
{
setTimeout
(
fn
,
0
);
},
...
...
@@ -123,69 +118,69 @@
return
result
;
};
},
tokenizeQuery
:
function
(
str
)
{
return
$
.
trim
(
str
).
toLowerCase
().
split
(
/
[\s]
+/
);
},
tokenizeText
:
function
(
str
)
{
return
$
.
trim
(
str
).
toLowerCase
().
split
(
/
[\s\-
_
]
+/
);
},
getProtocol
:
function
()
{
return
location
.
protocol
;
},
noop
:
function
()
{}
};
var
EventTarget
=
function
()
{
var
eventSplitter
=
/
\s
+/
;
return
{
on
:
function
(
events
,
callback
)
{
var
event
;
if
(
!
callback
)
{
return
this
;
}
this
.
_callbacks
=
this
.
_callbacks
||
{};
events
=
events
.
split
(
eventSplitter
);
while
(
event
=
events
.
shift
())
{
this
.
_callbacks
[
event
]
=
this
.
_callbacks
[
event
]
||
[];
this
.
_callbacks
[
event
].
push
(
callback
);
var
VERSION
=
"0.10.0"
;
var
LruCache
=
function
(
root
,
undefined
)
{
function
LruCache
(
maxSize
)
{
this
.
maxSize
=
maxSize
||
100
;
this
.
size
=
0
;
this
.
hash
=
{};
this
.
list
=
new
List
();
}
_
.
mixin
(
LruCache
.
prototype
,
{
set
:
function
set
(
key
,
val
)
{
var
tailItem
=
this
.
list
.
tail
,
node
;
if
(
this
.
size
>=
this
.
maxSize
)
{
this
.
list
.
remove
(
tailItem
);
delete
this
.
hash
[
tailItem
.
key
];
}
if
(
node
=
this
.
hash
[
key
])
{
node
.
val
=
val
;
this
.
list
.
moveToFront
(
node
);
}
else
{
node
=
new
Node
(
key
,
val
);
this
.
list
.
add
(
node
);
this
.
hash
[
key
]
=
node
;
this
.
size
++
;
}
return
this
;
},
trigger
:
function
(
events
,
data
)
{
var
event
,
callbacks
;
if
(
!
this
.
_callbacks
)
{
return
this
;
}
events
=
events
.
split
(
eventSplitter
);
while
(
event
=
events
.
shift
())
{
if
(
callbacks
=
this
.
_callbacks
[
event
])
{
for
(
var
i
=
0
;
i
<
callbacks
.
length
;
i
+=
1
)
{
callbacks
[
i
].
call
(
this
,
{
type
:
event
,
data
:
data
});
get
:
function
get
(
key
)
{
var
node
=
this
.
hash
[
key
];
if
(
node
)
{
this
.
list
.
moveToFront
(
node
);
return
node
.
val
;
}
}
});
function
List
()
{
this
.
head
=
this
.
tail
=
null
;
}
return
this
;
}
};
}();
var
EventBus
=
function
()
{
var
namespace
=
"typeahead:"
;
function
EventBus
(
o
)
{
if
(
!
o
||
!
o
.
el
)
{
$
.
error
(
"EventBus initialized without el"
);
}
this
.
$el
=
$
(
o
.
el
);
_
.
mixin
(
List
.
prototype
,
{
add
:
function
add
(
node
)
{
if
(
this
.
head
)
{
node
.
next
=
this
.
head
;
this
.
head
.
prev
=
node
;
}
utils
.
mixin
(
EventBus
.
prototype
,
{
trigger
:
function
(
type
)
{
var
args
=
[].
slice
.
call
(
arguments
,
1
);
this
.
$el
.
trigger
(
namespace
+
type
,
args
);
this
.
head
=
node
;
this
.
tail
=
this
.
tail
||
node
;
},
remove
:
function
remove
(
node
)
{
node
.
prev
?
node
.
prev
.
next
=
node
.
next
:
this
.
head
=
node
.
next
;
node
.
next
?
node
.
next
.
prev
=
node
.
prev
:
this
.
tail
=
node
.
prev
;
},
moveToFront
:
function
(
node
)
{
this
.
remove
(
node
);
this
.
add
(
node
);
}
});
return
EventBus
;
}();
function
Node
(
key
,
val
)
{
this
.
key
=
key
;
this
.
val
=
val
;
this
.
prev
=
this
.
next
=
null
;
}
return
LruCache
;
}(
this
);
var
PersistentStorage
=
function
()
{
var
ls
,
methods
;
try
{
...
...
@@ -215,7 +210,7 @@
return
decode
(
ls
.
getItem
(
this
.
_prefix
(
key
)));
},
set
:
function
(
key
,
val
,
ttl
)
{
if
(
utils
.
isNumber
(
ttl
))
{
if
(
_
.
isNumber
(
ttl
))
{
ls
.
setItem
(
this
.
_ttlKey
(
key
),
encode
(
now
()
+
ttl
));
}
else
{
ls
.
removeItem
(
this
.
_ttlKey
(
key
));
...
...
@@ -241,330 +236,630 @@
},
isExpired
:
function
(
key
)
{
var
ttl
=
decode
(
ls
.
getItem
(
this
.
_ttlKey
(
key
)));
return
utils
.
isNumber
(
ttl
)
&&
now
()
>
ttl
?
true
:
false
;
return
_
.
isNumber
(
ttl
)
&&
now
()
>
ttl
?
true
:
false
;
}
};
}
else
{
methods
=
{
get
:
utils
.
noop
,
set
:
utils
.
noop
,
remove
:
utils
.
noop
,
clear
:
utils
.
noop
,
isExpired
:
utils
.
noop
get
:
_
.
noop
,
set
:
_
.
noop
,
remove
:
_
.
noop
,
clear
:
_
.
noop
,
isExpired
:
_
.
noop
};
}
utils
.
mixin
(
PersistentStorage
.
prototype
,
methods
);
_
.
mixin
(
PersistentStorage
.
prototype
,
methods
);
return
PersistentStorage
;
function
now
()
{
return
new
Date
().
getTime
();
}
function
encode
(
val
)
{
return
JSON
.
stringify
(
utils
.
isUndefined
(
val
)
?
null
:
val
);
return
JSON
.
stringify
(
_
.
isUndefined
(
val
)
?
null
:
val
);
}
function
decode
(
val
)
{
return
JSON
.
parse
(
val
);
}
}();
var
RequestCache
=
function
()
{
function
RequestCache
(
o
)
{
utils
.
bindAll
(
this
);
o
=
o
||
{};
this
.
sizeLimit
=
o
.
sizeLimit
||
10
;
this
.
cache
=
{};
this
.
cachedKeysByAge
=
[];
}
utils
.
mixin
(
RequestCache
.
prototype
,
{
get
:
function
(
url
)
{
return
this
.
cache
[
url
];
},
set
:
function
(
url
,
resp
)
{
var
requestToEvict
;
if
(
this
.
cachedKeysByAge
.
length
===
this
.
sizeLimit
)
{
requestToEvict
=
this
.
cachedKeysByAge
.
shift
();
delete
this
.
cache
[
requestToEvict
];
}
this
.
cache
[
url
]
=
resp
;
this
.
cachedKeysByAge
.
push
(
url
);
}
});
return
RequestCache
;
}();
var
Transport
=
function
()
{
var
pendingRequestsCount
=
0
,
pendingRequests
=
{},
maxPendingRequests
,
requestCache
;
var
pendingRequestsCount
=
0
,
pendingRequests
=
{},
maxPendingRequests
=
6
,
requestCache
=
new
LruCache
(
10
)
;
function
Transport
(
o
)
{
utils
.
bindAll
(
this
);
o
=
utils
.
isString
(
o
)
?
{
url
:
o
}
:
o
;
requestCache
=
requestCache
||
new
RequestCache
();
maxPendingRequests
=
utils
.
isNumber
(
o
.
maxParallelRequests
)
?
o
.
maxParallelRequests
:
maxPendingRequests
||
6
;
this
.
url
=
o
.
url
;
this
.
wildcard
=
o
.
wildcard
||
"%QUERY"
;
this
.
filter
=
o
.
filter
;
this
.
replace
=
o
.
replace
;
this
.
ajaxSettings
=
{
type
:
"get"
,
cache
:
o
.
cache
,
timeout
:
o
.
timeout
,
dataType
:
o
.
dataType
||
"json"
,
beforeSend
:
o
.
beforeSend
};
this
.
_get
=
(
/^throttle$/i
.
test
(
o
.
rateLimitFn
)
?
utils
.
throttle
:
utils
.
debounce
)(
this
.
_get
,
o
.
rateLimitWait
||
300
);
o
=
o
||
{};
this
.
_send
=
o
.
send
?
callbackToDeferred
(
o
.
send
)
:
$
.
ajax
;
this
.
_get
=
o
.
rateLimiter
?
o
.
rateLimiter
(
this
.
_get
)
:
this
.
_get
;
}
utils
.
mixin
(
Transport
.
prototype
,
{
_get
:
function
(
url
,
cb
)
{
var
that
=
this
;
if
(
belowPendingRequestsThreshold
())
{
this
.
_sendRequest
(
url
).
done
(
done
);
Transport
.
setMaxPendingRequests
=
function
setMaxPendingRequests
(
num
)
{
maxPendingRequests
=
num
;
};
Transport
.
resetCache
=
function
clearCache
()
{
requestCache
=
new
LruCache
(
10
);
};
_
.
mixin
(
Transport
.
prototype
,
{
_get
:
function
(
url
,
o
,
cb
)
{
var
that
=
this
,
jqXhr
;
if
(
jqXhr
=
pendingRequests
[
url
])
{
jqXhr
.
done
(
done
);
}
else
if
(
pendingRequestsCount
<
maxPendingRequests
)
{
pendingRequestsCount
++
;
pendingRequests
[
url
]
=
this
.
_send
(
url
,
o
).
done
(
done
).
always
(
always
);
}
else
{
this
.
onDeckRequestArgs
=
[].
slice
.
call
(
arguments
,
0
);
}
function
done
(
resp
)
{
var
data
=
that
.
filter
?
that
.
filter
(
resp
)
:
resp
;
cb
&&
cb
(
data
);
cb
&&
cb
(
resp
);
requestCache
.
set
(
url
,
resp
);
}
},
_sendRequest
:
function
(
url
)
{
var
that
=
this
,
jqXhr
=
pendingRequests
[
url
];
if
(
!
jqXhr
)
{
incrementPendingRequests
();
jqXhr
=
pendingRequests
[
url
]
=
$
.
ajax
(
url
,
this
.
ajaxSettings
).
always
(
always
);
}
return
jqXhr
;
function
always
()
{
decrementPendingRequests
()
;
pendingRequests
[
url
]
=
null
;
pendingRequestsCount
--
;
delete
pendingRequests
[
url
]
;
if
(
that
.
onDeckRequestArgs
)
{
that
.
_get
.
apply
(
that
,
that
.
onDeckRequestArgs
);
that
.
onDeckRequestArgs
=
null
;
}
}
},
get
:
function
(
query
,
cb
)
{
var
that
=
this
,
encodedQuery
=
encodeURIComponent
(
query
||
""
),
url
,
resp
;
cb
=
cb
||
utils
.
noop
;
url
=
this
.
replace
?
this
.
replace
(
this
.
url
,
encodedQuery
)
:
this
.
url
.
replace
(
this
.
wildcard
,
encodedQuery
);
get
:
function
(
url
,
o
,
cb
)
{
var
that
=
this
,
resp
;
if
(
_
.
isFunction
(
o
))
{
cb
=
o
;
o
=
{};
}
if
(
resp
=
requestCache
.
get
(
url
))
{
utils
.
defer
(
function
()
{
cb
(
that
.
filter
?
that
.
filter
(
resp
)
:
resp
);
_
.
defer
(
function
()
{
cb
&&
cb
(
resp
);
});
}
else
{
this
.
_get
(
url
,
cb
);
this
.
_get
(
url
,
o
,
cb
);
}
return
!!
resp
;
}
});
return
Transport
;
function
incrementPendingRequests
()
{
pendingRequestsCount
++
;
function
callbackToDeferred
(
fn
)
{
return
function
customSendWrapper
(
url
,
o
)
{
var
deferred
=
$
.
Deferred
();
fn
(
url
,
o
,
onSuccess
,
onError
);
return
deferred
;
function
onSuccess
(
resp
)
{
_
.
defer
(
function
()
{
deferred
.
resolve
(
resp
);
});
}
function
decrementPendingRequests
()
{
pendingRequestsCount
--
;
function
onError
(
err
)
{
_
.
defer
(
function
()
{
deferred
.
reject
(
err
);
});
}
function
belowPendingRequestsThreshold
()
{
return
pendingRequestsCount
<
maxPendingRequests
;
};
}
}();
var
Dataset
=
function
()
{
var
keys
=
{
thumbprint
:
"thumbprint"
,
protocol
:
"protocol"
,
itemHash
:
"itemHash"
,
adjacencyList
:
"adjacencyList"
};
function
Dataset
(
o
)
{
utils
.
bindAll
(
this
);
if
(
utils
.
isString
(
o
.
template
)
&&
!
o
.
engine
)
{
$
.
error
(
"no template engine specified"
);
var
SearchIndex
=
function
()
{
function
SearchIndex
(
o
)
{
o
=
o
||
{};
if
(
!
o
.
datumTokenizer
||
!
o
.
queryTokenizer
)
{
$
.
error
(
"datumTokenizer and queryTokenizer are both required"
);
}
if
(
!
o
.
local
&&
!
o
.
prefetch
&&
!
o
.
remote
)
{
$
.
error
(
"one of local, prefetch, or remote is required"
);
this
.
datumTokenizer
=
o
.
datumTokenizer
;
this
.
queryTokenizer
=
o
.
queryTokenizer
;
this
.
datums
=
[];
this
.
trie
=
newNode
();
}
_
.
mixin
(
SearchIndex
.
prototype
,
{
bootstrap
:
function
bootstrap
(
o
)
{
this
.
datums
=
o
.
datums
;
this
.
trie
=
o
.
trie
;
},
add
:
function
(
data
)
{
var
that
=
this
;
data
=
_
.
isArray
(
data
)
?
data
:
[
data
];
_
.
each
(
data
,
function
(
datum
)
{
var
id
,
tokens
;
id
=
that
.
datums
.
push
(
datum
)
-
1
;
tokens
=
normalizeTokens
(
that
.
datumTokenizer
(
datum
));
_
.
each
(
tokens
,
function
(
token
)
{
var
node
,
chars
,
ch
,
ids
;
node
=
that
.
trie
;
chars
=
token
.
split
(
""
);
while
(
ch
=
chars
.
shift
())
{
node
=
node
.
children
[
ch
]
||
(
node
.
children
[
ch
]
=
newNode
());
node
.
ids
.
push
(
id
);
}
this
.
name
=
o
.
name
||
utils
.
getUniqueId
();
this
.
limit
=
o
.
limit
||
5
;
this
.
minLength
=
o
.
minLength
||
1
;
this
.
header
=
o
.
header
;
this
.
footer
=
o
.
footer
;
this
.
valueKey
=
o
.
valueKey
||
"value"
;
this
.
template
=
compileTemplate
(
o
.
template
,
o
.
engine
,
this
.
valueKey
);
this
.
local
=
o
.
local
;
this
.
prefetch
=
o
.
prefetch
;
this
.
remote
=
o
.
remote
;
this
.
itemHash
=
{};
this
.
adjacencyList
=
{};
this
.
storage
=
o
.
name
?
new
PersistentStorage
(
o
.
name
)
:
null
;
}
utils
.
mixin
(
Dataset
.
prototype
,
{
_processLocalData
:
function
(
data
)
{
this
.
_mergeProcessedData
(
this
.
_processData
(
data
));
},
_loadPrefetchData
:
function
(
o
)
{
var
that
=
this
,
thumbprint
=
VERSION
+
(
o
.
thumbprint
||
""
),
storedThumbprint
,
storedProtocol
,
storedItemHash
,
storedAdjacencyList
,
isExpired
,
deferred
;
if
(
this
.
storage
)
{
storedThumbprint
=
this
.
storage
.
get
(
keys
.
thumbprint
);
storedProtocol
=
this
.
storage
.
get
(
keys
.
protocol
);
storedItemHash
=
this
.
storage
.
get
(
keys
.
itemHash
);
storedAdjacencyList
=
this
.
storage
.
get
(
keys
.
adjacencyList
);
}
isExpired
=
storedThumbprint
!==
thumbprint
||
storedProtocol
!==
utils
.
getProtocol
();
o
=
utils
.
isString
(
o
)
?
{
url
:
o
}
:
o
;
o
.
ttl
=
utils
.
isNumber
(
o
.
ttl
)
?
o
.
ttl
:
24
*
60
*
60
*
1
e3
;
if
(
storedItemHash
&&
storedAdjacencyList
&&
!
isExpired
)
{
this
.
_mergeProcessedData
({
itemHash
:
storedItemHash
,
adjacencyList
:
storedAdjacencyList
});
deferred
=
$
.
Deferred
().
resolve
();
}
else
{
deferred
=
$
.
getJSON
(
o
.
url
).
done
(
processPrefetchData
);
});
},
get
:
function
get
(
query
)
{
var
that
=
this
,
tokens
,
matches
;
tokens
=
normalizeTokens
(
this
.
queryTokenizer
(
query
));
_
.
each
(
tokens
,
function
(
token
)
{
var
node
,
chars
,
ch
,
ids
;
if
(
matches
&&
matches
.
length
===
0
)
{
return
false
;
}
return
deferred
;
function
processPrefetchData
(
data
)
{
var
filteredData
=
o
.
filter
?
o
.
filter
(
data
)
:
data
,
processedData
=
that
.
_processData
(
filteredData
),
itemHash
=
processedData
.
itemHash
,
adjacencyList
=
processedData
.
adjacencyList
;
if
(
that
.
storage
)
{
that
.
storage
.
set
(
keys
.
itemHash
,
itemHash
,
o
.
ttl
);
that
.
storage
.
set
(
keys
.
adjacencyList
,
adjacencyList
,
o
.
ttl
);
that
.
storage
.
set
(
keys
.
thumbprint
,
thumbprint
,
o
.
ttl
);
that
.
storage
.
set
(
keys
.
protocol
,
utils
.
getProtocol
(),
o
.
ttl
);
node
=
that
.
trie
;
chars
=
token
.
split
(
""
);
while
(
node
&&
(
ch
=
chars
.
shift
()))
{
node
=
node
.
children
[
ch
];
}
that
.
_mergeProcessedData
(
processedData
);
if
(
node
&&
chars
.
length
===
0
)
{
ids
=
node
.
ids
.
slice
(
0
);
matches
=
matches
?
getIntersection
(
matches
,
ids
)
:
ids
;
}
else
{
matches
=
[];
return
false
;
}
});
return
matches
?
_
.
map
(
unique
(
matches
),
function
(
id
)
{
return
that
.
datums
[
id
];
})
:
[];
},
_transformDatum
:
function
(
datum
)
{
var
value
=
utils
.
isString
(
datum
)
?
datum
:
datum
[
this
.
valueKey
],
tokens
=
datum
.
tokens
||
utils
.
tokenizeText
(
value
),
item
=
{
value
:
value
,
t
okens
:
tokens
serialize
:
function
serialize
(
)
{
return
{
datums
:
this
.
datums
,
t
rie
:
this
.
trie
};
if
(
utils
.
isString
(
datum
))
{
item
.
datum
=
{};
item
.
datum
[
this
.
valueKey
]
=
datum
;
}
else
{
item
.
datum
=
datum
;
}
item
.
tokens
=
utils
.
filter
(
item
.
tokens
,
function
(
token
)
{
return
!
utils
.
isBlankString
(
token
);
});
item
.
tokens
=
utils
.
map
(
item
.
tokens
,
function
(
token
)
{
return
token
.
toLowerCase
();
});
return
item
;
},
_processData
:
function
(
data
)
{
var
that
=
this
,
itemHash
=
{},
adjacencyList
=
{};
utils
.
each
(
data
,
function
(
i
,
datum
)
{
var
item
=
that
.
_transformDatum
(
datum
),
id
=
utils
.
getUniqueId
(
item
.
value
);
itemHash
[
id
]
=
item
;
utils
.
each
(
item
.
tokens
,
function
(
i
,
token
)
{
var
character
=
token
.
charAt
(
0
),
adjacency
=
adjacencyList
[
character
]
||
(
adjacencyList
[
character
]
=
[
id
]);
!~
utils
.
indexOf
(
adjacency
,
id
)
&&
adjacency
.
push
(
id
);
return
SearchIndex
;
function
normalizeTokens
(
tokens
)
{
tokens
=
_
.
filter
(
tokens
,
function
(
token
)
{
return
!!
token
;
});
tokens
=
_
.
map
(
tokens
,
function
(
token
)
{
return
token
.
toLowerCase
();
});
return
tokens
;
}
function
newNode
()
{
return
{
itemHash
:
itemHash
,
adjacencyList
:
adjacencyList
ids
:
[]
,
children
:
{}
};
},
_mergeProcessedData
:
function
(
processedData
)
{
var
that
=
this
;
utils
.
mixin
(
this
.
itemHash
,
processedData
.
itemHash
);
utils
.
each
(
processedData
.
adjacencyList
,
function
(
character
,
adjacency
)
{
var
masterAdjacency
=
that
.
adjacencyList
[
character
];
that
.
adjacencyList
[
character
]
=
masterAdjacency
?
masterAdjacency
.
concat
(
adjacency
)
:
adjacency
;
}
function
unique
(
array
)
{
var
seen
=
{},
uniques
=
[];
for
(
var
i
=
0
;
i
<
array
.
length
;
i
++
)
{
if
(
!
seen
[
array
[
i
]])
{
seen
[
array
[
i
]]
=
true
;
uniques
.
push
(
array
[
i
]);
}
}
return
uniques
;
}
function
getIntersection
(
arrayA
,
arrayB
)
{
var
ai
=
0
,
bi
=
0
,
intersection
=
[];
arrayA
=
arrayA
.
sort
(
compare
);
arrayB
=
arrayB
.
sort
(
compare
);
while
(
ai
<
arrayA
.
length
&&
bi
<
arrayB
.
length
)
{
if
(
arrayA
[
ai
]
<
arrayB
[
bi
])
{
ai
++
;
}
else
if
(
arrayA
[
ai
]
>
arrayB
[
bi
])
{
bi
++
;
}
else
{
intersection
.
push
(
arrayA
[
ai
]);
ai
++
;
bi
++
;
}
}
return
intersection
;
function
compare
(
a
,
b
)
{
return
a
-
b
;
}
}
}();
var
oParser
=
function
()
{
return
{
local
:
getLocal
,
prefetch
:
getPrefetch
,
remote
:
getRemote
};
function
getLocal
(
o
)
{
return
o
.
local
||
null
;
}
function
getPrefetch
(
o
)
{
var
prefetch
,
defaults
;
defaults
=
{
url
:
null
,
thumbprint
:
""
,
ttl
:
24
*
60
*
60
*
1
e3
,
filter
:
null
,
ajax
:
{}
};
if
(
prefetch
=
o
.
prefetch
||
null
)
{
prefetch
=
_
.
isString
(
prefetch
)
?
{
url
:
prefetch
}
:
prefetch
;
prefetch
=
_
.
mixin
(
defaults
,
prefetch
);
prefetch
.
thumbprint
=
VERSION
+
prefetch
.
thumbprint
;
prefetch
.
ajax
.
method
=
prefetch
.
ajax
.
method
||
"get"
;
prefetch
.
ajax
.
dataType
=
prefetch
.
ajax
.
dataType
||
"json"
;
!
prefetch
.
url
&&
$
.
error
(
"prefetch requires url to be set"
);
}
return
prefetch
;
}
function
getRemote
(
o
)
{
var
remote
,
defaults
;
defaults
=
{
url
:
null
,
wildcard
:
"%QUERY"
,
replace
:
null
,
rateLimitBy
:
"debounce"
,
rateLimitWait
:
300
,
send
:
null
,
filter
:
null
,
ajax
:
{}
};
if
(
remote
=
o
.
remote
||
null
)
{
remote
=
_
.
isString
(
remote
)
?
{
url
:
remote
}
:
remote
;
remote
=
_
.
mixin
(
defaults
,
remote
);
remote
.
rateLimiter
=
/^throttle$/i
.
test
(
remote
.
rateLimitBy
)
?
byThrottle
(
remote
.
rateLimitWait
)
:
byDebounce
(
remote
.
rateLimitWait
);
remote
.
ajax
.
method
=
remote
.
ajax
.
method
||
"get"
;
remote
.
ajax
.
dataType
=
remote
.
ajax
.
dataType
||
"json"
;
delete
remote
.
rateLimitBy
;
delete
remote
.
rateLimitWait
;
!
remote
.
url
&&
$
.
error
(
"remote requires url to be set"
);
}
return
remote
;
function
byDebounce
(
wait
)
{
return
function
(
fn
)
{
return
_
.
debounce
(
fn
,
wait
);
};
}
function
byThrottle
(
wait
)
{
return
function
(
fn
)
{
return
_
.
throttle
(
fn
,
wait
);
};
}
}
}();
var
Bloodhound
=
window
.
Bloodhound
=
function
()
{
var
keys
;
keys
=
{
data
:
"data"
,
protocol
:
"protocol"
,
thumbprint
:
"thumbprint"
};
function
Bloodhound
(
o
)
{
if
(
!
o
||
!
o
.
local
&&
!
o
.
prefetch
&&
!
o
.
remote
)
{
$
.
error
(
"one of local, prefetch, or remote is required"
);
}
this
.
limit
=
o
.
limit
||
5
;
this
.
sorter
=
o
.
sorter
||
noSort
;
this
.
dupDetector
=
o
.
dupDetector
||
ignoreDuplicates
;
this
.
local
=
oParser
.
local
(
o
);
this
.
prefetch
=
oParser
.
prefetch
(
o
);
this
.
remote
=
oParser
.
remote
(
o
);
this
.
cacheKey
=
this
.
prefetch
?
this
.
prefetch
.
cacheKey
||
this
.
prefetch
.
url
:
null
;
this
.
index
=
new
SearchIndex
({
datumTokenizer
:
o
.
datumTokenizer
,
queryTokenizer
:
o
.
queryTokenizer
});
this
.
storage
=
this
.
cacheKey
?
new
PersistentStorage
(
this
.
cacheKey
)
:
null
;
}
Bloodhound
.
tokenizers
=
{
whitespace
:
function
whitespaceTokenizer
(
s
)
{
return
s
.
split
(
/
\s
+/
);
},
_getLocalSuggestions
:
function
(
terms
)
{
var
that
=
this
,
firstChars
=
[],
lists
=
[],
shortestList
,
suggestions
=
[];
utils
.
each
(
terms
,
function
(
i
,
term
)
{
var
firstChar
=
term
.
charAt
(
0
);
!~
utils
.
indexOf
(
firstChars
,
firstChar
)
&&
firstChars
.
push
(
firstChar
);
});
utils
.
each
(
firstChars
,
function
(
i
,
firstChar
)
{
var
list
=
that
.
adjacencyList
[
firstChar
];
if
(
!
list
)
{
return
false
;
nonword
:
function
nonwordTokenizer
(
s
)
{
return
s
.
split
(
/
\W
+/
);
}
};
_
.
mixin
(
Bloodhound
.
prototype
,
{
_loadPrefetch
:
function
loadPrefetch
(
o
)
{
var
that
=
this
,
serialized
,
deferred
;
if
(
serialized
=
this
.
_readFromStorage
(
o
.
thumbprint
))
{
this
.
index
.
bootstrap
(
serialized
);
deferred
=
$
.
Deferred
().
resolve
();
}
else
{
deferred
=
$
.
ajax
(
o
.
url
,
o
.
ajax
).
done
(
handlePrefetchResponse
);
}
lists
.
push
(
list
);
if
(
!
shortestList
||
list
.
length
<
shortestList
.
length
)
{
shortestList
=
list
;
return
deferred
;
function
handlePrefetchResponse
(
resp
)
{
var
filtered
;
filtered
=
o
.
filter
?
o
.
filter
(
resp
)
:
resp
;
that
.
add
(
filtered
);
that
.
_saveToStorage
(
that
.
index
.
serialize
(),
o
.
thumbprint
,
o
.
ttl
);
}
});
if
(
lists
.
length
<
firstChars
.
length
)
{
return
[];
},
_getFromRemote
:
function
getFromRemote
(
query
,
cb
)
{
var
that
=
this
,
url
,
uriEncodedQuery
;
query
=
query
||
""
;
uriEncodedQuery
=
encodeURIComponent
(
query
);
url
=
this
.
remote
.
replace
?
this
.
remote
.
replace
(
this
.
remote
.
url
,
query
)
:
this
.
remote
.
url
.
replace
(
this
.
remote
.
wildcard
,
uriEncodedQuery
);
return
this
.
transport
.
get
(
url
,
this
.
remote
.
ajax
,
handleRemoteResponse
);
function
handleRemoteResponse
(
resp
)
{
var
filtered
=
that
.
remote
.
filter
?
that
.
remote
.
filter
(
resp
)
:
resp
;
cb
(
filtered
);
}
utils
.
each
(
shortestList
,
function
(
i
,
id
)
{
var
item
=
that
.
itemHash
[
id
],
isCandidate
,
isMatch
;
isCandidate
=
utils
.
every
(
lists
,
function
(
list
)
{
return
~
utils
.
indexOf
(
list
,
id
);
});
isMatch
=
isCandidate
&&
utils
.
every
(
terms
,
function
(
term
)
{
return
utils
.
some
(
item
.
tokens
,
function
(
token
)
{
return
token
.
indexOf
(
term
)
===
0
;
});
},
_saveToStorage
:
function
saveToStorage
(
data
,
thumbprint
,
ttl
)
{
if
(
this
.
storage
)
{
this
.
storage
.
set
(
keys
.
data
,
data
,
ttl
);
this
.
storage
.
set
(
keys
.
protocol
,
location
.
protocol
,
ttl
);
this
.
storage
.
set
(
keys
.
thumbprint
,
thumbprint
,
ttl
);
}
},
_readFromStorage
:
function
readFromStorage
(
thumbprint
)
{
var
stored
=
{};
if
(
this
.
storage
)
{
stored
.
data
=
this
.
storage
.
get
(
keys
.
data
);
stored
.
protocol
=
this
.
storage
.
get
(
keys
.
protocol
);
stored
.
thumbprint
=
this
.
storage
.
get
(
keys
.
thumbprint
);
}
isExpired
=
stored
.
thumbprint
!==
thumbprint
||
stored
.
protocol
!==
location
.
protocol
;
return
stored
.
data
&&
!
isExpired
?
stored
.
data
:
null
;
},
initialize
:
function
initialize
()
{
var
that
=
this
,
deferred
;
deferred
=
this
.
prefetch
?
this
.
_loadPrefetch
(
this
.
prefetch
)
:
$
.
Deferred
().
resolve
();
this
.
local
&&
deferred
.
done
(
addLocalToIndex
);
this
.
transport
=
this
.
remote
?
new
Transport
(
this
.
remote
)
:
null
;
this
.
initialize
=
function
initialize
()
{
return
deferred
.
promise
();
};
return
deferred
.
promise
();
function
addLocalToIndex
()
{
that
.
add
(
that
.
local
);
}
},
add
:
function
add
(
data
)
{
this
.
index
.
add
(
data
);
},
get
:
function
get
(
query
,
cb
)
{
var
that
=
this
,
matches
,
cacheHit
=
false
;
matches
=
this
.
index
.
get
(
query
).
sort
(
this
.
sorter
).
slice
(
0
,
this
.
limit
);
if
(
matches
.
length
<
this
.
limit
&&
this
.
transport
)
{
cacheHit
=
this
.
_getFromRemote
(
query
,
returnRemoteMatches
);
}
!
cacheHit
&&
cb
&&
cb
(
matches
);
function
returnRemoteMatches
(
remoteMatches
)
{
var
matchesWithBackfill
=
matches
.
slice
(
0
);
_
.
each
(
remoteMatches
,
function
(
remoteMatch
)
{
var
isDuplicate
;
isDuplicate
=
_
.
some
(
matchesWithBackfill
,
function
(
match
)
{
return
that
.
dupDetector
(
remoteMatch
,
match
);
});
isMatch
&&
suggestions
.
push
(
item
);
!
isDuplicate
&&
matchesWithBackfill
.
push
(
remoteMatch
);
return
matchesWithBackfill
.
length
<
that
.
limit
;
});
return
suggestions
;
cb
&&
cb
(
matchesWithBackfill
.
sort
(
that
.
sorter
));
}
},
initialize
:
function
()
{
var
deferred
;
this
.
local
&&
this
.
_processLocalData
(
this
.
local
);
this
.
transport
=
this
.
remote
?
new
Transport
(
this
.
remote
)
:
null
;
deferred
=
this
.
prefetch
?
this
.
_loadPrefetchData
(
this
.
prefetch
)
:
$
.
Deferred
().
resolve
();
this
.
local
=
this
.
prefetch
=
this
.
remote
=
null
;
this
.
initialize
=
function
()
{
return
deferred
;
ttAdapter
:
function
ttAdapter
()
{
return
_
.
bind
(
this
.
get
,
this
);
}
});
return
Bloodhound
;
function
noSort
()
{
return
0
;
}
function
ignoreDuplicates
()
{
return
false
;
}
}();
var
html
=
{
wrapper
:
'<span class="twitter-typeahead"></span>'
,
dropdown
:
'<span class="tt-dropdown-menu"></span>'
,
dataset
:
'<div class="tt-dataset-%CLASS%"></div>'
,
suggestions
:
'<span class="tt-suggestions"></span>'
,
suggestion
:
'<div class="tt-suggestion">%BODY%</div>'
};
return
deferred
;
var
css
=
{
wrapper
:
{
position
:
"relative"
,
display
:
"inline-block"
},
getSuggestions
:
function
(
query
,
cb
)
{
var
that
=
this
,
terms
,
suggestions
,
cacheHit
=
false
;
if
(
query
.
length
<
this
.
minLength
)
{
return
;
hint
:
{
position
:
"absolute"
,
top
:
"0"
,
left
:
"0"
,
borderColor
:
"transparent"
,
boxShadow
:
"none"
},
input
:
{
position
:
"relative"
,
verticalAlign
:
"top"
,
backgroundColor
:
"transparent"
},
inputWithNoHint
:
{
position
:
"relative"
,
verticalAlign
:
"top"
},
dropdown
:
{
position
:
"absolute"
,
top
:
"100%"
,
left
:
"0"
,
zIndex
:
"100"
,
display
:
"none"
},
suggestions
:
{
display
:
"block"
},
suggestion
:
{
whiteSpace
:
"nowrap"
,
cursor
:
"pointer"
},
suggestionChild
:
{
whiteSpace
:
"normal"
},
ltr
:
{
left
:
"0"
,
right
:
"auto"
},
rtl
:
{
left
:
"auto"
,
right
:
" 0"
}
terms
=
utils
.
tokenizeQuery
(
query
);
suggestions
=
this
.
_getLocalSuggestions
(
terms
).
slice
(
0
,
this
.
limit
);
if
(
suggestions
.
length
<
this
.
limit
&&
this
.
transport
)
{
cacheHit
=
this
.
transport
.
get
(
query
,
processRemoteData
);
}
!
cacheHit
&&
cb
&&
cb
(
suggestions
);
function
processRemoteData
(
data
)
{
suggestions
=
suggestions
.
slice
(
0
);
utils
.
each
(
data
,
function
(
i
,
datum
)
{
var
item
=
that
.
_transformDatum
(
datum
),
isDuplicate
;
isDuplicate
=
utils
.
some
(
suggestions
,
function
(
suggestion
)
{
return
item
.
value
===
suggestion
.
value
;
};
if
(
_
.
isMsie
())
{
_
.
mixin
(
css
.
input
,
{
backgroundImage
:
"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
});
!
isDuplicate
&&
suggestions
.
push
(
item
);
return
suggestions
.
length
<
that
.
limit
;
}
if
(
_
.
isMsie
()
&&
_
.
isMsie
()
<=
7
)
{
_
.
mixin
(
css
.
input
,
{
marginTop
:
"-1px"
});
cb
&&
cb
(
suggestions
);
}
var
EventBus
=
function
()
{
var
namespace
=
"typeahead:"
;
function
EventBus
(
o
)
{
if
(
!
o
||
!
o
.
el
)
{
$
.
error
(
"EventBus initialized without el"
);
}
this
.
$el
=
$
(
o
.
el
);
}
_
.
mixin
(
EventBus
.
prototype
,
{
trigger
:
function
(
type
)
{
var
args
=
[].
slice
.
call
(
arguments
,
1
);
this
.
$el
.
trigger
(
namespace
+
type
,
args
);
}
});
return
Dataset
;
function
compileTemplate
(
template
,
engine
,
valueKey
)
{
var
renderFn
,
compiledTemplate
;
if
(
utils
.
isFunction
(
template
))
{
renderFn
=
template
;
}
else
if
(
utils
.
isString
(
template
))
{
compiledTemplate
=
engine
.
compile
(
template
);
renderFn
=
utils
.
bind
(
compiledTemplate
.
render
,
compiledTemplate
);
return
EventBus
;
}();
var
EventEmitter
=
function
()
{
var
splitter
=
/
\s
+/
,
nextTick
=
getNextTick
();
return
{
onSync
:
onSync
,
onAsync
:
onAsync
,
off
:
off
,
trigger
:
trigger
};
function
on
(
method
,
types
,
cb
,
context
)
{
var
type
;
if
(
!
cb
)
{
return
this
;
}
types
=
types
.
split
(
splitter
);
cb
=
context
?
bindContext
(
cb
,
context
)
:
cb
;
this
.
_callbacks
=
this
.
_callbacks
||
{};
while
(
type
=
types
.
shift
())
{
this
.
_callbacks
[
type
]
=
this
.
_callbacks
[
type
]
||
{
sync
:
[],
async
:
[]
};
this
.
_callbacks
[
type
][
method
].
push
(
cb
);
}
return
this
;
}
function
onAsync
(
types
,
cb
,
context
)
{
return
on
.
call
(
this
,
"async"
,
types
,
cb
,
context
);
}
function
onSync
(
types
,
cb
,
context
)
{
return
on
.
call
(
this
,
"sync"
,
types
,
cb
,
context
);
}
function
off
(
types
)
{
var
type
;
if
(
!
this
.
_callbacks
)
{
return
this
;
}
types
=
types
.
split
(
splitter
);
while
(
type
=
types
.
shift
())
{
delete
this
.
_callbacks
[
type
];
}
return
this
;
}
function
trigger
(
types
)
{
var
that
=
this
,
type
,
callbacks
,
args
,
syncFlush
,
asyncFlush
;
if
(
!
this
.
_callbacks
)
{
return
this
;
}
types
=
types
.
split
(
splitter
);
args
=
[].
slice
.
call
(
arguments
,
1
);
while
((
type
=
types
.
shift
())
&&
(
callbacks
=
this
.
_callbacks
[
type
]))
{
syncFlush
=
getFlush
(
callbacks
.
sync
,
this
,
[
type
].
concat
(
args
));
asyncFlush
=
getFlush
(
callbacks
.
async
,
this
,
[
type
].
concat
(
args
));
syncFlush
()
&&
nextTick
(
asyncFlush
);
}
return
this
;
}
function
getFlush
(
callbacks
,
context
,
args
)
{
return
flush
;
function
flush
()
{
var
cancelled
;
for
(
var
i
=
0
;
!
cancelled
&&
i
<
callbacks
.
length
;
i
+=
1
)
{
cancelled
=
callbacks
[
i
].
apply
(
context
,
args
)
===
false
;
}
return
!
cancelled
;
}
}
function
getNextTick
()
{
var
nextTickFn
,
messageChannel
;
if
(
window
.
setImmediate
)
{
nextTickFn
=
function
nextTickSetImmediate
(
fn
)
{
setImmediate
(
function
()
{
fn
();
});
};
}
else
{
renderFn
=
function
(
context
)
{
return
"<p>"
+
context
[
valueKey
]
+
"</p>"
;
nextTickFn
=
function
nextTickSetTimeout
(
fn
)
{
setTimeout
(
function
()
{
fn
();
},
0
);
};
}
return
renderFn
;
return
nextTickFn
;
}
function
bindContext
(
fn
,
context
)
{
return
fn
.
bind
?
fn
.
bind
(
context
)
:
function
()
{
fn
.
apply
(
context
,
[].
slice
.
call
(
arguments
,
0
));
};
}
}();
var
InputView
=
function
()
{
function
InputView
(
o
)
{
var
that
=
this
;
utils
.
bindAll
(
this
);
this
.
specialKeyCodeMap
=
{
var
highlight
=
function
(
doc
)
{
var
defaults
=
{
node
:
null
,
pattern
:
null
,
tagName
:
"strong"
,
className
:
null
,
wordsOnly
:
false
,
caseSensitive
:
false
};
return
function
hightlight
(
o
)
{
var
regex
;
o
=
_
.
mixin
({},
defaults
,
o
);
if
(
!
o
.
node
||
!
o
.
pattern
)
{
return
;
}
o
.
pattern
=
_
.
isArray
(
o
.
pattern
)
?
o
.
pattern
:
[
o
.
pattern
];
regex
=
getRegex
(
o
.
pattern
,
o
.
caseSensitive
,
o
.
wordsOnly
);
traverse
(
o
.
node
,
hightlightTextNode
);
function
hightlightTextNode
(
textNode
)
{
var
match
,
patternNode
;
if
(
match
=
regex
.
exec
(
textNode
.
data
))
{
wrapperNode
=
doc
.
createElement
(
o
.
tagName
);
o
.
className
&&
(
wrapperNode
.
className
=
o
.
className
);
patternNode
=
textNode
.
splitText
(
match
.
index
);
patternNode
.
splitText
(
match
[
0
].
length
);
wrapperNode
.
appendChild
(
patternNode
.
cloneNode
(
true
));
textNode
.
parentNode
.
replaceChild
(
wrapperNode
,
patternNode
);
}
return
!!
match
;
}
function
traverse
(
el
,
hightlightTextNode
)
{
var
childNode
,
TEXT_NODE_TYPE
=
3
;
for
(
var
i
=
0
;
i
<
el
.
childNodes
.
length
;
i
++
)
{
childNode
=
el
.
childNodes
[
i
];
if
(
childNode
.
nodeType
===
TEXT_NODE_TYPE
)
{
i
+=
hightlightTextNode
(
childNode
)
?
1
:
0
;
}
else
{
traverse
(
childNode
,
hightlightTextNode
);
}
}
}
};
function
getRegex
(
patterns
,
caseSensitive
,
wordsOnly
)
{
var
escapedPatterns
=
[],
regexStr
;
for
(
var
i
=
0
;
i
<
patterns
.
length
;
i
++
)
{
escapedPatterns
.
push
(
_
.
escapeRegExChars
(
patterns
[
i
]));
}
regexStr
=
wordsOnly
?
"
\\
b("
+
escapedPatterns
.
join
(
"|"
)
+
")
\\
b"
:
"("
+
escapedPatterns
.
join
(
"|"
)
+
")"
;
return
caseSensitive
?
new
RegExp
(
regexStr
)
:
new
RegExp
(
regexStr
,
"i"
);
}
}(
window
.
document
);
var
Input
=
function
()
{
var
specialKeyCodeMap
;
specialKeyCodeMap
=
{
9
:
"tab"
,
27
:
"esc"
,
37
:
"left"
,
...
...
@@ -573,84 +868,141 @@
38
:
"up"
,
40
:
"down"
};
function
Input
(
o
)
{
var
that
=
this
,
onBlur
,
onFocus
,
onKeydown
,
onInput
;
o
=
o
||
{};
if
(
!
o
.
input
)
{
$
.
error
(
"input is missing"
);
}
onBlur
=
_
.
bind
(
this
.
_onBlur
,
this
);
onFocus
=
_
.
bind
(
this
.
_onFocus
,
this
);
onKeydown
=
_
.
bind
(
this
.
_onKeydown
,
this
);
onInput
=
_
.
bind
(
this
.
_onInput
,
this
);
this
.
$hint
=
$
(
o
.
hint
);
this
.
$input
=
$
(
o
.
input
).
on
(
"blur.tt"
,
this
.
_handleBlur
).
on
(
"focus.tt"
,
this
.
_handleFocus
).
on
(
"keydown.tt"
,
this
.
_handleSpecialKeyEvent
);
if
(
!
utils
.
isMsie
())
{
this
.
$input
.
on
(
"input.tt"
,
this
.
_compareQueryToInputValue
);
this
.
$input
=
$
(
o
.
input
).
on
(
"blur.tt"
,
onBlur
).
on
(
"focus.tt"
,
onFocus
).
on
(
"keydown.tt"
,
onKeydown
);
if
(
this
.
$hint
.
length
===
0
)
{
this
.
setHintValue
=
this
.
getHintValue
=
this
.
clearHint
=
_
.
noop
;
}
if
(
!
_
.
isMsie
())
{
this
.
$input
.
on
(
"input.tt"
,
onInput
);
}
else
{
this
.
$input
.
on
(
"keydown.tt keypress.tt cut.tt paste.tt"
,
function
(
$e
)
{
if
(
that
.
specialKeyCodeMap
[
$e
.
which
||
$e
.
keyCode
])
{
if
(
specialKeyCodeMap
[
$e
.
which
||
$e
.
keyCode
])
{
return
;
}
utils
.
defer
(
that
.
_compareQueryToInputValue
);
_
.
defer
(
_
.
bind
(
that
.
_onInput
,
that
,
$e
)
);
});
}
this
.
query
=
this
.
$input
.
val
();
this
.
$overflowHelper
=
buildOverflowHelper
(
this
.
$input
);
}
utils
.
mixin
(
InputView
.
prototype
,
EventTarget
,
{
_handleFocus
:
function
()
{
Input
.
normalizeQuery
=
function
(
str
)
{
return
(
str
||
""
).
replace
(
/^
\s
*/g
,
""
).
replace
(
/
\s{2,}
/g
,
" "
);
};
_
.
mixin
(
Input
.
prototype
,
EventEmitter
,
{
_onBlur
:
function
onBlur
(
$e
)
{
this
.
resetInputValue
();
this
.
trigger
(
"blurred"
);
},
_onFocus
:
function
onFocus
(
$e
)
{
this
.
trigger
(
"focused"
);
},
_handleBlur
:
function
()
{
this
.
trigger
(
"blured"
);
_onKeydown
:
function
onKeydown
(
$e
)
{
var
keyName
=
specialKeyCodeMap
[
$e
.
which
||
$e
.
keyCode
];
this
.
_managePreventDefault
(
keyName
,
$e
);
if
(
keyName
&&
this
.
_shouldTrigger
(
keyName
,
$e
))
{
this
.
trigger
(
keyName
+
"Keyed"
,
$e
);
}
},
_handleSpecialKeyEvent
:
function
(
$e
)
{
var
keyName
=
this
.
specialKeyCodeMap
[
$e
.
which
||
$e
.
keyCode
];
keyName
&&
this
.
trigger
(
keyName
+
"Keyed"
,
$e
);
_onInput
:
function
onInput
(
$e
)
{
this
.
_checkInputValue
();
},
_compareQueryToInputValue
:
function
()
{
var
inputValue
=
this
.
getInputValue
(),
isSameQuery
=
compareQueries
(
this
.
query
,
inputValue
),
isSameQueryExceptWhitespace
=
isSameQuery
?
this
.
query
.
length
!==
inputValue
.
length
:
false
;
if
(
isSameQueryExceptWhitespace
)
{
this
.
trigger
(
"whitespaceChanged"
,
{
value
:
this
.
query
});
}
else
if
(
!
isSameQuery
)
{
this
.
trigger
(
"queryChanged"
,
{
value
:
this
.
query
=
inputValue
});
_managePreventDefault
:
function
managePreventDefault
(
keyName
,
$e
)
{
var
preventDefault
,
hintValue
,
inputValue
;
switch
(
keyName
)
{
case
"tab"
:
hintValue
=
this
.
getHintValue
();
inputValue
=
this
.
getInputValue
();
preventDefault
=
hintValue
&&
hintValue
!==
inputValue
&&
!
withModifier
(
$e
);
break
;
case
"up"
:
case
"down"
:
preventDefault
=
!
withModifier
(
$e
);
break
;
default
:
preventDefault
=
false
;
}
preventDefault
&&
$e
.
preventDefault
();
},
destroy
:
function
()
{
this
.
$hint
.
off
(
".tt"
);
this
.
$input
.
off
(
".tt"
);
this
.
$hint
=
this
.
$input
=
this
.
$overflowHelper
=
null
;
_shouldTrigger
:
function
shouldTrigger
(
keyName
,
$e
)
{
var
trigger
;
switch
(
keyName
)
{
case
"tab"
:
trigger
=
!
withModifier
(
$e
);
break
;
default
:
trigger
=
true
;
}
return
trigger
;
},
_checkInputValue
:
function
checkInputValue
()
{
var
inputValue
,
areEquivalent
,
hasDifferentWhitespace
;
inputValue
=
this
.
getInputValue
();
areEquivalent
=
areQueriesEquivalent
(
inputValue
,
this
.
query
);
hasDifferentWhitespace
=
areEquivalent
?
this
.
query
.
length
!==
inputValue
.
length
:
false
;
if
(
!
areEquivalent
)
{
this
.
trigger
(
"queryChanged"
,
this
.
query
=
inputValue
);
}
else
if
(
hasDifferentWhitespace
)
{
this
.
trigger
(
"whitespaceChanged"
,
this
.
query
);
}
},
focus
:
function
()
{
focus
:
function
focus
()
{
this
.
$input
.
focus
();
},
blur
:
function
()
{
blur
:
function
blur
()
{
this
.
$input
.
blur
();
},
getQuery
:
function
()
{
getQuery
:
function
getQuery
()
{
return
this
.
query
;
},
setQuery
:
function
(
query
)
{
setQuery
:
function
setQuery
(
query
)
{
this
.
query
=
query
;
},
getInputValue
:
function
()
{
getInputValue
:
function
getInputValue
()
{
return
this
.
$input
.
val
();
},
setInputValue
:
function
(
value
,
silent
)
{
setInputValue
:
function
setInputValue
(
value
,
silent
)
{
this
.
$input
.
val
(
value
);
!
silent
&&
this
.
_c
ompareQueryTo
InputValue
();
!
silent
&&
this
.
_c
heck
InputValue
();
},
getHintValue
:
function
()
{
getHintValue
:
function
getHintValue
()
{
return
this
.
$hint
.
val
();
},
setHintValue
:
function
(
value
)
{
setHintValue
:
function
setHintValue
(
value
)
{
this
.
$hint
.
val
(
value
);
},
getLanguageDirection
:
function
()
{
resetInputValue
:
function
resetInputValue
()
{
this
.
$input
.
val
(
this
.
query
);
},
clearHint
:
function
clearHint
()
{
this
.
$hint
.
val
(
""
);
},
getLanguageDirection
:
function
getLanguageDirection
()
{
return
(
this
.
$input
.
css
(
"direction"
)
||
"ltr"
).
toLowerCase
();
},
isOverflow
:
function
()
{
hasOverflow
:
function
hasOverflow
()
{
var
constraint
=
this
.
$input
.
width
()
-
2
;
this
.
$overflowHelper
.
text
(
this
.
getInputValue
());
return
this
.
$overflowHelper
.
width
()
>
this
.
$input
.
width
()
;
return
this
.
$overflowHelper
.
width
()
>
=
constraint
;
},
isCursorAtEnd
:
function
()
{
var
valueLength
=
this
.
$input
.
val
().
length
,
selectionStart
=
this
.
$input
[
0
].
selectionStart
,
range
;
if
(
utils
.
isNumber
(
selectionStart
))
{
var
valueLength
,
selectionStart
,
range
;
valueLength
=
this
.
$input
.
val
().
length
;
selectionStart
=
this
.
$input
[
0
].
selectionStart
;
if
(
_
.
isNumber
(
selectionStart
))
{
return
selectionStart
===
valueLength
;
}
else
if
(
document
.
selection
)
{
range
=
document
.
selection
.
createRange
();
...
...
@@ -658,13 +1010,17 @@
return
valueLength
===
range
.
text
.
length
;
}
return
true
;
},
destroy
:
function
destroy
()
{
this
.
$hint
.
off
(
".tt"
);
this
.
$input
.
off
(
".tt"
);
this
.
$hint
=
this
.
$input
=
this
.
$overflowHelper
=
null
;
}
});
return
Input
View
;
return
Input
;
function
buildOverflowHelper
(
$input
)
{
return
$
(
"<span></span>"
).
css
({
return
$
(
'<pre aria-hidden="true"></pre>'
).
css
({
position
:
"absolute"
,
left
:
"-9999px"
,
visibility
:
"hidden"
,
whiteSpace
:
"nowrap"
,
fontFamily
:
$input
.
css
(
"font-family"
),
...
...
@@ -679,452 +1035,597 @@
textTransform
:
$input
.
css
(
"text-transform"
)
}).
insertAfter
(
$input
);
}
function
compareQueries
(
a
,
b
)
{
a
=
(
a
||
""
).
replace
(
/^
\s
*/g
,
""
).
replace
(
/
\s{2,}
/g
,
" "
);
b
=
(
b
||
""
).
replace
(
/^
\s
*/g
,
""
).
replace
(
/
\s{2,}
/g
,
" "
);
return
a
===
b
;
function
areQueriesEquivalent
(
a
,
b
)
{
return
Input
.
normalizeQuery
(
a
)
===
Input
.
normalizeQuery
(
b
);
}
function
withModifier
(
$e
)
{
return
$e
.
altKey
||
$e
.
ctrlKey
||
$e
.
metaKey
||
$e
.
shiftKey
;
}
}();
var
DropdownView
=
function
()
{
var
html
=
{
suggestionsList
:
'<span class="tt-suggestions"></span>'
},
css
=
{
suggestionsList
:
{
display
:
"block"
var
Dataset
=
function
()
{
var
datasetKey
=
"ttDataset"
,
valueKey
=
"ttValue"
,
datumKey
=
"ttDatum"
;
function
Dataset
(
o
)
{
o
=
o
||
{};
o
.
templates
=
o
.
templates
||
{};
if
(
!
o
.
source
)
{
$
.
error
(
"missing source"
);
}
this
.
query
=
null
;
this
.
highlight
=
!!
o
.
highlight
;
this
.
name
=
o
.
name
||
_
.
getUniqueId
();
this
.
source
=
o
.
source
;
this
.
valueKey
=
o
.
displayKey
||
"value"
;
this
.
templates
=
getTemplates
(
o
.
templates
,
this
.
valueKey
);
this
.
$el
=
$
(
html
.
dataset
.
replace
(
"%CLASS%"
,
this
.
name
));
}
Dataset
.
extractDatasetName
=
function
extractDatasetName
(
el
)
{
return
$
(
el
).
data
(
datasetKey
);
};
Dataset
.
extractValue
=
function
extractDatum
(
el
)
{
return
$
(
el
).
data
(
valueKey
);
};
Dataset
.
extractDatum
=
function
extractDatum
(
el
)
{
return
$
(
el
).
data
(
datumKey
);
};
_
.
mixin
(
Dataset
.
prototype
,
EventEmitter
,
{
_render
:
function
render
(
query
,
suggestions
)
{
if
(
!
this
.
$el
)
{
return
;
}
var
that
=
this
,
hasSuggestions
;
this
.
$el
.
empty
();
hasSuggestions
=
suggestions
&&
suggestions
.
length
;
if
(
!
hasSuggestions
&&
this
.
templates
.
empty
)
{
this
.
$el
.
html
(
getEmptyHtml
()).
prepend
(
that
.
templates
.
header
?
getHeaderHtml
()
:
null
).
append
(
that
.
templates
.
footer
?
getFooterHtml
()
:
null
);
}
else
if
(
hasSuggestions
)
{
this
.
$el
.
html
(
getSuggestionsHtml
()).
prepend
(
that
.
templates
.
header
?
getHeaderHtml
()
:
null
).
append
(
that
.
templates
.
footer
?
getFooterHtml
()
:
null
);
}
this
.
trigger
(
"rendered"
);
function
getEmptyHtml
()
{
return
that
.
templates
.
empty
({
query
:
query
});
}
function
getSuggestionsHtml
()
{
var
$suggestions
;
$suggestions
=
$
(
html
.
suggestions
).
css
(
css
.
suggestions
).
append
(
_
.
map
(
suggestions
,
getSuggestionNode
));
that
.
highlight
&&
highlight
({
node
:
$suggestions
[
0
],
pattern
:
query
});
return
$suggestions
;
function
getSuggestionNode
(
suggestion
)
{
var
$el
,
innerHtml
,
outerHtml
;
innerHtml
=
that
.
templates
.
suggestion
(
suggestion
);
outerHtml
=
html
.
suggestion
.
replace
(
"%BODY%"
,
innerHtml
);
$el
=
$
(
outerHtml
).
data
(
datasetKey
,
that
.
name
).
data
(
valueKey
,
suggestion
[
that
.
valueKey
]).
data
(
datumKey
,
suggestion
);
$el
.
children
().
each
(
function
()
{
$
(
this
).
css
(
css
.
suggestionChild
);
});
return
$el
;
}
}
function
getHeaderHtml
()
{
return
that
.
templates
.
header
({
query
:
query
,
isEmpty
:
!
hasSuggestions
});
}
function
getFooterHtml
()
{
return
that
.
templates
.
footer
({
query
:
query
,
isEmpty
:
!
hasSuggestions
});
}
},
suggestion
:
{
whiteSpace
:
"nowrap"
,
cursor
:
"pointer"
getRoot
:
function
getRoot
()
{
return
this
.
$el
;
},
suggestionChild
:
{
whiteSpace
:
"normal"
update
:
function
update
(
query
)
{
var
that
=
this
;
this
.
query
=
query
;
this
.
source
(
query
,
renderIfQueryIsSame
);
function
renderIfQueryIsSame
(
suggestions
)
{
query
===
that
.
query
&&
that
.
_render
(
query
,
suggestions
);
}
},
clear
:
function
clear
()
{
this
.
_render
(
this
.
query
||
""
);
},
isEmpty
:
function
isEmpty
()
{
return
this
.
$el
.
is
(
":empty"
);
},
destroy
:
function
destroy
()
{
this
.
$el
=
null
;
}
});
return
Dataset
;
function
getTemplates
(
templates
,
valueKey
)
{
return
{
empty
:
templates
.
empty
&&
_
.
templatify
(
templates
.
empty
),
header
:
templates
.
header
&&
_
.
templatify
(
templates
.
header
),
footer
:
templates
.
footer
&&
_
.
templatify
(
templates
.
footer
),
suggestion
:
templates
.
suggestion
||
suggestionTemplate
};
function
DropdownView
(
o
)
{
utils
.
bindAll
(
this
);
function
suggestionTemplate
(
context
)
{
return
"<p>"
+
context
[
valueKey
]
+
"</p>"
;
}
}
}();
var
Dropdown
=
function
()
{
function
Dropdown
(
o
)
{
var
that
=
this
,
onMouseEnter
,
onMouseLeave
,
onSuggestionClick
,
onSuggestionMouseEnter
,
onSuggestionMouseLeave
;
o
=
o
||
{};
if
(
!
o
.
menu
)
{
$
.
error
(
"menu is required"
);
}
this
.
isOpen
=
false
;
this
.
isEmpty
=
true
;
this
.
isMouseOverDropdown
=
false
;
this
.
$menu
=
$
(
o
.
menu
).
on
(
"mouseenter.tt"
,
this
.
_handleMouseenter
).
on
(
"mouseleave.tt"
,
this
.
_handleMouseleave
).
on
(
"click.tt"
,
".tt-suggestion"
,
this
.
_handleSelection
).
on
(
"mouseover.tt"
,
".tt-suggestion"
,
this
.
_handleMouseover
);
this
.
datasets
=
_
.
map
(
o
.
datasets
,
initializeDataset
);
onMouseEnter
=
_
.
bind
(
this
.
_onMouseEnter
,
this
);
onMouseLeave
=
_
.
bind
(
this
.
_onMouseLeave
,
this
);
onSuggestionClick
=
_
.
bind
(
this
.
_onSuggestionClick
,
this
);
onSuggestionMouseEnter
=
_
.
bind
(
this
.
_onSuggestionMouseEnter
,
this
);
onSuggestionMouseLeave
=
_
.
bind
(
this
.
_onSuggestionMouseLeave
,
this
);
this
.
$menu
=
$
(
o
.
menu
).
on
(
"mouseenter.tt"
,
onMouseEnter
).
on
(
"mouseleave.tt"
,
onMouseLeave
).
on
(
"click.tt"
,
".tt-suggestion"
,
onSuggestionClick
).
on
(
"mouseenter.tt"
,
".tt-suggestion"
,
onSuggestionMouseEnter
).
on
(
"mouseleave.tt"
,
".tt-suggestion"
,
onSuggestionMouseLeave
);
_
.
each
(
this
.
datasets
,
function
(
dataset
)
{
that
.
$menu
.
append
(
dataset
.
getRoot
());
dataset
.
onSync
(
"rendered"
,
that
.
_onRendered
,
that
);
});
}
utils
.
mixin
(
DropdownView
.
prototype
,
EventTarget
,
{
_
handleMouseenter
:
function
(
)
{
_
.
mixin
(
Dropdown
.
prototype
,
EventEmitter
,
{
_
onMouseEnter
:
function
onMouseEnter
(
$e
)
{
this
.
isMouseOverDropdown
=
true
;
},
_
handleMouseleave
:
function
(
)
{
_
onMouseLeave
:
function
onMouseLeave
(
$e
)
{
this
.
isMouseOverDropdown
=
false
;
},
_handleMouseover
:
function
(
$e
)
{
var
$suggestion
=
$
(
$e
.
currentTarget
);
this
.
_getSuggestions
().
removeClass
(
"tt-is-under-cursor"
);
$suggestion
.
addClass
(
"tt-is-under-cursor"
);
_onSuggestionClick
:
function
onSuggestionClick
(
$e
)
{
this
.
trigger
(
"suggestionClicked"
,
$
(
$e
.
currentTarget
));
},
_
handleSelection
:
function
(
$e
)
{
var
$suggestion
=
$
(
$e
.
currentTarget
);
this
.
trigger
(
"suggestionSelected"
,
extractSuggestion
(
$suggestion
)
);
_
onSuggestionMouseEnter
:
function
onSuggestionMouseEnter
(
$e
)
{
this
.
_removeCursor
(
);
this
.
_setCursor
(
$
(
$e
.
currentTarget
),
true
);
},
_show
:
function
()
{
this
.
$menu
.
css
(
"display"
,
"block"
);
_onSuggestionMouseLeave
:
function
onSuggestionMouseLeave
(
$e
)
{
this
.
_removeCursor
();
},
_onRendered
:
function
onRendered
()
{
this
.
isEmpty
=
_
.
every
(
this
.
datasets
,
isDatasetEmpty
);
this
.
isEmpty
?
this
.
_hide
()
:
this
.
isOpen
&&
this
.
_show
();
this
.
trigger
(
"datasetRendered"
);
function
isDatasetEmpty
(
dataset
)
{
return
dataset
.
isEmpty
();
}
},
_hide
:
function
()
{
this
.
$menu
.
hide
();
},
_moveCursor
:
function
(
increment
)
{
var
$suggestions
,
$cur
,
nextIndex
,
$underCursor
;
if
(
!
this
.
isVisible
())
{
_show
:
function
()
{
this
.
$menu
.
css
(
"display"
,
"block"
);
},
_getSuggestions
:
function
getSuggestions
()
{
return
this
.
$menu
.
find
(
".tt-suggestion"
);
},
_getCursor
:
function
getCursor
()
{
return
this
.
$menu
.
find
(
".tt-cursor"
).
first
();
},
_setCursor
:
function
setCursor
(
$el
,
silent
)
{
$el
.
first
().
addClass
(
"tt-cursor"
);
!
silent
&&
this
.
trigger
(
"cursorMoved"
);
},
_removeCursor
:
function
removeCursor
()
{
this
.
_getCursor
().
removeClass
(
"tt-cursor"
);
},
_moveCursor
:
function
moveCursor
(
increment
)
{
var
$suggestions
,
$oldCursor
,
newCursorIndex
,
$newCursor
;
if
(
!
this
.
isOpen
)
{
return
;
}
$oldCursor
=
this
.
_getCursor
();
$suggestions
=
this
.
_getSuggestions
();
$cur
=
$suggestions
.
filter
(
".tt-is-under-cursor"
);
$cur
.
removeClass
(
"tt-is-under-cursor"
);
nextIndex
=
$suggestions
.
index
(
$cur
)
+
increment
;
nextIndex
=
(
nextIndex
+
1
)
%
(
$suggestions
.
length
+
1
)
-
1
;
if
(
nextIndex
===
-
1
)
{
this
.
_removeCursor
();
newCursorIndex
=
$suggestions
.
index
(
$oldCursor
)
+
increment
;
newCursorIndex
=
(
newCursorIndex
+
1
)
%
(
$suggestions
.
length
+
1
)
-
1
;
if
(
newCursorIndex
===
-
1
)
{
this
.
trigger
(
"cursorRemoved"
);
return
;
}
else
if
(
ne
xt
Index
<
-
1
)
{
ne
xt
Index
=
$suggestions
.
length
-
1
;
}
$underCursor
=
$suggestions
.
eq
(
nextIndex
).
addClass
(
"tt-is-under-cursor"
);
this
.
_ensureVisib
ility
(
$under
Cursor
);
this
.
trigger
(
"cursorMoved"
,
extractSuggestion
(
$underCursor
));
},
_getSuggestions
:
function
()
{
return
this
.
$menu
.
find
(
".tt-suggestions > .tt-suggestion"
)
;
},
_ensureVisibility
:
function
(
$el
)
{
var
menuHeight
=
this
.
$menu
.
height
()
+
parseInt
(
this
.
$menu
.
css
(
"paddingTop"
),
10
)
+
parseInt
(
this
.
$menu
.
css
(
"paddingBottom"
),
10
),
menuScrollTop
=
this
.
$menu
.
scrollTop
(),
elTop
=
$el
.
position
().
top
,
elBottom
=
elTop
+
$el
.
outerHeight
(
true
);
}
else
if
(
ne
wCursor
Index
<
-
1
)
{
ne
wCursor
Index
=
$suggestions
.
length
-
1
;
}
this
.
_setCursor
(
$newCursor
=
$suggestions
.
eq
(
newCursorIndex
)
);
this
.
_ensureVisib
le
(
$new
Cursor
);
},
_ensureVisible
:
function
ensureVisible
(
$el
)
{
var
elTop
,
elBottom
,
menuScrollTop
,
menuHeight
;
elTop
=
$el
.
position
().
top
;
elBottom
=
elTop
+
$el
.
outerHeight
(
true
);
menuScrollTop
=
this
.
$menu
.
scrollTop
();
menuHeight
=
this
.
$menu
.
height
()
+
parseInt
(
this
.
$menu
.
css
(
"paddingTop"
),
10
)
+
parseInt
(
this
.
$menu
.
css
(
"paddingBottom"
),
10
);
if
(
elTop
<
0
)
{
this
.
$menu
.
scrollTop
(
menuScrollTop
+
elTop
);
}
else
if
(
menuHeight
<
elBottom
)
{
this
.
$menu
.
scrollTop
(
menuScrollTop
+
(
elBottom
-
menuHeight
));
}
},
destroy
:
function
()
{
this
.
$menu
.
off
(
".tt"
);
this
.
$menu
=
null
;
},
isVisible
:
function
()
{
return
this
.
isOpen
&&
!
this
.
isEmpty
;
},
closeUnlessMouseIsOverDropdown
:
function
()
{
if
(
!
this
.
isMouseOverDropdown
)
{
this
.
close
();
}
},
close
:
function
()
{
close
:
function
close
()
{
if
(
this
.
isOpen
)
{
this
.
isOpen
=
false
;
this
.
isMouseOverDropdown
=
false
;
this
.
isOpen
=
this
.
isMouseOverDropdown
=
false
;
this
.
_removeCursor
()
;
this
.
_hide
();
this
.
$menu
.
find
(
".tt-suggestions > .tt-suggestion"
).
removeClass
(
"tt-is-under-cursor"
);
this
.
trigger
(
"closed"
);
}
},
open
:
function
()
{
open
:
function
open
()
{
if
(
!
this
.
isOpen
)
{
this
.
isOpen
=
true
;
!
this
.
isEmpty
&&
this
.
_show
();
this
.
trigger
(
"opened"
);
}
},
setLanguageDirection
:
function
(
dir
)
{
var
ltrCss
=
{
left
:
"0"
,
right
:
"auto"
},
rtlCss
=
{
left
:
"auto"
,
right
:
" 0"
};
dir
===
"ltr"
?
this
.
$menu
.
css
(
ltrCss
)
:
this
.
$menu
.
css
(
rtlCss
);
setLanguageDirection
:
function
setLanguageDirection
(
dir
)
{
this
.
$menu
.
css
(
dir
===
"ltr"
?
css
.
ltr
:
css
.
rtl
);
},
moveCursorUp
:
function
()
{
moveCursorUp
:
function
moveCursorUp
()
{
this
.
_moveCursor
(
-
1
);
},
moveCursorDown
:
function
()
{
moveCursorDown
:
function
moveCursorDown
()
{
this
.
_moveCursor
(
+
1
);
},
getSuggestionUnderCursor
:
function
()
{
var
$suggestion
=
this
.
_getSuggestions
().
filter
(
".tt-is-under-cursor"
).
first
();
return
$suggestion
.
length
>
0
?
extractSuggestion
(
$suggestion
)
:
null
;
},
getFirstSuggestion
:
function
()
{
var
$suggestion
=
this
.
_getSuggestions
().
first
();
return
$suggestion
.
length
>
0
?
extractSuggestion
(
$suggestion
)
:
null
;
},
renderSuggestions
:
function
(
dataset
,
suggestions
)
{
var
datasetClassName
=
"tt-dataset-"
+
dataset
.
name
,
wrapper
=
'<div class="tt-suggestion">%body</div>'
,
compiledHtml
,
$suggestionsList
,
$dataset
=
this
.
$menu
.
find
(
"."
+
datasetClassName
),
elBuilder
,
fragment
,
$el
;
if
(
$dataset
.
length
===
0
)
{
$suggestionsList
=
$
(
html
.
suggestionsList
).
css
(
css
.
suggestionsList
);
$dataset
=
$
(
"<div></div>"
).
addClass
(
datasetClassName
).
append
(
dataset
.
header
).
append
(
$suggestionsList
).
append
(
dataset
.
footer
).
appendTo
(
this
.
$menu
);
}
if
(
suggestions
.
length
>
0
)
{
this
.
isEmpty
=
false
;
this
.
isOpen
&&
this
.
_show
();
elBuilder
=
document
.
createElement
(
"div"
);
fragment
=
document
.
createDocumentFragment
();
utils
.
each
(
suggestions
,
function
(
i
,
suggestion
)
{
suggestion
.
dataset
=
dataset
.
name
;
compiledHtml
=
dataset
.
template
(
suggestion
.
datum
);
elBuilder
.
innerHTML
=
wrapper
.
replace
(
"%body"
,
compiledHtml
);
$el
=
$
(
elBuilder
.
firstChild
).
css
(
css
.
suggestion
).
data
(
"suggestion"
,
suggestion
);
$el
.
children
().
each
(
function
()
{
$
(
this
).
css
(
css
.
suggestionChild
);
});
fragment
.
appendChild
(
$el
[
0
]);
});
$dataset
.
show
().
find
(
".tt-suggestions"
).
html
(
fragment
);
}
else
{
this
.
clearSuggestions
(
dataset
.
name
);
getDatumForSuggestion
:
function
getDatumForSuggestion
(
$el
)
{
var
datum
=
null
;
if
(
$el
.
length
)
{
datum
=
{
raw
:
Dataset
.
extractDatum
(
$el
),
value
:
Dataset
.
extractValue
(
$el
),
datasetName
:
Dataset
.
extractDatasetName
(
$el
)
};
}
this
.
trigger
(
"suggestionsRendered"
)
;
return
datum
;
},
clearSuggestions
:
function
(
datasetName
)
{
var
$datasets
=
datasetName
?
this
.
$menu
.
find
(
".tt-dataset-"
+
datasetName
)
:
this
.
$menu
.
find
(
'[class^="tt-dataset-"]'
),
$suggestions
=
$datasets
.
find
(
".tt-suggestions"
);
$datasets
.
hide
();
$suggestions
.
empty
();
if
(
this
.
_getSuggestions
().
length
===
0
)
{
this
.
isEmpty
=
true
;
this
.
_hide
();
}
}
});
return
DropdownView
;
function
extractSuggestion
(
$el
)
{
return
$el
.
data
(
"suggestion"
);
getDatumForCursor
:
function
getDatumForCursor
()
{
return
this
.
getDatumForSuggestion
(
this
.
_getCursor
().
first
());
},
getDatumForTopSuggestion
:
function
getDatumForTopSuggestion
()
{
return
this
.
getDatumForSuggestion
(
this
.
_getSuggestions
().
first
());
},
update
:
function
update
(
query
)
{
_
.
each
(
this
.
datasets
,
updateDataset
);
function
updateDataset
(
dataset
)
{
dataset
.
update
(
query
);
}
}();
var
TypeaheadView
=
function
()
{
var
html
=
{
wrapper
:
'<span class="twitter-typeahead"></span>'
,
hint
:
'<input class="tt-hint" type="text" autocomplete="off" spellcheck="off" disabled>'
,
dropdown
:
'<span class="tt-dropdown-menu"></span>'
},
css
=
{
wrapper
:
{
position
:
"relative"
,
display
:
"inline-block"
},
hint
:
{
position
:
"absolute"
,
top
:
"0"
,
left
:
"0"
,
borderColor
:
"transparent"
,
boxShadow
:
"none"
empty
:
function
empty
()
{
_
.
each
(
this
.
datasets
,
clearDataset
);
function
clearDataset
(
dataset
)
{
dataset
.
clear
();
}
},
query
:
{
position
:
"relative"
,
verticalAlign
:
"top"
,
backgroundColor
:
"transparent"
isVisible
:
function
isVisible
()
{
return
this
.
isOpen
&&
!
this
.
isEmpty
;
},
d
ropdown
:
{
position
:
"absolute"
,
t
op
:
"100%"
,
left
:
"0"
,
zIndex
:
"100"
,
display
:
"none"
d
estroy
:
function
destroy
()
{
this
.
$menu
.
off
(
".tt"
);
t
his
.
$menu
=
null
;
_
.
each
(
this
.
datasets
,
destroyDataset
);
function
destroyDataset
(
dataset
)
{
dataset
.
destroy
();
}
};
if
(
utils
.
isMsie
())
{
utils
.
mixin
(
css
.
query
,
{
backgroundImage
:
"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
});
}
if
(
utils
.
isMsie
()
&&
utils
.
isMsie
()
<=
7
)
{
utils
.
mixin
(
css
.
wrapper
,
{
display
:
"inline"
,
zoom
:
"1"
});
utils
.
mixin
(
css
.
query
,
{
marginTop
:
"-1px"
});
return
Dropdown
;
function
initializeDataset
(
oDataset
)
{
return
new
Dataset
(
oDataset
);
}
function
TypeaheadView
(
o
)
{
var
$menu
,
$input
,
$hint
;
utils
.
bindAll
(
this
);
this
.
$node
=
buildDomStructure
(
o
.
input
);
this
.
datasets
=
o
.
datasets
;
this
.
dir
=
null
;
this
.
eventBus
=
o
.
eventBus
;
}();
var
Typeahead
=
function
()
{
var
attrsKey
=
"ttAttrs"
;
function
Typeahead
(
o
)
{
var
$menu
,
$input
,
$hint
,
datasets
;
o
=
o
||
{};
if
(
!
o
.
input
)
{
$
.
error
(
"missing input"
);
}
this
.
autoselect
=
!!
o
.
autoselect
;
this
.
minLength
=
_
.
isNumber
(
o
.
minLength
)
?
o
.
minLength
:
1
;
this
.
$node
=
buildDomStructure
(
o
.
input
,
o
.
withHint
);
$menu
=
this
.
$node
.
find
(
".tt-dropdown-menu"
);
$input
=
this
.
$node
.
find
(
".tt-
query
"
);
$input
=
this
.
$node
.
find
(
".tt-
input
"
);
$hint
=
this
.
$node
.
find
(
".tt-hint"
);
this
.
dropdownView
=
new
DropdownView
({
menu
:
$menu
}).
on
(
"suggestionSelected"
,
this
.
_handleSelection
).
on
(
"cursorMoved"
,
this
.
_clearHint
).
on
(
"cursorMoved"
,
this
.
_setInputValueToSuggestionUnderCursor
).
on
(
"cursorRemoved"
,
this
.
_setInputValueToQuery
).
on
(
"cursorRemoved"
,
this
.
_updateHint
).
on
(
"suggestionsRendered"
,
this
.
_updateHint
).
on
(
"opened"
,
this
.
_updateHint
).
on
(
"closed"
,
this
.
_clearHint
).
on
(
"opened closed"
,
this
.
_propagateEvent
);
this
.
inputView
=
new
InputView
({
this
.
eventBus
=
o
.
eventBus
||
new
EventBus
({
el
:
$input
});
this
.
dropdown
=
new
Dropdown
({
menu
:
$menu
,
datasets
:
o
.
datasets
}).
onSync
(
"suggestionClicked"
,
this
.
_onSuggestionClicked
,
this
).
onSync
(
"cursorMoved"
,
this
.
_onCursorMoved
,
this
).
onSync
(
"cursorRemoved"
,
this
.
_onCursorRemoved
,
this
).
onSync
(
"opened"
,
this
.
_onOpened
,
this
).
onSync
(
"closed"
,
this
.
_onClosed
,
this
).
onAsync
(
"datasetRendered"
,
this
.
_onDatasetRendered
,
this
);
this
.
input
=
new
Input
({
input
:
$input
,
hint
:
$hint
}).
on
(
"focused"
,
this
.
_openDropdown
).
on
(
"blured"
,
this
.
_closeDropdown
).
on
(
"blured"
,
this
.
_setInputValueToQuery
).
on
(
"enterKeyed tabKeyed"
,
this
.
_handleSelection
).
on
(
"queryChanged"
,
this
.
_clearHint
).
on
(
"queryChanged"
,
this
.
_clearSuggestions
).
on
(
"queryChanged"
,
this
.
_getSuggestions
).
on
(
"whitespaceChanged"
,
this
.
_updateHint
).
on
(
"queryChanged whitespaceChanged"
,
this
.
_openDropdown
).
on
(
"queryChanged whitespaceChanged"
,
this
.
_setLanguageDirection
).
on
(
"escKeyed"
,
this
.
_closeDropdown
).
on
(
"escKeyed"
,
this
.
_setInputValueToQuery
).
on
(
"tabKeyed upKeyed downKeyed"
,
this
.
_managePreventDefault
).
on
(
"upKeyed downKeyed"
,
this
.
_moveDropdownCursor
).
on
(
"upKeyed downKeyed"
,
this
.
_openDropdown
).
on
(
"tabKeyed leftKeyed rightKeyed"
,
this
.
_autocomplete
);
}
utils
.
mixin
(
TypeaheadView
.
prototype
,
EventTarget
,
{
_managePreventDefault
:
function
(
e
)
{
var
$e
=
e
.
data
,
hint
,
inputValue
,
preventDefault
=
false
;
switch
(
e
.
type
)
{
case
"tabKeyed"
:
hint
=
this
.
inputView
.
getHintValue
();
inputValue
=
this
.
inputView
.
getInputValue
();
preventDefault
=
hint
&&
hint
!==
inputValue
;
break
;
case
"upKeyed"
:
case
"downKeyed"
:
preventDefault
=
!
$e
.
shiftKey
&&
!
$e
.
ctrlKey
&&
!
$e
.
metaKey
;
break
;
}).
onSync
(
"focused"
,
this
.
_onFocused
,
this
).
onSync
(
"blurred"
,
this
.
_onBlurred
,
this
).
onSync
(
"enterKeyed"
,
this
.
_onEnterKeyed
,
this
).
onSync
(
"tabKeyed"
,
this
.
_onTabKeyed
,
this
).
onSync
(
"escKeyed"
,
this
.
_onEscKeyed
,
this
).
onSync
(
"upKeyed"
,
this
.
_onUpKeyed
,
this
).
onSync
(
"downKeyed"
,
this
.
_onDownKeyed
,
this
).
onSync
(
"leftKeyed"
,
this
.
_onLeftKeyed
,
this
).
onSync
(
"rightKeyed"
,
this
.
_onRightKeyed
,
this
).
onSync
(
"queryChanged"
,
this
.
_onQueryChanged
,
this
).
onSync
(
"whitespaceChanged"
,
this
.
_onWhitespaceChanged
,
this
);
$menu
.
on
(
"mousedown.tt"
,
function
(
$e
)
{
if
(
_
.
isMsie
()
&&
_
.
isMsie
()
<
9
)
{
$input
[
0
].
onbeforedeactivate
=
function
()
{
window
.
event
.
returnValue
=
false
;
$input
[
0
].
onbeforedeactivate
=
null
;
};
}
preventDefault
&&
$e
.
preventDefault
();
},
_setLanguageDirection
:
function
()
{
var
dir
=
this
.
inputView
.
getLanguageDirection
();
if
(
dir
!==
this
.
dir
)
{
this
.
dir
=
dir
;
this
.
$node
.
css
(
"direction"
,
dir
);
this
.
dropdownView
.
setLanguageDirection
(
dir
);
$e
.
preventDefault
();
});
}
},
_updateHint
:
function
()
{
var
suggestion
=
this
.
dropdownView
.
getFirstSuggestion
(),
hint
=
suggestion
?
suggestion
.
value
:
null
,
dropdownIsVisible
=
this
.
dropdownView
.
isVisible
(),
inputHasOverflow
=
this
.
inputView
.
isOverflow
(),
inputValue
,
query
,
escapedQuery
,
beginsWithQuery
,
match
;
if
(
hint
&&
dropdownIsVisible
&&
!
inputHasOverflow
)
{
inputValue
=
this
.
inputView
.
getInputValue
();
query
=
inputValue
.
replace
(
/
\s{2,}
/g
,
" "
).
replace
(
/^
\s
+/g
,
""
);
escapedQuery
=
utils
.
escapeRegExChars
(
query
);
beginsWithQuery
=
new
RegExp
(
"^(?:"
+
escapedQuery
+
")(.*$)"
,
"i"
);
match
=
beginsWithQuery
.
exec
(
hint
);
this
.
inputView
.
setHintValue
(
inputValue
+
(
match
?
match
[
1
]
:
""
));
_
.
mixin
(
Typeahead
.
prototype
,
{
_onSuggestionClicked
:
function
onSuggestionClicked
(
type
,
$el
)
{
var
datum
;
if
(
datum
=
this
.
dropdown
.
getDatumForSuggestion
(
$el
))
{
this
.
_select
(
datum
);
}
},
_onCursorMoved
:
function
onCursorMoved
()
{
var
datum
=
this
.
dropdown
.
getDatumForCursor
();
this
.
input
.
clearHint
();
this
.
input
.
setInputValue
(
datum
.
value
,
true
);
this
.
eventBus
.
trigger
(
"cursorchanged"
,
datum
.
raw
,
datum
.
datasetName
);
},
_onCursorRemoved
:
function
onCursorRemoved
()
{
this
.
input
.
resetInputValue
();
this
.
_updateHint
();
},
_onDatasetRendered
:
function
onDatasetRendered
()
{
this
.
_updateHint
();
},
_onOpened
:
function
onOpened
()
{
this
.
_updateHint
();
this
.
eventBus
.
trigger
(
"opened"
);
},
_onClosed
:
function
onClosed
()
{
this
.
input
.
clearHint
();
this
.
eventBus
.
trigger
(
"closed"
);
},
_onFocused
:
function
onFocused
()
{
this
.
dropdown
.
open
();
},
_onBlurred
:
function
onBlurred
()
{
!
this
.
dropdown
.
isMouseOverDropdown
&&
this
.
dropdown
.
close
();
},
_onEnterKeyed
:
function
onEnterKeyed
(
type
,
$e
)
{
var
cursorDatum
,
topSuggestionDatum
;
cursorDatum
=
this
.
dropdown
.
getDatumForCursor
();
topSuggestionDatum
=
this
.
dropdown
.
getDatumForTopSuggestion
();
if
(
cursorDatum
)
{
this
.
_select
(
cursorDatum
);
$e
.
preventDefault
();
}
else
if
(
this
.
autoselect
&&
topSuggestionDatum
)
{
this
.
_select
(
topSuggestionDatum
);
$e
.
preventDefault
();
}
},
_onTabKeyed
:
function
onTabKeyed
(
type
,
$e
)
{
var
datum
;
if
(
datum
=
this
.
dropdown
.
getDatumForCursor
())
{
this
.
_select
(
datum
);
$e
.
preventDefault
();
}
else
{
this
.
_autocomplete
();
}
},
_clearHint
:
function
()
{
this
.
inputView
.
setHintValue
(
""
);
},
_clearSuggestions
:
function
()
{
this
.
dropdownView
.
clearSuggestions
();
},
_setInputValueToQuery
:
function
()
{
this
.
inputView
.
setInputValue
(
this
.
inputView
.
getQuery
());
},
_setInputValueToSuggestionUnderCursor
:
function
(
e
)
{
var
suggestion
=
e
.
data
;
this
.
inputView
.
setInputValue
(
suggestion
.
value
,
true
);
},
_openDropdown
:
function
()
{
this
.
dropdownView
.
open
();
},
_closeDropdown
:
function
(
e
)
{
this
.
dropdownView
[
e
.
type
===
"blured"
?
"closeUnlessMouseIsOverDropdown"
:
"close"
]();
_onEscKeyed
:
function
onEscKeyed
()
{
this
.
dropdown
.
close
();
this
.
input
.
resetInputValue
();
},
_
moveDropdownCursor
:
function
(
e
)
{
var
$e
=
e
.
data
;
if
(
!
$e
.
shiftKey
&&
!
$e
.
ctrlKey
&&
!
$e
.
metaKey
)
{
this
.
dropdown
View
[
e
.
type
===
"upKeyed"
?
"moveCursorUp"
:
"moveCursorDown"
](
);
_
onUpKeyed
:
function
onUpKeyed
(
)
{
var
query
=
this
.
input
.
getQuery
()
;
if
(
!
this
.
dropdown
.
isOpen
&&
query
.
length
>=
this
.
minLength
)
{
this
.
dropdown
.
update
(
query
);
}
this
.
dropdown
.
open
();
this
.
dropdown
.
moveCursorUp
();
},
_handleSelection
:
function
(
e
)
{
var
byClick
=
e
.
type
===
"suggestionSelected"
,
suggestion
=
byClick
?
e
.
data
:
this
.
dropdownView
.
getSuggestionUnderCursor
();
if
(
suggestion
)
{
this
.
inputView
.
setInputValue
(
suggestion
.
value
);
byClick
?
this
.
inputView
.
focus
()
:
e
.
data
.
preventDefault
();
byClick
&&
utils
.
isMsie
()
?
utils
.
defer
(
this
.
dropdownView
.
close
)
:
this
.
dropdownView
.
close
();
this
.
eventBus
.
trigger
(
"selected"
,
suggestion
.
datum
,
suggestion
.
dataset
);
_onDownKeyed
:
function
onDownKeyed
()
{
var
query
=
this
.
input
.
getQuery
();
if
(
!
this
.
dropdown
.
isOpen
&&
query
.
length
>=
this
.
minLength
)
{
this
.
dropdown
.
update
(
query
);
}
this
.
dropdown
.
open
();
this
.
dropdown
.
moveCursorDown
();
},
_getSuggestions
:
function
()
{
var
that
=
this
,
query
=
this
.
inputView
.
getQuery
();
if
(
utils
.
isBlankString
(
query
))
{
return
;
}
utils
.
each
(
this
.
datasets
,
function
(
i
,
dataset
)
{
dataset
.
getSuggestions
(
query
,
function
(
suggestions
)
{
if
(
query
===
that
.
inputView
.
getQuery
())
{
that
.
dropdownView
.
renderSuggestions
(
dataset
,
suggestions
);
}
});
});
_onLeftKeyed
:
function
onLeftKeyed
()
{
this
.
dir
===
"rtl"
&&
this
.
_autocomplete
();
},
_autocomplete
:
function
(
e
)
{
var
isCursorAtEnd
,
ignoreEvent
,
query
,
hint
,
suggestion
;
if
(
e
.
type
===
"rightKeyed"
||
e
.
type
===
"leftKeyed"
)
{
isCursorAtEnd
=
this
.
inputView
.
isCursorAtEnd
();
ignoreEvent
=
this
.
inputView
.
getLanguageDirection
()
===
"ltr"
?
e
.
type
===
"leftKeyed"
:
e
.
type
===
"rightKeyed"
;
if
(
!
isCursorAtEnd
||
ignoreEvent
)
{
return
;
}
}
query
=
this
.
inputView
.
getQuery
();
hint
=
this
.
inputView
.
getHintValue
();
if
(
hint
!==
""
&&
query
!==
hint
)
{
suggestion
=
this
.
dropdownView
.
getFirstSuggestion
();
this
.
inputView
.
setInputValue
(
suggestion
.
value
);
this
.
eventBus
.
trigger
(
"autocompleted"
,
suggestion
.
datum
,
suggestion
.
dataset
);
}
_onRightKeyed
:
function
onRightKeyed
()
{
this
.
dir
===
"ltr"
&&
this
.
_autocomplete
();
},
_onQueryChanged
:
function
onQueryChanged
(
e
,
query
)
{
this
.
input
.
clearHint
();
this
.
dropdown
.
empty
();
query
.
length
>=
this
.
minLength
&&
this
.
dropdown
.
update
(
query
);
this
.
dropdown
.
open
();
this
.
_setLanguageDirection
();
},
_propagateEvent
:
function
(
e
)
{
this
.
eventBus
.
trigger
(
e
.
type
);
_onWhitespaceChanged
:
function
onWhitespaceChanged
()
{
this
.
_updateHint
();
this
.
dropdown
.
open
();
},
destroy
:
function
()
{
this
.
inputView
.
destroy
();
this
.
dropdownView
.
destroy
();
_setLanguageDirection
:
function
setLanguageDirection
()
{
var
dir
;
if
(
this
.
dir
!==
(
dir
=
this
.
input
.
getLanguageDirection
()))
{
this
.
dir
=
dir
;
this
.
$node
.
css
(
"direction"
,
dir
);
this
.
dropdown
.
setLanguageDirection
(
dir
);
}
},
_updateHint
:
function
updateHint
()
{
var
datum
,
inputValue
,
query
,
escapedQuery
,
frontMatchRegEx
,
match
;
datum
=
this
.
dropdown
.
getDatumForTopSuggestion
();
if
(
datum
&&
this
.
dropdown
.
isVisible
()
&&
!
this
.
input
.
hasOverflow
())
{
inputValue
=
this
.
input
.
getInputValue
();
query
=
Input
.
normalizeQuery
(
inputValue
);
escapedQuery
=
_
.
escapeRegExChars
(
query
);
frontMatchRegEx
=
new
RegExp
(
"^(?:"
+
escapedQuery
+
")(.*$)"
,
"i"
);
match
=
frontMatchRegEx
.
exec
(
datum
.
value
);
this
.
input
.
setHintValue
(
inputValue
+
(
match
?
match
[
1
]
:
""
));
}
},
_autocomplete
:
function
autocomplete
()
{
var
hint
,
query
,
datum
;
hint
=
this
.
input
.
getHintValue
();
query
=
this
.
input
.
getQuery
();
if
(
hint
&&
query
!==
hint
&&
this
.
input
.
isCursorAtEnd
())
{
datum
=
this
.
dropdown
.
getDatumForTopSuggestion
();
datum
&&
this
.
input
.
setInputValue
(
datum
.
value
);
this
.
eventBus
.
trigger
(
"autocompleted"
,
datum
.
raw
,
datum
.
datasetName
);
}
},
_select
:
function
select
(
datum
)
{
this
.
input
.
clearHint
();
this
.
input
.
setQuery
(
datum
.
value
);
this
.
input
.
setInputValue
(
datum
.
value
,
true
);
this
.
dropdown
.
empty
();
this
.
_setLanguageDirection
();
_
.
defer
(
_
.
bind
(
this
.
dropdown
.
close
,
this
.
dropdown
));
this
.
eventBus
.
trigger
(
"selected"
,
datum
.
raw
,
datum
.
datasetName
);
},
open
:
function
open
()
{
this
.
dropdown
.
open
();
},
close
:
function
close
()
{
this
.
dropdown
.
close
();
},
getQuery
:
function
getQuery
()
{
return
this
.
input
.
getQuery
();
},
setQuery
:
function
setQuery
(
val
)
{
this
.
input
.
setInputValue
(
val
);
},
destroy
:
function
destroy
()
{
this
.
input
.
destroy
();
this
.
dropdown
.
destroy
();
destroyDomStructure
(
this
.
$node
);
this
.
$node
=
null
;
},
setQuery
:
function
(
query
)
{
this
.
inputView
.
setQuery
(
query
);
this
.
inputView
.
setInputValue
(
query
);
this
.
_clearHint
();
this
.
_clearSuggestions
();
this
.
_getSuggestions
();
}
});
return
TypeaheadView
;
function
buildDomStructure
(
input
)
{
var
$wrapper
=
$
(
html
.
wrapper
),
$dropdown
=
$
(
html
.
dropdown
),
$input
=
$
(
input
),
$hint
=
$
(
html
.
hint
);
$wrapper
=
$wrapper
.
css
(
css
.
wrapper
);
$dropdown
=
$dropdown
.
css
(
css
.
dropdown
);
$hint
.
css
(
css
.
hint
).
css
({
backgroundAttachment
:
$input
.
css
(
"background-attachment"
),
backgroundClip
:
$input
.
css
(
"background-clip"
),
backgroundColor
:
$input
.
css
(
"background-color"
),
backgroundImage
:
$input
.
css
(
"background-image"
),
backgroundOrigin
:
$input
.
css
(
"background-origin"
),
backgroundPosition
:
$input
.
css
(
"background-position"
),
backgroundRepeat
:
$input
.
css
(
"background-repeat"
),
backgroundSize
:
$input
.
css
(
"background-size"
)
return
Typeahead
;
function
buildDomStructure
(
input
,
withHint
)
{
var
$input
,
$wrapper
,
$dropdown
,
$hint
;
$input
=
$
(
input
);
$wrapper
=
$
(
html
.
wrapper
).
css
(
css
.
wrapper
);
$dropdown
=
$
(
html
.
dropdown
).
css
(
css
.
dropdown
);
$hint
=
$input
.
clone
().
css
(
css
.
hint
).
css
(
getBackgroundStyles
(
$input
));
$hint
.
removeData
().
addClass
(
"tt-hint"
).
removeAttr
(
"id name placeholder"
).
prop
(
"disabled"
,
true
).
attr
({
autocomplete
:
"off"
,
spellcheck
:
"false"
});
$input
.
data
(
"ttAttrs"
,
{
$input
.
data
(
attrsKey
,
{
dir
:
$input
.
attr
(
"dir"
),
autocomplete
:
$input
.
attr
(
"autocomplete"
),
spellcheck
:
$input
.
attr
(
"spellcheck"
),
style
:
$input
.
attr
(
"style"
)
});
$input
.
addClass
(
"tt-
query
"
).
attr
({
$input
.
addClass
(
"tt-
input
"
).
attr
({
autocomplete
:
"off"
,
spellcheck
:
false
}).
css
(
css
.
query
);
}).
css
(
withHint
?
css
.
input
:
css
.
inputWithNoHint
);
try
{
!
$input
.
attr
(
"dir"
)
&&
$input
.
attr
(
"dir"
,
"auto"
);
}
catch
(
e
)
{}
return
$input
.
wrap
(
$wrapper
).
parent
().
prepend
(
$hint
).
append
(
$dropdown
);
return
$input
.
wrap
(
$wrapper
).
parent
().
prepend
(
withHint
?
$hint
:
null
).
append
(
$dropdown
);
}
function
getBackgroundStyles
(
$el
)
{
return
{
backgroundAttachment
:
$el
.
css
(
"background-attachment"
),
backgroundClip
:
$el
.
css
(
"background-clip"
),
backgroundColor
:
$el
.
css
(
"background-color"
),
backgroundImage
:
$el
.
css
(
"background-image"
),
backgroundOrigin
:
$el
.
css
(
"background-origin"
),
backgroundPosition
:
$el
.
css
(
"background-position"
),
backgroundRepeat
:
$el
.
css
(
"background-repeat"
),
backgroundSize
:
$el
.
css
(
"background-size"
)
};
}
function
destroyDomStructure
(
$node
)
{
var
$input
=
$node
.
find
(
".tt-
query
"
);
utils
.
each
(
$input
.
data
(
"ttAttrs"
),
function
(
key
,
val
)
{
utils
.
isUndefined
(
val
)
?
$input
.
removeAttr
(
key
)
:
$input
.
attr
(
key
,
val
);
var
$input
=
$node
.
find
(
".tt-
input
"
);
_
.
each
(
$input
.
data
(
attrsKey
),
function
(
val
,
key
)
{
_
.
isUndefined
(
val
)
?
$input
.
removeAttr
(
key
)
:
$input
.
attr
(
key
,
val
);
});
$input
.
detach
().
removeData
(
"ttAttrs"
).
removeClass
(
"tt-query
"
).
insertAfter
(
$node
);
$input
.
detach
().
removeData
(
attrsKey
).
removeClass
(
"tt-input
"
).
insertAfter
(
$node
);
$node
.
remove
();
}
}();
(
function
()
{
var
cache
=
{},
viewKey
=
"ttView"
,
methods
;
var
typeaheadKey
,
methods
;
typeaheadKey
=
"ttTypeahead"
;
methods
=
{
initialize
:
function
(
datasetDefs
)
{
var
datasets
;
datasetDefs
=
utils
.
isArray
(
datasetDefs
)
?
datasetDefs
:
[
datasetDefs
];
if
(
datasetDefs
.
length
===
0
)
{
$
.
error
(
"no datasets provided"
);
}
datasets
=
utils
.
map
(
datasetDefs
,
function
(
o
)
{
var
dataset
=
cache
[
o
.
name
]
?
cache
[
o
.
name
]
:
new
Dataset
(
o
);
if
(
o
.
name
)
{
cache
[
o
.
name
]
=
dataset
;
}
return
dataset
;
});
return
this
.
each
(
initialize
);
function
initialize
()
{
var
$input
=
$
(
this
),
deferreds
,
eventBus
=
new
EventBus
({
el
:
$input
});
deferreds
=
utils
.
map
(
datasets
,
function
(
dataset
)
{
return
dataset
.
initialize
();
initialize
:
function
initialize
(
o
)
{
var
datasets
=
[].
slice
.
call
(
arguments
,
1
);
o
=
o
||
{};
return
this
.
each
(
attach
);
function
attach
()
{
var
$input
=
$
(
this
),
eventBus
,
typeahead
;
_
.
each
(
datasets
,
function
(
d
)
{
d
.
highlight
=
!!
o
.
highlight
;
});
$input
.
data
(
viewKey
,
new
TypeaheadView
({
typeahead
=
new
Typeahead
({
input
:
$input
,
eventBus
:
eventBus
=
new
EventBus
({
el
:
$input
}),
withHint
:
_
.
isUndefined
(
o
.
hint
)
?
true
:
!!
o
.
hint
,
minLength
:
o
.
minLength
,
autoselect
:
o
.
autoselect
,
datasets
:
datasets
}));
$
.
when
.
apply
(
$
,
deferreds
).
always
(
function
()
{
utils
.
defer
(
function
()
{
eventBus
.
trigger
(
"initialized"
);
});
$input
.
data
(
typeaheadKey
,
typeahead
);
function
trigger
(
eventName
)
{
return
function
()
{
_
.
defer
(
function
()
{
eventBus
.
trigger
(
eventName
);
});
};
}
}
},
open
:
function
open
()
{
return
this
.
each
(
openTypeahead
);
function
openTypeahead
()
{
var
$input
=
$
(
this
),
typeahead
;
if
(
typeahead
=
$input
.
data
(
typeaheadKey
))
{
typeahead
.
open
();
}
}
},
destroy
:
function
()
{
return
this
.
each
(
destroy
);
function
destroy
()
{
var
$this
=
$
(
this
),
view
=
$this
.
data
(
viewKey
);
if
(
view
)
{
view
.
destroy
();
$this
.
removeData
(
viewKey
);
close
:
function
close
()
{
return
this
.
each
(
closeTypeahead
);
function
closeTypeahead
()
{
var
$input
=
$
(
this
),
typeahead
;
if
(
typeahead
=
$input
.
data
(
typeaheadKey
))
{
typeahead
.
close
();
}
}
},
setQuery
:
function
(
query
)
{
return
this
.
each
(
setQuery
);
val
:
function
val
(
newVal
)
{
return
_
.
isString
(
newVal
)
?
this
.
each
(
setQuery
)
:
this
.
map
(
getQuery
).
get
(
);
function
setQuery
()
{
var
view
=
$
(
this
).
data
(
viewKey
);
view
&&
view
.
setQuery
(
query
);
var
$input
=
$
(
this
),
typeahead
;
if
(
typeahead
=
$input
.
data
(
typeaheadKey
))
{
typeahead
.
setQuery
(
newVal
);
}
}
function
getQuery
()
{
var
$input
=
$
(
this
),
typeahead
,
query
;
if
(
typeahead
=
$input
.
data
(
typeaheadKey
))
{
query
=
typeahead
.
getQuery
();
}
return
query
;
}
},
destroy
:
function
destroy
()
{
return
this
.
each
(
unattach
);
function
unattach
()
{
var
$input
=
$
(
this
),
typeahead
;
if
(
typeahead
=
$input
.
data
(
typeaheadKey
))
{
typeahead
.
destroy
();
$input
.
removeData
(
typeaheadKey
);
}
}
}
};
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment