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
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1205 additions
and
704 deletions
+1205
-704
typeahead.js
extensions/gii/assets/typeahead.js
+1205
-704
No files found.
extensions/gii/assets/typeahead.js
View file @
0960664f
/*!
/*!
* typeahead.js 0.
9.3
* typeahead.js 0.
10.0
* https://github.com/twitter/typeahead
* https://github.com/twitter/typeahead
.js
* Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
* Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
*/
*/
(
function
(
$
)
{
(
function
(
$
)
{
var
VERSION
=
"0.9.3"
;
var
_
=
{
var
utils
=
{
isMsie
:
function
()
{
isMsie
:
function
()
{
var
match
=
/
(
msie
)
([\w
.
]
+
)
/i
.
exec
(
navigator
.
userAgent
);
return
/
(
msie|trident
)
/i
.
test
(
navigator
.
userAgent
)
?
navigator
.
userAgent
.
match
(
/
(
msie |rv:
)(\d
+
(
.
\d
+
)?)
/i
)[
2
]
:
false
;
return
match
?
parseInt
(
match
[
2
],
10
)
:
false
;
},
},
isBlankString
:
function
(
str
)
{
isBlankString
:
function
(
str
)
{
return
!
str
||
/^
\s
*$/
.
test
(
str
);
return
!
str
||
/^
\s
*$/
.
test
(
str
);
...
@@ -30,21 +28,12 @@
...
@@ -30,21 +28,12 @@
return
typeof
obj
===
"undefined"
;
return
typeof
obj
===
"undefined"
;
},
},
bind
:
$
.
proxy
,
bind
:
$
.
proxy
,
bindAll
:
function
(
obj
)
{
each
:
function
(
collection
,
cb
)
{
var
val
;
$
.
each
(
collection
,
reverseArgs
)
;
f
or
(
var
key
in
obj
)
{
f
unction
reverseArgs
(
index
,
value
)
{
$
.
isFunction
(
val
=
obj
[
key
])
&&
(
obj
[
key
]
=
$
.
proxy
(
val
,
obj
)
);
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
,
map
:
$
.
map
,
filter
:
$
.
grep
,
filter
:
$
.
grep
,
every
:
function
(
obj
,
test
)
{
every
:
function
(
obj
,
test
)
{
...
@@ -78,6 +67,12 @@
...
@@ -78,6 +67,12 @@
return
counter
++
;
return
counter
++
;
};
};
}(),
}(),
templatify
:
function
templatify
(
obj
)
{
return
$
.
isFunction
(
obj
)
?
obj
:
template
;
function
template
()
{
return
String
(
obj
);
}
},
defer
:
function
(
fn
)
{
defer
:
function
(
fn
)
{
setTimeout
(
fn
,
0
);
setTimeout
(
fn
,
0
);
},
},
...
@@ -123,69 +118,69 @@
...
@@ -123,69 +118,69 @@
return
result
;
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
()
{}
noop
:
function
()
{}
};
};
var
EventTarget
=
function
()
{
var
VERSION
=
"0.10.0"
;
var
eventSplitter
=
/
\s
+/
;
var
LruCache
=
function
(
root
,
undefined
)
{
return
{
function
LruCache
(
maxSize
)
{
on
:
function
(
events
,
callback
)
{
this
.
maxSize
=
maxSize
||
100
;
var
event
;
this
.
size
=
0
;
if
(
!
callback
)
{
this
.
hash
=
{};
return
this
;
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
];
}
}
this
.
_callbacks
=
this
.
_callbacks
||
{};
if
(
node
=
this
.
hash
[
key
])
{
events
=
events
.
split
(
eventSplitter
);
node
.
val
=
val
;
while
(
event
=
events
.
shift
())
{
this
.
list
.
moveToFront
(
node
);
this
.
_callbacks
[
event
]
=
this
.
_callbacks
[
event
]
||
[];
}
else
{
this
.
_callbacks
[
event
].
push
(
callback
);
node
=
new
Node
(
key
,
val
);
this
.
list
.
add
(
node
);
this
.
hash
[
key
]
=
node
;
this
.
size
++
;
}
}
return
this
;
},
},
trigger
:
function
(
events
,
data
)
{
get
:
function
get
(
key
)
{
var
event
,
callbacks
;
var
node
=
this
.
hash
[
key
];
if
(
!
this
.
_callbacks
)
{
if
(
node
)
{
return
this
;
this
.
list
.
moveToFront
(
node
);
}
return
node
.
val
;
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
});
}
}
}
}
return
this
;
}
};
}();
var
EventBus
=
function
()
{
var
namespace
=
"typeahead:"
;
function
EventBus
(
o
)
{
if
(
!
o
||
!
o
.
el
)
{
$
.
error
(
"EventBus initialized without el"
);
}
}
this
.
$el
=
$
(
o
.
el
);
});
function
List
()
{
this
.
head
=
this
.
tail
=
null
;
}
}
utils
.
mixin
(
EventBus
.
prototype
,
{
_
.
mixin
(
List
.
prototype
,
{
trigger
:
function
(
type
)
{
add
:
function
add
(
node
)
{
var
args
=
[].
slice
.
call
(
arguments
,
1
);
if
(
this
.
head
)
{
this
.
$el
.
trigger
(
namespace
+
type
,
args
);
node
.
next
=
this
.
head
;
this
.
head
.
prev
=
node
;
}
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
PersistentStorage
=
function
()
{
var
ls
,
methods
;
var
ls
,
methods
;
try
{
try
{
...
@@ -215,7 +210,7 @@
...
@@ -215,7 +210,7 @@
return
decode
(
ls
.
getItem
(
this
.
_prefix
(
key
)));
return
decode
(
ls
.
getItem
(
this
.
_prefix
(
key
)));
},
},
set
:
function
(
key
,
val
,
ttl
)
{
set
:
function
(
key
,
val
,
ttl
)
{
if
(
utils
.
isNumber
(
ttl
))
{
if
(
_
.
isNumber
(
ttl
))
{
ls
.
setItem
(
this
.
_ttlKey
(
key
),
encode
(
now
()
+
ttl
));
ls
.
setItem
(
this
.
_ttlKey
(
key
),
encode
(
now
()
+
ttl
));
}
else
{
}
else
{
ls
.
removeItem
(
this
.
_ttlKey
(
key
));
ls
.
removeItem
(
this
.
_ttlKey
(
key
));
...
@@ -241,416 +236,773 @@
...
@@ -241,416 +236,773 @@
},
},
isExpired
:
function
(
key
)
{
isExpired
:
function
(
key
)
{
var
ttl
=
decode
(
ls
.
getItem
(
this
.
_ttlKey
(
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
{
}
else
{
methods
=
{
methods
=
{
get
:
utils
.
noop
,
get
:
_
.
noop
,
set
:
utils
.
noop
,
set
:
_
.
noop
,
remove
:
utils
.
noop
,
remove
:
_
.
noop
,
clear
:
utils
.
noop
,
clear
:
_
.
noop
,
isExpired
:
utils
.
noop
isExpired
:
_
.
noop
};
};
}
}
utils
.
mixin
(
PersistentStorage
.
prototype
,
methods
);
_
.
mixin
(
PersistentStorage
.
prototype
,
methods
);
return
PersistentStorage
;
return
PersistentStorage
;
function
now
()
{
function
now
()
{
return
new
Date
().
getTime
();
return
new
Date
().
getTime
();
}
}
function
encode
(
val
)
{
function
encode
(
val
)
{
return
JSON
.
stringify
(
utils
.
isUndefined
(
val
)
?
null
:
val
);
return
JSON
.
stringify
(
_
.
isUndefined
(
val
)
?
null
:
val
);
}
}
function
decode
(
val
)
{
function
decode
(
val
)
{
return
JSON
.
parse
(
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
Transport
=
function
()
{
var
pendingRequestsCount
=
0
,
pendingRequests
=
{},
maxPendingRequests
,
requestCache
;
var
pendingRequestsCount
=
0
,
pendingRequests
=
{},
maxPendingRequests
=
6
,
requestCache
=
new
LruCache
(
10
)
;
function
Transport
(
o
)
{
function
Transport
(
o
)
{
utils
.
bindAll
(
this
);
o
=
o
||
{};
o
=
utils
.
isString
(
o
)
?
{
this
.
_send
=
o
.
send
?
callbackToDeferred
(
o
.
send
)
:
$
.
ajax
;
url
:
o
this
.
_get
=
o
.
rateLimiter
?
o
.
rateLimiter
(
this
.
_get
)
:
this
.
_get
;
}
:
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
);
}
}
utils
.
mixin
(
Transport
.
prototype
,
{
Transport
.
setMaxPendingRequests
=
function
setMaxPendingRequests
(
num
)
{
_get
:
function
(
url
,
cb
)
{
maxPendingRequests
=
num
;
var
that
=
this
;
};
if
(
belowPendingRequestsThreshold
())
{
Transport
.
resetCache
=
function
clearCache
()
{
this
.
_sendRequest
(
url
).
done
(
done
);
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
{
}
else
{
this
.
onDeckRequestArgs
=
[].
slice
.
call
(
arguments
,
0
);
this
.
onDeckRequestArgs
=
[].
slice
.
call
(
arguments
,
0
);
}
}
function
done
(
resp
)
{
function
done
(
resp
)
{
var
data
=
that
.
filter
?
that
.
filter
(
resp
)
:
resp
;
cb
&&
cb
(
resp
);
cb
&&
cb
(
data
);
requestCache
.
set
(
url
,
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
()
{
function
always
()
{
decrementPendingRequests
()
;
pendingRequestsCount
--
;
pendingRequests
[
url
]
=
null
;
delete
pendingRequests
[
url
]
;
if
(
that
.
onDeckRequestArgs
)
{
if
(
that
.
onDeckRequestArgs
)
{
that
.
_get
.
apply
(
that
,
that
.
onDeckRequestArgs
);
that
.
_get
.
apply
(
that
,
that
.
onDeckRequestArgs
);
that
.
onDeckRequestArgs
=
null
;
that
.
onDeckRequestArgs
=
null
;
}
}
}
}
},
},
get
:
function
(
query
,
cb
)
{
get
:
function
(
url
,
o
,
cb
)
{
var
that
=
this
,
encodedQuery
=
encodeURIComponent
(
query
||
""
),
url
,
resp
;
var
that
=
this
,
resp
;
cb
=
cb
||
utils
.
noop
;
if
(
_
.
isFunction
(
o
))
{
url
=
this
.
replace
?
this
.
replace
(
this
.
url
,
encodedQuery
)
:
this
.
url
.
replace
(
this
.
wildcard
,
encodedQuery
);
cb
=
o
;
o
=
{};
}
if
(
resp
=
requestCache
.
get
(
url
))
{
if
(
resp
=
requestCache
.
get
(
url
))
{
utils
.
defer
(
function
()
{
_
.
defer
(
function
()
{
cb
(
that
.
filter
?
that
.
filter
(
resp
)
:
resp
);
cb
&&
cb
(
resp
);
});
});
}
else
{
}
else
{
this
.
_get
(
url
,
cb
);
this
.
_get
(
url
,
o
,
cb
);
}
}
return
!!
resp
;
return
!!
resp
;
}
}
});
});
return
Transport
;
return
Transport
;
function
incrementPendingRequests
()
{
function
callbackToDeferred
(
fn
)
{
pendingRequestsCount
++
;
return
function
customSendWrapper
(
url
,
o
)
{
var
deferred
=
$
.
Deferred
();
fn
(
url
,
o
,
onSuccess
,
onError
);
return
deferred
;
function
onSuccess
(
resp
)
{
_
.
defer
(
function
()
{
deferred
.
resolve
(
resp
);
});
}
function
onError
(
err
)
{
_
.
defer
(
function
()
{
deferred
.
reject
(
err
);
});
}
};
}
}();
var
SearchIndex
=
function
()
{
function
SearchIndex
(
o
)
{
o
=
o
||
{};
if
(
!
o
.
datumTokenizer
||
!
o
.
queryTokenizer
)
{
$
.
error
(
"datumTokenizer and queryTokenizer are both 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
);
}
});
});
},
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
;
}
node
=
that
.
trie
;
chars
=
token
.
split
(
""
);
while
(
node
&&
(
ch
=
chars
.
shift
()))
{
node
=
node
.
children
[
ch
];
}
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
];
})
:
[];
},
serialize
:
function
serialize
()
{
return
{
datums
:
this
.
datums
,
trie
:
this
.
trie
};
}
});
return
SearchIndex
;
function
normalizeTokens
(
tokens
)
{
tokens
=
_
.
filter
(
tokens
,
function
(
token
)
{
return
!!
token
;
});
tokens
=
_
.
map
(
tokens
,
function
(
token
)
{
return
token
.
toLowerCase
();
});
return
tokens
;
}
}
function
decrementPendingRequests
()
{
function
newNode
()
{
pendingRequestsCount
--
;
return
{
ids
:
[],
children
:
{}
};
}
}
function
belowPendingRequestsThreshold
()
{
function
unique
(
array
)
{
return
pendingRequestsCount
<
maxPendingRequests
;
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
Dataset
=
function
()
{
var
oParser
=
function
()
{
var
keys
=
{
return
{
thumbprint
:
"thumbprint"
,
local
:
getLocal
,
protocol
:
"protocol"
,
prefetch
:
getPrefetch
,
itemHash
:
"itemHash"
,
remote
:
getRemote
adjacencyList
:
"adjacencyList"
};
};
function
Dataset
(
o
)
{
function
getLocal
(
o
)
{
utils
.
bindAll
(
this
);
return
o
.
local
||
null
;
if
(
utils
.
isString
(
o
.
template
)
&&
!
o
.
engine
)
{
}
$
.
error
(
"no template engine specified"
);
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
);
};
}
}
if
(
!
o
.
local
&&
!
o
.
prefetch
&&
!
o
.
remote
)
{
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"
);
$
.
error
(
"one of local, prefetch, or remote is required"
);
}
}
this
.
name
=
o
.
name
||
utils
.
getUniqueId
();
this
.
limit
=
o
.
limit
||
5
;
this
.
limit
=
o
.
limit
||
5
;
this
.
minLength
=
o
.
minLength
||
1
;
this
.
sorter
=
o
.
sorter
||
noSort
;
this
.
header
=
o
.
header
;
this
.
dupDetector
=
o
.
dupDetector
||
ignoreDuplicates
;
this
.
footer
=
o
.
footer
;
this
.
local
=
oParser
.
local
(
o
)
;
this
.
valueKey
=
o
.
valueKey
||
"value"
;
this
.
prefetch
=
oParser
.
prefetch
(
o
)
;
this
.
template
=
compileTemplate
(
o
.
template
,
o
.
engine
,
this
.
valueKey
);
this
.
remote
=
oParser
.
remote
(
o
);
this
.
local
=
o
.
loca
l
;
this
.
cacheKey
=
this
.
prefetch
?
this
.
prefetch
.
cacheKey
||
this
.
prefetch
.
url
:
nul
l
;
this
.
prefetch
=
o
.
prefetch
;
this
.
index
=
new
SearchIndex
({
this
.
remote
=
o
.
remote
;
datumTokenizer
:
o
.
datumTokenizer
,
this
.
itemHash
=
{};
queryTokenizer
:
o
.
queryTokenizer
this
.
adjacencyList
=
{}
;
})
;
this
.
storage
=
o
.
name
?
new
PersistentStorage
(
o
.
name
)
:
null
;
this
.
storage
=
this
.
cacheKey
?
new
PersistentStorage
(
this
.
cacheKey
)
:
null
;
}
}
utils
.
mixin
(
Dataset
.
prototype
,
{
Bloodhound
.
tokenizers
=
{
_processLocalData
:
function
(
data
)
{
whitespace
:
function
whitespaceTokenizer
(
s
)
{
this
.
_mergeProcessedData
(
this
.
_processData
(
data
)
);
return
s
.
split
(
/
\s
+/
);
},
},
_loadPrefetchData
:
function
(
o
)
{
nonword
:
function
nonwordTokenizer
(
s
)
{
var
that
=
this
,
thumbprint
=
VERSION
+
(
o
.
thumbprint
||
""
),
storedThumbprint
,
storedProtocol
,
storedItemHash
,
storedAdjacencyList
,
isExpired
,
deferred
;
return
s
.
split
(
/
\W
+/
);
if
(
this
.
storage
)
{
}
storedThumbprint
=
this
.
storage
.
get
(
keys
.
thumbprint
);
};
storedProtocol
=
this
.
storage
.
get
(
keys
.
protocol
);
_
.
mixin
(
Bloodhound
.
prototype
,
{
storedItemHash
=
this
.
storage
.
get
(
keys
.
itemHash
);
_loadPrefetch
:
function
loadPrefetch
(
o
)
{
storedAdjacencyList
=
this
.
storage
.
get
(
keys
.
adjacencyList
);
var
that
=
this
,
serialized
,
deferred
;
}
if
(
serialized
=
this
.
_readFromStorage
(
o
.
thumbprint
))
{
isExpired
=
storedThumbprint
!==
thumbprint
||
storedProtocol
!==
utils
.
getProtocol
();
this
.
index
.
bootstrap
(
serialized
);
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
();
deferred
=
$
.
Deferred
().
resolve
();
}
else
{
}
else
{
deferred
=
$
.
getJSON
(
o
.
url
).
done
(
processPrefetchData
);
deferred
=
$
.
ajax
(
o
.
url
,
o
.
ajax
).
done
(
handlePrefetchResponse
);
}
}
return
deferred
;
return
deferred
;
function
processPrefetchData
(
data
)
{
function
handlePrefetchResponse
(
resp
)
{
var
filteredData
=
o
.
filter
?
o
.
filter
(
data
)
:
data
,
processedData
=
that
.
_processData
(
filteredData
),
itemHash
=
processedData
.
itemHash
,
adjacencyList
=
processedData
.
adjacencyList
;
var
filtered
;
if
(
that
.
storage
)
{
filtered
=
o
.
filter
?
o
.
filter
(
resp
)
:
resp
;
that
.
storage
.
set
(
keys
.
itemHash
,
itemHash
,
o
.
ttl
);
that
.
add
(
filtered
);
that
.
storage
.
set
(
keys
.
adjacencyList
,
adjacencyList
,
o
.
ttl
);
that
.
_saveToStorage
(
that
.
index
.
serialize
(),
o
.
thumbprint
,
o
.
ttl
);
that
.
storage
.
set
(
keys
.
thumbprint
,
thumbprint
,
o
.
ttl
);
that
.
storage
.
set
(
keys
.
protocol
,
utils
.
getProtocol
(),
o
.
ttl
);
}
that
.
_mergeProcessedData
(
processedData
);
}
}
},
},
_transformDatum
:
function
(
datum
)
{
_getFromRemote
:
function
getFromRemote
(
query
,
cb
)
{
var
value
=
utils
.
isString
(
datum
)
?
datum
:
datum
[
this
.
valueKey
],
tokens
=
datum
.
tokens
||
utils
.
tokenizeText
(
value
),
item
=
{
var
that
=
this
,
url
,
uriEncodedQuery
;
value
:
value
,
query
=
query
||
""
;
tokens
:
tokens
uriEncodedQuery
=
encodeURIComponent
(
query
);
};
url
=
this
.
remote
.
replace
?
this
.
remote
.
replace
(
this
.
remote
.
url
,
query
)
:
this
.
remote
.
url
.
replace
(
this
.
remote
.
wildcard
,
uriEncodedQuery
);
if
(
utils
.
isString
(
datum
))
{
return
this
.
transport
.
get
(
url
,
this
.
remote
.
ajax
,
handleRemoteResponse
);
item
.
datum
=
{};
function
handleRemoteResponse
(
resp
)
{
item
.
datum
[
this
.
valueKey
]
=
datum
;
var
filtered
=
that
.
remote
.
filter
?
that
.
remote
.
filter
(
resp
)
:
resp
;
}
else
{
cb
(
filtered
);
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
{
itemHash
:
itemHash
,
adjacencyList
:
adjacencyList
};
},
},
_mergeProcessedData
:
function
(
processedData
)
{
_saveToStorage
:
function
saveToStorage
(
data
,
thumbprint
,
ttl
)
{
var
that
=
this
;
if
(
this
.
storage
)
{
utils
.
mixin
(
this
.
itemHash
,
processedData
.
itemHash
);
this
.
storage
.
set
(
keys
.
data
,
data
,
ttl
);
utils
.
each
(
processedData
.
adjacencyList
,
function
(
character
,
adjacency
)
{
this
.
storage
.
set
(
keys
.
protocol
,
location
.
protocol
,
ttl
);
var
masterAdjacency
=
that
.
adjacencyList
[
character
];
this
.
storage
.
set
(
keys
.
thumbprint
,
thumbprint
,
ttl
);
that
.
adjacencyList
[
character
]
=
masterAdjacency
?
masterAdjacency
.
concat
(
adjacency
)
:
adjacency
;
}
});
},
},
_getLocalSuggestions
:
function
(
terms
)
{
_readFromStorage
:
function
readFromStorage
(
thumbprint
)
{
var
that
=
this
,
firstChars
=
[],
lists
=
[],
shortestList
,
suggestions
=
[];
var
stored
=
{};
utils
.
each
(
terms
,
function
(
i
,
term
)
{
if
(
this
.
storage
)
{
var
firstChar
=
term
.
charAt
(
0
);
stored
.
data
=
this
.
storage
.
get
(
keys
.
data
);
!~
utils
.
indexOf
(
firstChars
,
firstChar
)
&&
firstChars
.
push
(
firstChar
);
stored
.
protocol
=
this
.
storage
.
get
(
keys
.
protocol
);
});
stored
.
thumbprint
=
this
.
storage
.
get
(
keys
.
thumbprint
);
utils
.
each
(
firstChars
,
function
(
i
,
firstChar
)
{
var
list
=
that
.
adjacencyList
[
firstChar
];
if
(
!
list
)
{
return
false
;
}
lists
.
push
(
list
);
if
(
!
shortestList
||
list
.
length
<
shortestList
.
length
)
{
shortestList
=
list
;
}
});
if
(
lists
.
length
<
firstChars
.
length
)
{
return
[];
}
}
utils
.
each
(
shortestList
,
function
(
i
,
id
)
{
isExpired
=
stored
.
thumbprint
!==
thumbprint
||
stored
.
protocol
!==
location
.
protocol
;
var
item
=
that
.
itemHash
[
id
],
isCandidate
,
isMatch
;
return
stored
.
data
&&
!
isExpired
?
stored
.
data
:
null
;
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
;
});
});
isMatch
&&
suggestions
.
push
(
item
);
});
return
suggestions
;
},
},
initialize
:
function
()
{
initialize
:
function
initialize
()
{
var
deferred
;
var
that
=
this
,
deferred
;
this
.
local
&&
this
.
_processLocalData
(
this
.
local
);
deferred
=
this
.
prefetch
?
this
.
_loadPrefetch
(
this
.
prefetch
)
:
$
.
Deferred
().
resolve
();
this
.
local
&&
deferred
.
done
(
addLocalToIndex
);
this
.
transport
=
this
.
remote
?
new
Transport
(
this
.
remote
)
:
null
;
this
.
transport
=
this
.
remote
?
new
Transport
(
this
.
remote
)
:
null
;
deferred
=
this
.
prefetch
?
this
.
_loadPrefetchData
(
this
.
prefetch
)
:
$
.
Deferred
().
resolve
();
this
.
initialize
=
function
initialize
()
{
this
.
local
=
this
.
prefetch
=
this
.
remote
=
null
;
return
deferred
.
promise
();
this
.
initialize
=
function
()
{
return
deferred
;
};
};
return
deferred
;
return
deferred
.
promise
();
function
addLocalToIndex
()
{
that
.
add
(
that
.
local
);
}
},
},
getSuggestions
:
function
(
query
,
cb
)
{
add
:
function
add
(
data
)
{
var
that
=
this
,
terms
,
suggestions
,
cacheHit
=
false
;
this
.
index
.
add
(
data
);
if
(
query
.
length
<
this
.
minLength
)
{
},
return
;
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
);
}
}
terms
=
utils
.
tokenizeQuery
(
query
);
!
cacheHit
&&
cb
&&
cb
(
matches
);
suggestions
=
this
.
_getLocalSuggestions
(
terms
).
slice
(
0
,
this
.
limit
);
function
returnRemoteMatches
(
remoteMatches
)
{
if
(
suggestions
.
length
<
this
.
limit
&&
this
.
transport
)
{
var
matchesWithBackfill
=
matches
.
slice
(
0
);
cacheHit
=
this
.
transport
.
get
(
query
,
processRemoteData
);
_
.
each
(
remoteMatches
,
function
(
remoteMatch
)
{
}
var
isDuplicate
;
!
cacheHit
&&
cb
&&
cb
(
suggestions
);
isDuplicate
=
_
.
some
(
matchesWithBackfill
,
function
(
match
)
{
function
processRemoteData
(
data
)
{
return
that
.
dupDetector
(
remoteMatch
,
match
);
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
;
});
});
!
isDuplicate
&&
suggestions
.
push
(
item
);
!
isDuplicate
&&
matchesWithBackfill
.
push
(
remoteMatch
);
return
suggestions
.
length
<
that
.
limit
;
return
matchesWithBackfill
.
length
<
that
.
limit
;
});
});
cb
&&
cb
(
suggestions
);
cb
&&
cb
(
matchesWithBackfill
.
sort
(
that
.
sorter
)
);
}
}
},
ttAdapter
:
function
ttAdapter
()
{
return
_
.
bind
(
this
.
get
,
this
);
}
}
});
});
return
Dataset
;
return
Bloodhound
;
function
compileTemplate
(
template
,
engine
,
valueKey
)
{
function
noSort
()
{
var
renderFn
,
compiledTemplate
;
return
0
;
if
(
utils
.
isFunction
(
template
))
{
}
renderFn
=
template
;
function
ignoreDuplicates
()
{
}
else
if
(
utils
.
isString
(
template
))
{
return
false
;
compiledTemplate
=
engine
.
compile
(
template
);
}
renderFn
=
utils
.
bind
(
compiledTemplate
.
render
,
compiledTemplate
);
}();
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>'
};
var
css
=
{
wrapper
:
{
position
:
"relative"
,
display
:
"inline-block"
},
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"
}
};
if
(
_
.
isMsie
())
{
_
.
mixin
(
css
.
input
,
{
backgroundImage
:
"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"
});
}
if
(
_
.
isMsie
()
&&
_
.
isMsie
()
<=
7
)
{
_
.
mixin
(
css
.
input
,
{
marginTop
:
"-1px"
});
}
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
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
{
}
else
{
renderFn
=
function
(
context
)
{
nextTickFn
=
function
nextTickSetTimeout
(
fn
)
{
return
"<p>"
+
context
[
valueKey
]
+
"</p>"
;
setTimeout
(
function
()
{
fn
();
},
0
);
};
};
}
}
return
render
Fn
;
return
nextTick
Fn
;
}
}
}();
function
bindContext
(
fn
,
context
)
{
var
InputView
=
function
()
{
return
fn
.
bind
?
fn
.
bind
(
context
)
:
function
()
{
function
InputView
(
o
)
{
fn
.
apply
(
context
,
[].
slice
.
call
(
arguments
,
0
));
var
that
=
this
;
utils
.
bindAll
(
this
);
this
.
specialKeyCodeMap
=
{
9
:
"tab"
,
27
:
"esc"
,
37
:
"left"
,
39
:
"right"
,
13
:
"enter"
,
38
:
"up"
,
40
:
"down"
};
};
}
}();
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"
,
39
:
"right"
,
13
:
"enter"
,
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
.
$hint
=
$
(
o
.
hint
);
this
.
$input
=
$
(
o
.
input
).
on
(
"blur.tt"
,
this
.
_handleBlur
).
on
(
"focus.tt"
,
this
.
_handleFocus
).
on
(
"keydown.tt"
,
this
.
_handleSpecialKeyEvent
);
this
.
$input
=
$
(
o
.
input
).
on
(
"blur.tt"
,
onBlur
).
on
(
"focus.tt"
,
onFocus
).
on
(
"keydown.tt"
,
onKeydown
);
if
(
!
utils
.
isMsie
())
{
if
(
this
.
$hint
.
length
===
0
)
{
this
.
$input
.
on
(
"input.tt"
,
this
.
_compareQueryToInputValue
);
this
.
setHintValue
=
this
.
getHintValue
=
this
.
clearHint
=
_
.
noop
;
}
if
(
!
_
.
isMsie
())
{
this
.
$input
.
on
(
"input.tt"
,
onInput
);
}
else
{
}
else
{
this
.
$input
.
on
(
"keydown.tt keypress.tt cut.tt paste.tt"
,
function
(
$e
)
{
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
;
return
;
}
}
utils
.
defer
(
that
.
_compareQueryToInputValue
);
_
.
defer
(
_
.
bind
(
that
.
_onInput
,
that
,
$e
)
);
});
});
}
}
this
.
query
=
this
.
$input
.
val
();
this
.
query
=
this
.
$input
.
val
();
this
.
$overflowHelper
=
buildOverflowHelper
(
this
.
$input
);
this
.
$overflowHelper
=
buildOverflowHelper
(
this
.
$input
);
}
}
utils
.
mixin
(
InputView
.
prototype
,
EventTarget
,
{
Input
.
normalizeQuery
=
function
(
str
)
{
_handleFocus
:
function
()
{
return
(
str
||
""
).
replace
(
/^
\s
*/g
,
""
).
replace
(
/
\s{2,}
/g
,
" "
);
this
.
trigger
(
"focused"
);
};
_
.
mixin
(
Input
.
prototype
,
EventEmitter
,
{
_onBlur
:
function
onBlur
(
$e
)
{
this
.
resetInputValue
();
this
.
trigger
(
"blurred"
);
},
},
_
handleBlur
:
function
(
)
{
_
onFocus
:
function
onFocus
(
$e
)
{
this
.
trigger
(
"
blur
ed"
);
this
.
trigger
(
"
focus
ed"
);
},
},
_handleSpecialKeyEvent
:
function
(
$e
)
{
_onKeydown
:
function
onKeydown
(
$e
)
{
var
keyName
=
this
.
specialKeyCodeMap
[
$e
.
which
||
$e
.
keyCode
];
var
keyName
=
specialKeyCodeMap
[
$e
.
which
||
$e
.
keyCode
];
keyName
&&
this
.
trigger
(
keyName
+
"Keyed"
,
$e
);
this
.
_managePreventDefault
(
keyName
,
$e
);
if
(
keyName
&&
this
.
_shouldTrigger
(
keyName
,
$e
))
{
this
.
trigger
(
keyName
+
"Keyed"
,
$e
);
}
},
},
_compareQueryToInputValue
:
function
()
{
_onInput
:
function
onInput
(
$e
)
{
var
inputValue
=
this
.
getInputValue
(),
isSameQuery
=
compareQueries
(
this
.
query
,
inputValue
),
isSameQueryExceptWhitespace
=
isSameQuery
?
this
.
query
.
length
!==
inputValue
.
length
:
false
;
this
.
_checkInputValue
();
if
(
isSameQueryExceptWhitespace
)
{
},
this
.
trigger
(
"whitespaceChanged"
,
{
_managePreventDefault
:
function
managePreventDefault
(
keyName
,
$e
)
{
value
:
this
.
query
var
preventDefault
,
hintValue
,
inputValue
;
});
switch
(
keyName
)
{
}
else
if
(
!
isSameQuery
)
{
case
"tab"
:
this
.
trigger
(
"queryChanged"
,
{
hintValue
=
this
.
getHintValue
();
value
:
this
.
query
=
inputValue
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
()
{
_shouldTrigger
:
function
shouldTrigger
(
keyName
,
$e
)
{
this
.
$hint
.
off
(
".tt"
);
var
trigger
;
this
.
$input
.
off
(
".tt"
);
switch
(
keyName
)
{
this
.
$hint
=
this
.
$input
=
this
.
$overflowHelper
=
null
;
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
();
this
.
$input
.
focus
();
},
},
blur
:
function
()
{
blur
:
function
blur
()
{
this
.
$input
.
blur
();
this
.
$input
.
blur
();
},
},
getQuery
:
function
()
{
getQuery
:
function
getQuery
()
{
return
this
.
query
;
return
this
.
query
;
},
},
setQuery
:
function
(
query
)
{
setQuery
:
function
setQuery
(
query
)
{
this
.
query
=
query
;
this
.
query
=
query
;
},
},
getInputValue
:
function
()
{
getInputValue
:
function
getInputValue
()
{
return
this
.
$input
.
val
();
return
this
.
$input
.
val
();
},
},
setInputValue
:
function
(
value
,
silent
)
{
setInputValue
:
function
setInputValue
(
value
,
silent
)
{
this
.
$input
.
val
(
value
);
this
.
$input
.
val
(
value
);
!
silent
&&
this
.
_c
ompareQueryTo
InputValue
();
!
silent
&&
this
.
_c
heck
InputValue
();
},
},
getHintValue
:
function
()
{
getHintValue
:
function
getHintValue
()
{
return
this
.
$hint
.
val
();
return
this
.
$hint
.
val
();
},
},
setHintValue
:
function
(
value
)
{
setHintValue
:
function
setHintValue
(
value
)
{
this
.
$hint
.
val
(
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
();
return
(
this
.
$input
.
css
(
"direction"
)
||
"ltr"
).
toLowerCase
();
},
},
isOverflow
:
function
()
{
hasOverflow
:
function
hasOverflow
()
{
var
constraint
=
this
.
$input
.
width
()
-
2
;
this
.
$overflowHelper
.
text
(
this
.
getInputValue
());
this
.
$overflowHelper
.
text
(
this
.
getInputValue
());
return
this
.
$overflowHelper
.
width
()
>
this
.
$input
.
width
()
;
return
this
.
$overflowHelper
.
width
()
>
=
constraint
;
},
},
isCursorAtEnd
:
function
()
{
isCursorAtEnd
:
function
()
{
var
valueLength
=
this
.
$input
.
val
().
length
,
selectionStart
=
this
.
$input
[
0
].
selectionStart
,
range
;
var
valueLength
,
selectionStart
,
range
;
if
(
utils
.
isNumber
(
selectionStart
))
{
valueLength
=
this
.
$input
.
val
().
length
;
selectionStart
=
this
.
$input
[
0
].
selectionStart
;
if
(
_
.
isNumber
(
selectionStart
))
{
return
selectionStart
===
valueLength
;
return
selectionStart
===
valueLength
;
}
else
if
(
document
.
selection
)
{
}
else
if
(
document
.
selection
)
{
range
=
document
.
selection
.
createRange
();
range
=
document
.
selection
.
createRange
();
...
@@ -658,13 +1010,17 @@
...
@@ -658,13 +1010,17 @@
return
valueLength
===
range
.
text
.
length
;
return
valueLength
===
range
.
text
.
length
;
}
}
return
true
;
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
)
{
function
buildOverflowHelper
(
$input
)
{
return
$
(
"<span></span>"
).
css
({
return
$
(
'<pre aria-hidden="true"></pre>'
).
css
({
position
:
"absolute"
,
position
:
"absolute"
,
left
:
"-9999px"
,
visibility
:
"hidden"
,
visibility
:
"hidden"
,
whiteSpace
:
"nowrap"
,
whiteSpace
:
"nowrap"
,
fontFamily
:
$input
.
css
(
"font-family"
),
fontFamily
:
$input
.
css
(
"font-family"
),
...
@@ -679,452 +1035,597 @@
...
@@ -679,452 +1035,597 @@
textTransform
:
$input
.
css
(
"text-transform"
)
textTransform
:
$input
.
css
(
"text-transform"
)
}).
insertAfter
(
$input
);
}).
insertAfter
(
$input
);
}
}
function
compareQueries
(
a
,
b
)
{
function
areQueriesEquivalent
(
a
,
b
)
{
a
=
(
a
||
""
).
replace
(
/^
\s
*/g
,
""
).
replace
(
/
\s{2,}
/g
,
" "
);
return
Input
.
normalizeQuery
(
a
)
===
Input
.
normalizeQuery
(
b
);
b
=
(
b
||
""
).
replace
(
/^
\s
*/g
,
""
).
replace
(
/
\s{2,}
/g
,
" "
);
}
return
a
===
b
;
function
withModifier
(
$e
)
{
return
$e
.
altKey
||
$e
.
ctrlKey
||
$e
.
metaKey
||
$e
.
shiftKey
;
}
}
}();
}();
var
DropdownView
=
function
()
{
var
Dataset
=
function
()
{
var
html
=
{
var
datasetKey
=
"ttDataset"
,
valueKey
=
"ttValue"
,
datumKey
=
"ttDatum"
;
suggestionsList
:
'<span class="tt-suggestions"></span>'
function
Dataset
(
o
)
{
},
css
=
{
o
=
o
||
{};
suggestionsList
:
{
o
.
templates
=
o
.
templates
||
{};
display
:
"block"
if
(
!
o
.
source
)
{
},
$
.
error
(
"missing source"
);
suggestion
:
{
whiteSpace
:
"nowrap"
,
cursor
:
"pointer"
},
suggestionChild
:
{
whiteSpace
:
"normal"
}
}
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
);
};
};
function
DropdownView
(
o
)
{
Dataset
.
extractDatum
=
function
extractDatum
(
el
)
{
utils
.
bindAll
(
this
);
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
});
}
},
getRoot
:
function
getRoot
()
{
return
this
.
$el
;
},
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
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
.
isOpen
=
false
;
this
.
isEmpty
=
true
;
this
.
isEmpty
=
true
;
this
.
isMouseOverDropdown
=
false
;
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
,
{
_
.
mixin
(
Dropdown
.
prototype
,
EventEmitter
,
{
_
handleMouseenter
:
function
(
)
{
_
onMouseEnter
:
function
onMouseEnter
(
$e
)
{
this
.
isMouseOverDropdown
=
true
;
this
.
isMouseOverDropdown
=
true
;
},
},
_
handleMouseleave
:
function
(
)
{
_
onMouseLeave
:
function
onMouseLeave
(
$e
)
{
this
.
isMouseOverDropdown
=
false
;
this
.
isMouseOverDropdown
=
false
;
},
},
_handleMouseover
:
function
(
$e
)
{
_onSuggestionClick
:
function
onSuggestionClick
(
$e
)
{
var
$suggestion
=
$
(
$e
.
currentTarget
);
this
.
trigger
(
"suggestionClicked"
,
$
(
$e
.
currentTarget
));
this
.
_getSuggestions
().
removeClass
(
"tt-is-under-cursor"
);
$suggestion
.
addClass
(
"tt-is-under-cursor"
);
},
},
_
handleSelection
:
function
(
$e
)
{
_
onSuggestionMouseEnter
:
function
onSuggestionMouseEnter
(
$e
)
{
var
$suggestion
=
$
(
$e
.
currentTarget
);
this
.
_removeCursor
(
);
this
.
trigger
(
"suggestionSelected"
,
extractSuggestion
(
$suggestion
)
);
this
.
_setCursor
(
$
(
$e
.
currentTarget
),
true
);
},
},
_show
:
function
()
{
_onSuggestionMouseLeave
:
function
onSuggestionMouseLeave
(
$e
)
{
this
.
$menu
.
css
(
"display"
,
"block"
);
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
()
{
_hide
:
function
()
{
this
.
$menu
.
hide
();
this
.
$menu
.
hide
();
},
},
_moveCursor
:
function
(
increment
)
{
_show
:
function
()
{
var
$suggestions
,
$cur
,
nextIndex
,
$underCursor
;
this
.
$menu
.
css
(
"display"
,
"block"
);
if
(
!
this
.
isVisible
())
{
},
_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
;
return
;
}
}
$oldCursor
=
this
.
_getCursor
();
$suggestions
=
this
.
_getSuggestions
();
$suggestions
=
this
.
_getSuggestions
();
$cur
=
$suggestions
.
filter
(
".tt-is-under-cursor"
);
this
.
_removeCursor
();
$cur
.
removeClass
(
"tt-is-under-cursor"
);
newCursorIndex
=
$suggestions
.
index
(
$oldCursor
)
+
increment
;
nextIndex
=
$suggestions
.
index
(
$cur
)
+
increment
;
newCursorIndex
=
(
newCursorIndex
+
1
)
%
(
$suggestions
.
length
+
1
)
-
1
;
nextIndex
=
(
nextIndex
+
1
)
%
(
$suggestions
.
length
+
1
)
-
1
;
if
(
newCursorIndex
===
-
1
)
{
if
(
nextIndex
===
-
1
)
{
this
.
trigger
(
"cursorRemoved"
);
this
.
trigger
(
"cursorRemoved"
);
return
;
return
;
}
else
if
(
ne
xt
Index
<
-
1
)
{
}
else
if
(
ne
wCursor
Index
<
-
1
)
{
ne
xt
Index
=
$suggestions
.
length
-
1
;
ne
wCursor
Index
=
$suggestions
.
length
-
1
;
}
}
$underCursor
=
$suggestions
.
eq
(
nextIndex
).
addClass
(
"tt-is-under-cursor"
);
this
.
_setCursor
(
$newCursor
=
$suggestions
.
eq
(
newCursorIndex
)
);
this
.
_ensureVisib
ility
(
$under
Cursor
);
this
.
_ensureVisib
le
(
$new
Cursor
);
this
.
trigger
(
"cursorMoved"
,
extractSuggestion
(
$underCursor
));
},
},
_ensureVisible
:
function
ensureVisible
(
$el
)
{
_getSuggestions
:
function
()
{
var
elTop
,
elBottom
,
menuScrollTop
,
menuHeight
;
return
this
.
$menu
.
find
(
".tt-suggestions > .tt-suggestion"
)
;
elTop
=
$el
.
position
().
top
;
},
elBottom
=
elTop
+
$el
.
outerHeight
(
true
);
_ensureVisibility
:
function
(
$el
)
{
menuScrollTop
=
this
.
$menu
.
scrollTop
();
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
);
menuHeight
=
this
.
$menu
.
height
()
+
parseInt
(
this
.
$menu
.
css
(
"paddingTop"
),
10
)
+
parseInt
(
this
.
$menu
.
css
(
"paddingBottom"
),
10
);
if
(
elTop
<
0
)
{
if
(
elTop
<
0
)
{
this
.
$menu
.
scrollTop
(
menuScrollTop
+
elTop
);
this
.
$menu
.
scrollTop
(
menuScrollTop
+
elTop
);
}
else
if
(
menuHeight
<
elBottom
)
{
}
else
if
(
menuHeight
<
elBottom
)
{
this
.
$menu
.
scrollTop
(
menuScrollTop
+
(
elBottom
-
menuHeight
));
this
.
$menu
.
scrollTop
(
menuScrollTop
+
(
elBottom
-
menuHeight
));
}
}
},
},
destroy
:
function
()
{
close
:
function
close
()
{
this
.
$menu
.
off
(
".tt"
);
this
.
$menu
=
null
;
},
isVisible
:
function
()
{
return
this
.
isOpen
&&
!
this
.
isEmpty
;
},
closeUnlessMouseIsOverDropdown
:
function
()
{
if
(
!
this
.
isMouseOverDropdown
)
{
this
.
close
();
}
},
close
:
function
()
{
if
(
this
.
isOpen
)
{
if
(
this
.
isOpen
)
{
this
.
isOpen
=
false
;
this
.
isOpen
=
this
.
isMouseOverDropdown
=
false
;
this
.
isMouseOverDropdown
=
false
;
this
.
_removeCursor
()
;
this
.
_hide
();
this
.
_hide
();
this
.
$menu
.
find
(
".tt-suggestions > .tt-suggestion"
).
removeClass
(
"tt-is-under-cursor"
);
this
.
trigger
(
"closed"
);
this
.
trigger
(
"closed"
);
}
}
},
},
open
:
function
()
{
open
:
function
open
()
{
if
(
!
this
.
isOpen
)
{
if
(
!
this
.
isOpen
)
{
this
.
isOpen
=
true
;
this
.
isOpen
=
true
;
!
this
.
isEmpty
&&
this
.
_show
();
!
this
.
isEmpty
&&
this
.
_show
();
this
.
trigger
(
"opened"
);
this
.
trigger
(
"opened"
);
}
}
},
},
setLanguageDirection
:
function
(
dir
)
{
setLanguageDirection
:
function
setLanguageDirection
(
dir
)
{
var
ltrCss
=
{
this
.
$menu
.
css
(
dir
===
"ltr"
?
css
.
ltr
:
css
.
rtl
);
left
:
"0"
,
right
:
"auto"
},
rtlCss
=
{
left
:
"auto"
,
right
:
" 0"
};
dir
===
"ltr"
?
this
.
$menu
.
css
(
ltrCss
)
:
this
.
$menu
.
css
(
rtlCss
);
},
},
moveCursorUp
:
function
()
{
moveCursorUp
:
function
moveCursorUp
()
{
this
.
_moveCursor
(
-
1
);
this
.
_moveCursor
(
-
1
);
},
},
moveCursorDown
:
function
()
{
moveCursorDown
:
function
moveCursorDown
()
{
this
.
_moveCursor
(
+
1
);
this
.
_moveCursor
(
+
1
);
},
},
getSuggestionUnderCursor
:
function
()
{
getDatumForSuggestion
:
function
getDatumForSuggestion
(
$el
)
{
var
$suggestion
=
this
.
_getSuggestions
().
filter
(
".tt-is-under-cursor"
).
first
();
var
datum
=
null
;
return
$suggestion
.
length
>
0
?
extractSuggestion
(
$suggestion
)
:
null
;
if
(
$el
.
length
)
{
},
datum
=
{
getFirstSuggestion
:
function
()
{
raw
:
Dataset
.
extractDatum
(
$el
),
var
$suggestion
=
this
.
_getSuggestions
().
first
();
value
:
Dataset
.
extractValue
(
$el
),
return
$suggestion
.
length
>
0
?
extractSuggestion
(
$suggestion
)
:
null
;
datasetName
:
Dataset
.
extractDatasetName
(
$el
)
},
};
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
);
}
}
this
.
trigger
(
"suggestionsRendered"
)
;
return
datum
;
},
},
clearSuggestions
:
function
(
datasetName
)
{
getDatumForCursor
:
function
getDatumForCursor
()
{
var
$datasets
=
datasetName
?
this
.
$menu
.
find
(
".tt-dataset-"
+
datasetName
)
:
this
.
$menu
.
find
(
'[class^="tt-dataset-"]'
),
$suggestions
=
$datasets
.
find
(
".tt-suggestions"
);
return
this
.
getDatumForSuggestion
(
this
.
_getCursor
().
first
());
$datasets
.
hide
();
},
$suggestions
.
empty
();
getDatumForTopSuggestion
:
function
getDatumForTopSuggestion
()
{
if
(
this
.
_getSuggestions
().
length
===
0
)
{
return
this
.
getDatumForSuggestion
(
this
.
_getSuggestions
().
first
());
this
.
isEmpty
=
true
;
},
this
.
_hide
();
update
:
function
update
(
query
)
{
_
.
each
(
this
.
datasets
,
updateDataset
);
function
updateDataset
(
dataset
)
{
dataset
.
update
(
query
);
}
},
empty
:
function
empty
()
{
_
.
each
(
this
.
datasets
,
clearDataset
);
function
clearDataset
(
dataset
)
{
dataset
.
clear
();
}
},
isVisible
:
function
isVisible
()
{
return
this
.
isOpen
&&
!
this
.
isEmpty
;
},
destroy
:
function
destroy
()
{
this
.
$menu
.
off
(
".tt"
);
this
.
$menu
=
null
;
_
.
each
(
this
.
datasets
,
destroyDataset
);
function
destroyDataset
(
dataset
)
{
dataset
.
destroy
();
}
}
}
}
});
});
return
Dropdown
View
;
return
Dropdown
;
function
extractSuggestion
(
$el
)
{
function
initializeDataset
(
oDataset
)
{
return
$el
.
data
(
"suggestion"
);
return
new
Dataset
(
oDataset
);
}
}
}();
}();
var
TypeaheadView
=
function
()
{
var
Typeahead
=
function
()
{
var
html
=
{
var
attrsKey
=
"ttAttrs"
;
wrapper
:
'<span class="twitter-typeahead"></span>'
,
function
Typeahead
(
o
)
{
hint
:
'<input class="tt-hint" type="text" autocomplete="off" spellcheck="off" disabled>'
,
var
$menu
,
$input
,
$hint
,
datasets
;
dropdown
:
'<span class="tt-dropdown-menu"></span>'
o
=
o
||
{};
},
css
=
{
if
(
!
o
.
input
)
{
wrapper
:
{
$
.
error
(
"missing input"
);
position
:
"relative"
,
display
:
"inline-block"
},
hint
:
{
position
:
"absolute"
,
top
:
"0"
,
left
:
"0"
,
borderColor
:
"transparent"
,
boxShadow
:
"none"
},
query
:
{
position
:
"relative"
,
verticalAlign
:
"top"
,
backgroundColor
:
"transparent"
},
dropdown
:
{
position
:
"absolute"
,
top
:
"100%"
,
left
:
"0"
,
zIndex
:
"100"
,
display
:
"none"
}
}
};
this
.
autoselect
=
!!
o
.
autoselect
;
if
(
utils
.
isMsie
())
{
this
.
minLength
=
_
.
isNumber
(
o
.
minLength
)
?
o
.
minLength
:
1
;
utils
.
mixin
(
css
.
query
,
{
this
.
$node
=
buildDomStructure
(
o
.
input
,
o
.
withHint
);
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"
});
}
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
;
$menu
=
this
.
$node
.
find
(
".tt-dropdown-menu"
);
$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"
);
$hint
=
this
.
$node
.
find
(
".tt-hint"
);
this
.
dropdownView
=
new
DropdownView
({
this
.
eventBus
=
o
.
eventBus
||
new
EventBus
({
menu
:
$menu
el
:
$input
}).
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
.
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
,
input
:
$input
,
hint
:
$hint
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
);
}).
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
;
};
}
$e
.
preventDefault
();
});
}
}
utils
.
mixin
(
TypeaheadView
.
prototype
,
EventTarget
,
{
_
.
mixin
(
Typeahead
.
prototype
,
{
_managePreventDefault
:
function
(
e
)
{
_onSuggestionClicked
:
function
onSuggestionClicked
(
type
,
$el
)
{
var
$e
=
e
.
data
,
hint
,
inputValue
,
preventDefault
=
false
;
var
datum
;
switch
(
e
.
type
)
{
if
(
datum
=
this
.
dropdown
.
getDatumForSuggestion
(
$el
))
{
case
"tabKeyed"
:
this
.
_select
(
datum
);
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
;
}
}
preventDefault
&&
$e
.
preventDefault
();
},
},
_setLanguageDirection
:
function
()
{
_onCursorMoved
:
function
onCursorMoved
()
{
var
dir
=
this
.
inputView
.
getLanguageDirection
();
var
datum
=
this
.
dropdown
.
getDatumForCursor
();
if
(
dir
!==
this
.
dir
)
{
this
.
input
.
clearHint
();
this
.
dir
=
dir
;
this
.
input
.
setInputValue
(
datum
.
value
,
true
);
this
.
$node
.
css
(
"direction"
,
dir
);
this
.
eventBus
.
trigger
(
"cursorchanged"
,
datum
.
raw
,
datum
.
datasetName
);
this
.
dropdownView
.
setLanguageDirection
(
dir
);
},
_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
();
}
}
},
},
_updateHint
:
function
()
{
_onTabKeyed
:
function
onTabKeyed
(
type
,
$e
)
{
var
suggestion
=
this
.
dropdownView
.
getFirstSuggestion
(),
hint
=
suggestion
?
suggestion
.
value
:
null
,
dropdownIsVisible
=
this
.
dropdownView
.
isVisible
(),
inputHasOverflow
=
this
.
inputView
.
isOverflow
(),
inputValue
,
query
,
escapedQuery
,
beginsWithQuery
,
match
;
var
datum
;
if
(
hint
&&
dropdownIsVisible
&&
!
inputHasOverflow
)
{
if
(
datum
=
this
.
dropdown
.
getDatumForCursor
())
{
inputValue
=
this
.
inputView
.
getInputValue
();
this
.
_select
(
datum
);
query
=
inputValue
.
replace
(
/
\s{2,}
/g
,
" "
).
replace
(
/^
\s
+/g
,
""
);
$e
.
preventDefault
();
escapedQuery
=
utils
.
escapeRegExChars
(
query
);
}
else
{
beginsWithQuery
=
new
RegExp
(
"^(?:"
+
escapedQuery
+
")(.*$)"
,
"i"
);
this
.
_autocomplete
();
match
=
beginsWithQuery
.
exec
(
hint
);
this
.
inputView
.
setHintValue
(
inputValue
+
(
match
?
match
[
1
]
:
""
));
}
}
},
},
_clearHint
:
function
()
{
_onEscKeyed
:
function
onEscKeyed
()
{
this
.
inputView
.
setHintValue
(
""
);
this
.
dropdown
.
close
();
this
.
input
.
resetInputValue
();
},
},
_clearSuggestions
:
function
()
{
_onUpKeyed
:
function
onUpKeyed
()
{
this
.
dropdownView
.
clearSuggestions
();
var
query
=
this
.
input
.
getQuery
();
if
(
!
this
.
dropdown
.
isOpen
&&
query
.
length
>=
this
.
minLength
)
{
this
.
dropdown
.
update
(
query
);
}
this
.
dropdown
.
open
();
this
.
dropdown
.
moveCursorUp
();
},
},
_setInputValueToQuery
:
function
()
{
_onDownKeyed
:
function
onDownKeyed
()
{
this
.
inputView
.
setInputValue
(
this
.
inputView
.
getQuery
());
var
query
=
this
.
input
.
getQuery
();
if
(
!
this
.
dropdown
.
isOpen
&&
query
.
length
>=
this
.
minLength
)
{
this
.
dropdown
.
update
(
query
);
}
this
.
dropdown
.
open
();
this
.
dropdown
.
moveCursorDown
();
},
},
_setInputValueToSuggestionUnderCursor
:
function
(
e
)
{
_onLeftKeyed
:
function
onLeftKeyed
()
{
var
suggestion
=
e
.
data
;
this
.
dir
===
"rtl"
&&
this
.
_autocomplete
();
this
.
inputView
.
setInputValue
(
suggestion
.
value
,
true
);
},
},
_o
penDropdown
:
function
()
{
_o
nRightKeyed
:
function
onRightKeyed
()
{
this
.
d
ropdownView
.
open
();
this
.
d
ir
===
"ltr"
&&
this
.
_autocomplete
();
},
},
_closeDropdown
:
function
(
e
)
{
_onQueryChanged
:
function
onQueryChanged
(
e
,
query
)
{
this
.
dropdownView
[
e
.
type
===
"blured"
?
"closeUnlessMouseIsOverDropdown"
:
"close"
]();
this
.
input
.
clearHint
();
this
.
dropdown
.
empty
();
query
.
length
>=
this
.
minLength
&&
this
.
dropdown
.
update
(
query
);
this
.
dropdown
.
open
();
this
.
_setLanguageDirection
();
},
},
_moveDropdownCursor
:
function
(
e
)
{
_onWhitespaceChanged
:
function
onWhitespaceChanged
()
{
var
$e
=
e
.
data
;
this
.
_updateHint
();
if
(
!
$e
.
shiftKey
&&
!
$e
.
ctrlKey
&&
!
$e
.
metaKey
)
{
this
.
dropdown
.
open
();
this
.
dropdownView
[
e
.
type
===
"upKeyed"
?
"moveCursorUp"
:
"moveCursorDown"
]();
}
},
},
_handleSelection
:
function
(
e
)
{
_setLanguageDirection
:
function
setLanguageDirection
()
{
var
byClick
=
e
.
type
===
"suggestionSelected"
,
suggestion
=
byClick
?
e
.
data
:
this
.
dropdownView
.
getSuggestionUnderCursor
();
var
dir
;
if
(
suggestion
)
{
if
(
this
.
dir
!==
(
dir
=
this
.
input
.
getLanguageDirection
()))
{
this
.
inputView
.
setInputValue
(
suggestion
.
value
);
this
.
dir
=
dir
;
byClick
?
this
.
inputView
.
focus
()
:
e
.
data
.
preventDefault
();
this
.
$node
.
css
(
"direction"
,
dir
);
byClick
&&
utils
.
isMsie
()
?
utils
.
defer
(
this
.
dropdownView
.
close
)
:
this
.
dropdownView
.
close
();
this
.
dropdown
.
setLanguageDirection
(
dir
);
this
.
eventBus
.
trigger
(
"selected"
,
suggestion
.
datum
,
suggestion
.
dataset
);
}
}
},
},
_getSuggestions
:
function
()
{
_updateHint
:
function
updateHint
()
{
var
that
=
this
,
query
=
this
.
inputView
.
getQuery
();
var
datum
,
inputValue
,
query
,
escapedQuery
,
frontMatchRegEx
,
match
;
if
(
utils
.
isBlankString
(
query
))
{
datum
=
this
.
dropdown
.
getDatumForTopSuggestion
();
return
;
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
]
:
""
));
}
}
utils
.
each
(
this
.
datasets
,
function
(
i
,
dataset
)
{
dataset
.
getSuggestions
(
query
,
function
(
suggestions
)
{
if
(
query
===
that
.
inputView
.
getQuery
())
{
that
.
dropdownView
.
renderSuggestions
(
dataset
,
suggestions
);
}
});
});
},
},
_autocomplete
:
function
(
e
)
{
_autocomplete
:
function
autocomplete
()
{
var
isCursorAtEnd
,
ignoreEvent
,
query
,
hint
,
suggestion
;
var
hint
,
query
,
datum
;
if
(
e
.
type
===
"rightKeyed"
||
e
.
type
===
"leftKeyed"
)
{
hint
=
this
.
input
.
getHintValue
();
isCursorAtEnd
=
this
.
inputView
.
isCursorAtEnd
();
query
=
this
.
input
.
getQuery
();
ignoreEvent
=
this
.
inputView
.
getLanguageDirection
()
===
"ltr"
?
e
.
type
===
"leftKeyed"
:
e
.
type
===
"rightKeyed"
;
if
(
hint
&&
query
!==
hint
&&
this
.
input
.
isCursorAtEnd
())
{
if
(
!
isCursorAtEnd
||
ignoreEvent
)
{
datum
=
this
.
dropdown
.
getDatumForTopSuggestion
();
return
;
datum
&&
this
.
input
.
setInputValue
(
datum
.
value
);
}
this
.
eventBus
.
trigger
(
"autocompleted"
,
datum
.
raw
,
datum
.
datasetName
);
}
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
);
}
}
},
},
_propagateEvent
:
function
(
e
)
{
_select
:
function
select
(
datum
)
{
this
.
eventBus
.
trigger
(
e
.
type
);
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
);
},
},
destroy
:
function
()
{
open
:
function
open
()
{
this
.
inputView
.
destroy
();
this
.
dropdown
.
open
();
this
.
dropdownView
.
destroy
();
},
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
);
destroyDomStructure
(
this
.
$node
);
this
.
$node
=
null
;
this
.
$node
=
null
;
},
setQuery
:
function
(
query
)
{
this
.
inputView
.
setQuery
(
query
);
this
.
inputView
.
setInputValue
(
query
);
this
.
_clearHint
();
this
.
_clearSuggestions
();
this
.
_getSuggestions
();
}
}
});
});
return
TypeaheadView
;
return
Typeahead
;
function
buildDomStructure
(
input
)
{
function
buildDomStructure
(
input
,
withHint
)
{
var
$wrapper
=
$
(
html
.
wrapper
),
$dropdown
=
$
(
html
.
dropdown
),
$input
=
$
(
input
),
$hint
=
$
(
html
.
hint
);
var
$input
,
$wrapper
,
$dropdown
,
$hint
;
$wrapper
=
$wrapper
.
css
(
css
.
wrapper
);
$input
=
$
(
input
);
$dropdown
=
$dropdown
.
css
(
css
.
dropdown
);
$wrapper
=
$
(
html
.
wrapper
).
css
(
css
.
wrapper
);
$hint
.
css
(
css
.
hint
).
css
({
$dropdown
=
$
(
html
.
dropdown
).
css
(
css
.
dropdown
);
backgroundAttachment
:
$input
.
css
(
"background-attachment"
),
$hint
=
$input
.
clone
().
css
(
css
.
hint
).
css
(
getBackgroundStyles
(
$input
));
backgroundClip
:
$input
.
css
(
"background-clip"
),
$hint
.
removeData
().
addClass
(
"tt-hint"
).
removeAttr
(
"id name placeholder"
).
prop
(
"disabled"
,
true
).
attr
({
backgroundColor
:
$input
.
css
(
"background-color"
),
autocomplete
:
"off"
,
backgroundImage
:
$input
.
css
(
"background-image"
),
spellcheck
:
"false"
backgroundOrigin
:
$input
.
css
(
"background-origin"
),
backgroundPosition
:
$input
.
css
(
"background-position"
),
backgroundRepeat
:
$input
.
css
(
"background-repeat"
),
backgroundSize
:
$input
.
css
(
"background-size"
)
});
});
$input
.
data
(
"ttAttrs"
,
{
$input
.
data
(
attrsKey
,
{
dir
:
$input
.
attr
(
"dir"
),
dir
:
$input
.
attr
(
"dir"
),
autocomplete
:
$input
.
attr
(
"autocomplete"
),
autocomplete
:
$input
.
attr
(
"autocomplete"
),
spellcheck
:
$input
.
attr
(
"spellcheck"
),
spellcheck
:
$input
.
attr
(
"spellcheck"
),
style
:
$input
.
attr
(
"style"
)
style
:
$input
.
attr
(
"style"
)
});
});
$input
.
addClass
(
"tt-
query
"
).
attr
({
$input
.
addClass
(
"tt-
input
"
).
attr
({
autocomplete
:
"off"
,
autocomplete
:
"off"
,
spellcheck
:
false
spellcheck
:
false
}).
css
(
css
.
query
);
}).
css
(
withHint
?
css
.
input
:
css
.
inputWithNoHint
);
try
{
try
{
!
$input
.
attr
(
"dir"
)
&&
$input
.
attr
(
"dir"
,
"auto"
);
!
$input
.
attr
(
"dir"
)
&&
$input
.
attr
(
"dir"
,
"auto"
);
}
catch
(
e
)
{}
}
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
)
{
function
destroyDomStructure
(
$node
)
{
var
$input
=
$node
.
find
(
".tt-
query
"
);
var
$input
=
$node
.
find
(
".tt-
input
"
);
utils
.
each
(
$input
.
data
(
"ttAttrs"
),
function
(
key
,
val
)
{
_
.
each
(
$input
.
data
(
attrsKey
),
function
(
val
,
key
)
{
utils
.
isUndefined
(
val
)
?
$input
.
removeAttr
(
key
)
:
$input
.
attr
(
key
,
val
);
_
.
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
();
$node
.
remove
();
}
}
}();
}();
(
function
()
{
(
function
()
{
var
cache
=
{},
viewKey
=
"ttView"
,
methods
;
var
typeaheadKey
,
methods
;
typeaheadKey
=
"ttTypeahead"
;
methods
=
{
methods
=
{
initialize
:
function
(
datasetDefs
)
{
initialize
:
function
initialize
(
o
)
{
var
datasets
;
var
datasets
=
[].
slice
.
call
(
arguments
,
1
);
datasetDefs
=
utils
.
isArray
(
datasetDefs
)
?
datasetDefs
:
[
datasetDefs
];
o
=
o
||
{};
if
(
datasetDefs
.
length
===
0
)
{
return
this
.
each
(
attach
);
$
.
error
(
"no datasets provided"
);
function
attach
()
{
}
var
$input
=
$
(
this
),
eventBus
,
typeahead
;
datasets
=
utils
.
map
(
datasetDefs
,
function
(
o
)
{
_
.
each
(
datasets
,
function
(
d
)
{
var
dataset
=
cache
[
o
.
name
]
?
cache
[
o
.
name
]
:
new
Dataset
(
o
);
d
.
highlight
=
!!
o
.
highlight
;
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
();
});
});
$input
.
data
(
viewKey
,
new
TypeaheadView
({
typeahead
=
new
Typeahead
({
input
:
$input
,
input
:
$input
,
eventBus
:
eventBus
=
new
EventBus
({
eventBus
:
eventBus
=
new
EventBus
({
el
:
$input
el
:
$input
}),
}),
withHint
:
_
.
isUndefined
(
o
.
hint
)
?
true
:
!!
o
.
hint
,
minLength
:
o
.
minLength
,
autoselect
:
o
.
autoselect
,
datasets
:
datasets
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
()
{
close
:
function
close
()
{
return
this
.
each
(
destroy
);
return
this
.
each
(
closeTypeahead
);
function
destroy
()
{
function
closeTypeahead
()
{
var
$this
=
$
(
this
),
view
=
$this
.
data
(
viewKey
);
var
$input
=
$
(
this
),
typeahead
;
if
(
view
)
{
if
(
typeahead
=
$input
.
data
(
typeaheadKey
))
{
view
.
destroy
();
typeahead
.
close
();
$this
.
removeData
(
viewKey
);
}
}
}
}
},
},
setQuery
:
function
(
query
)
{
val
:
function
val
(
newVal
)
{
return
this
.
each
(
setQuery
);
return
_
.
isString
(
newVal
)
?
this
.
each
(
setQuery
)
:
this
.
map
(
getQuery
).
get
(
);
function
setQuery
()
{
function
setQuery
()
{
var
view
=
$
(
this
).
data
(
viewKey
);
var
$input
=
$
(
this
),
typeahead
;
view
&&
view
.
setQuery
(
query
);
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