Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
M
Moecube Store
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Moecube Store
Commits
89ea4df0
Commit
89ea4df0
authored
Jan 26, 2013
by
神楽坂玲奈
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
server filter
parent
7d606ddb
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1169 additions
and
101 deletions
+1169
-101
assets/javascripts/rooms.coffee
assets/javascripts/rooms.coffee
+53
-22
assets/javascripts/rooms.js
assets/javascripts/rooms.js
+79
-45
assets/stylesheets/rooms.less
assets/stylesheets/rooms.less
+10
-0
rooms/index.html
rooms/index.html
+28
-34
vendor/javascripts/jquery.multiselect.min.js
vendor/javascripts/jquery.multiselect.min.js
+725
-0
vendor/stylesheets/jquery.multiselect.css
vendor/stylesheets/jquery.multiselect.css
+23
-0
vendor/stylesheets/mod_websocket.lua
vendor/stylesheets/mod_websocket.lua
+251
-0
No files found.
assets/javascripts/rooms.coffee
View file @
89ea4df0
...
...
@@ -3,6 +3,38 @@ class Server extends Spine.Model
@
extend
Spine
.
Model
.
Ajax
@
url
:
"/servers.json"
class
Servers
extends
Spine
.
Controller
constructor
:
->
super
Server
.
bind
"refresh"
,
@
render
Server
.
one
"refresh"
,
@
connect
render
:
=>
@
html
$
(
'#server_template'
).
tmpl
Server
.
all
()
@
el
.
multiselect
(
noneSelectedText
:
'房间筛选'
selectedText
:
'房间筛选'
header
:
false
minWidth
:
'auto'
classes
:
'server_filter'
).
bind
"multiselectclick"
,
(
event
,
ui
)
->
Room
.
trigger
'refresh'
connect
:
=>
wsServer
=
'ws://mycard-server.my-card.in:9998'
websocket
=
new
WebSocket
(
wsServer
);
websocket
.
onopen
=
->
console
.
log
(
"websocket: Connected to WebSocket server."
)
websocket
.
onclose
=
->
console
.
log
(
"websocket: Disconnected"
);
websocket
.
onmessage
=
(
evt
)
->
#console.log('Retrieved data from server: ' + evt.data)
rooms
=
JSON
.
parse
(
evt
.
data
)
for
room
in
rooms
if
room
.
_deleted
Room
.
find
(
room
.
id
).
destroy
()
if
Room
.
exists
(
room
.
id
)
Room
.
refresh
(
room
for
room
in
rooms
when
!
room
.
_deleted
)
websocket
.
onerror
=
(
evt
)
->
console
.
log
(
'websocket: Error occured: '
+
evt
.
data
);
class
Room
extends
Spine
.
Model
@
configure
"Room"
,
"name"
,
"status"
,
"private"
,
"rule"
,
"mode"
,
"start_lp"
@
belongsTo
'server'
,
Server
...
...
@@ -14,7 +46,11 @@ class Rooms extends Spine.Controller
super
Room
.
bind
"refresh"
,
@
render
render
:
=>
@
html
$
(
'#room_template'
).
tmpl
_
.
sortBy
Room
.
all
(),
@
sort
@
html
$
(
'#room_template'
).
tmpl
_
.
sortBy
(
_
.
filter
(
Room
.
all
(),
@
filter
),
@
sort
)
filter
:
(
room
)
->
_
.
find
$
(
'#servers'
).
multiselect
(
'getChecked'
),
(
e
)
->
parseInt
(
e
.
value
)
==
room
.
server_id
sort
:
(
room
)
->
[
if
room
.
status
==
"wait"
then
0
else
1
,
...
...
@@ -30,6 +66,17 @@ class Rooms extends Spine.Controller
mycard
.
join
(
room
.
server
().
ip
,
room
.
server
().
port
,
mycard
.
room_name
(
room
.
name
,
null
,
room
.
pvp
,
room
.
rule
,
room
.
mode
,
room
.
start_lp
))
login
=
(
username
,
password
)
->
#Candy.Core.Event.Jabber.Presence = (msg)->
# Candy.Core.log('[Jabber] Presence');
# msg = $(msg);
# if(msg.children('x[xmlns^="' + Strophe.NS.MUC + '"]').length > 0)
# if (msg.attr('type') == 'error')
# self.Jabber.Room.PresenceError(msg);
# else
# self.Jabber.Room.Presence(msg);
# else
# alert msg
# true
Candy
.
init
(
'http://s70.hebexpo.com:5280/http-bind/'
,
core
:
debug
:
false
,
...
...
@@ -116,10 +163,10 @@ $(document).ready ->
#$('#login_domain').combobox()
$
(
'#login_dialog'
).
dialog
autoOpen
:
false
,
resizable
:
false
,
title
:
"用户登录"
#
$('#login_dialog').dialog
#
autoOpen:false,
#
resizable:false,
#
title:"用户登录"
$
(
'#login_button'
).
click
->
login
()
...
...
@@ -132,23 +179,7 @@ $(document).ready ->
# false
rooms
=
new
Rooms
(
el
:
$
(
'#rooms'
))
Server
.
one
"refresh"
,
->
wsServer
=
'ws://mycard-server.my-card.in:9998'
websocket
=
new
WebSocket
(
wsServer
);
websocket
.
onopen
=
->
console
.
log
(
"Connected to WebSocket server."
)
websocket
.
onclose
=
->
console
.
log
(
"Disconnected"
);
websocket
.
onmessage
=
(
evt
)
->
console
.
log
(
'Retrieved data from server: '
+
evt
.
data
)
rooms
=
JSON
.
parse
(
evt
.
data
)
for
room
in
rooms
if
room
.
_deleted
Room
.
find
(
room
.
id
).
destroy
()
if
Room
.
exists
(
room
.
id
)
Room
.
refresh
(
room
for
room
in
rooms
when
!
room
.
_deleted
)
websocket
.
onerror
=
(
evt
)
->
console
.
log
(
'Error occured: '
+
evt
.
data
);
servers
=
new
Servers
(
el
:
$
(
'#servers'
))
Server
.
fetch
()
assets/javascripts/rooms.js
View file @
89ea4df0
// Generated by CoffeeScript 1.4.0
(
function
()
{
var
Room
,
Rooms
,
Server
,
login
,
var
Room
,
Rooms
,
Server
,
Servers
,
login
,
__hasProp
=
{}.
hasOwnProperty
,
__extends
=
function
(
child
,
parent
)
{
for
(
var
key
in
parent
)
{
if
(
__hasProp
.
call
(
parent
,
key
))
child
[
key
]
=
parent
[
key
];
}
function
ctor
()
{
this
.
constructor
=
child
;
}
ctor
.
prototype
=
parent
.
prototype
;
child
.
prototype
=
new
ctor
();
child
.
__super__
=
parent
.
prototype
;
return
child
;
},
__bind
=
function
(
fn
,
me
){
return
function
(){
return
fn
.
apply
(
me
,
arguments
);
};
};
...
...
@@ -23,6 +23,74 @@
})(
Spine
.
Model
);
Servers
=
(
function
(
_super
)
{
__extends
(
Servers
,
_super
);
function
Servers
()
{
this
.
connect
=
__bind
(
this
.
connect
,
this
);
this
.
render
=
__bind
(
this
.
render
,
this
);
Servers
.
__super__
.
constructor
.
apply
(
this
,
arguments
);
Server
.
bind
(
"
refresh
"
,
this
.
render
);
Server
.
one
(
"
refresh
"
,
this
.
connect
);
}
Servers
.
prototype
.
render
=
function
()
{
this
.
html
(
$
(
'
#server_template
'
).
tmpl
(
Server
.
all
()));
return
this
.
el
.
multiselect
({
noneSelectedText
:
'
房间筛选
'
,
selectedText
:
'
房间筛选
'
,
header
:
false
,
minWidth
:
'
auto
'
,
classes
:
'
server_filter
'
}).
bind
(
"
multiselectclick
"
,
function
(
event
,
ui
)
{
return
Room
.
trigger
(
'
refresh
'
);
});
};
Servers
.
prototype
.
connect
=
function
()
{
var
websocket
,
wsServer
;
wsServer
=
'
ws://mycard-server.my-card.in:9998
'
;
websocket
=
new
WebSocket
(
wsServer
);
websocket
.
onopen
=
function
()
{
return
console
.
log
(
"
websocket: Connected to WebSocket server.
"
);
};
websocket
.
onclose
=
function
()
{
return
console
.
log
(
"
websocket: Disconnected
"
);
};
websocket
.
onmessage
=
function
(
evt
)
{
var
room
,
rooms
,
_i
,
_len
;
rooms
=
JSON
.
parse
(
evt
.
data
);
for
(
_i
=
0
,
_len
=
rooms
.
length
;
_i
<
_len
;
_i
++
)
{
room
=
rooms
[
_i
];
if
(
room
.
_deleted
)
{
if
(
Room
.
exists
(
room
.
id
))
{
Room
.
find
(
room
.
id
).
destroy
();
}
}
}
return
Room
.
refresh
((
function
()
{
var
_j
,
_len1
,
_results
;
_results
=
[];
for
(
_j
=
0
,
_len1
=
rooms
.
length
;
_j
<
_len1
;
_j
++
)
{
room
=
rooms
[
_j
];
if
(
!
room
.
_deleted
)
{
_results
.
push
(
room
);
}
}
return
_results
;
})());
};
return
websocket
.
onerror
=
function
(
evt
)
{
return
console
.
log
(
'
websocket: Error occured:
'
+
evt
.
data
);
};
};
return
Servers
;
})(
Spine
.
Controller
);
Room
=
(
function
(
_super
)
{
__extends
(
Room
,
_super
);
...
...
@@ -54,7 +122,13 @@
}
Rooms
.
prototype
.
render
=
function
()
{
return
this
.
html
(
$
(
'
#room_template
'
).
tmpl
(
_
.
sortBy
(
Room
.
all
(),
this
.
sort
)));
return
this
.
html
(
$
(
'
#room_template
'
).
tmpl
(
_
.
sortBy
(
_
.
filter
(
Room
.
all
(),
this
.
filter
),
this
.
sort
)));
};
Rooms
.
prototype
.
filter
=
function
(
room
)
{
return
_
.
find
(
$
(
'
#servers
'
).
multiselect
(
'
getChecked
'
),
function
(
e
)
{
return
parseInt
(
e
.
value
)
===
room
.
server_id
;
});
};
Rooms
.
prototype
.
sort
=
function
(
room
)
{
...
...
@@ -113,7 +187,7 @@
};
$
(
document
).
ready
(
function
()
{
var
jid
,
new_room
,
rooms
;
var
jid
,
new_room
,
rooms
,
servers
;
if
(
jid
=
Candy
.
Util
.
getCookie
(
'
jid
'
))
{
login
(
jid
,
Candy
.
Util
.
getCookie
(
'
password
'
));
}
...
...
@@ -179,54 +253,14 @@
new_room
.
name
.
value
=
Math
.
floor
(
Math
.
random
()
*
1000
);
return
$
(
'
#new_room_dialog
'
).
dialog
(
'
open
'
);
});
$
(
'
#login_dialog
'
).
dialog
({
autoOpen
:
false
,
resizable
:
false
,
title
:
"
用户登录
"
});
$
(
'
#login_button
'
).
click
(
function
()
{
return
login
();
});
rooms
=
new
Rooms
({
el
:
$
(
'
#rooms
'
)
});
Server
.
one
(
"
refresh
"
,
function
()
{
var
websocket
,
wsServer
;
wsServer
=
'
ws://mycard-server.my-card.in:9998
'
;
websocket
=
new
WebSocket
(
wsServer
);
websocket
.
onopen
=
function
()
{
return
console
.
log
(
"
Connected to WebSocket server.
"
);
};
websocket
.
onclose
=
function
()
{
return
console
.
log
(
"
Disconnected
"
);
};
websocket
.
onmessage
=
function
(
evt
)
{
var
room
,
_i
,
_len
;
console
.
log
(
'
Retrieved data from server:
'
+
evt
.
data
);
rooms
=
JSON
.
parse
(
evt
.
data
);
for
(
_i
=
0
,
_len
=
rooms
.
length
;
_i
<
_len
;
_i
++
)
{
room
=
rooms
[
_i
];
if
(
room
.
_deleted
)
{
if
(
Room
.
exists
(
room
.
id
))
{
Room
.
find
(
room
.
id
).
destroy
();
}
}
}
return
Room
.
refresh
((
function
()
{
var
_j
,
_len1
,
_results
;
_results
=
[];
for
(
_j
=
0
,
_len1
=
rooms
.
length
;
_j
<
_len1
;
_j
++
)
{
room
=
rooms
[
_j
];
if
(
!
room
.
_deleted
)
{
_results
.
push
(
room
);
}
}
return
_results
;
})());
};
return
websocket
.
onerror
=
function
(
evt
)
{
return
console
.
log
(
'
Error occured:
'
+
evt
.
data
);
};
servers
=
new
Servers
({
el
:
$
(
'
#servers
'
)
});
return
Server
.
fetch
();
});
...
...
assets/stylesheets/rooms.less
View file @
89ea4df0
...
...
@@ -78,3 +78,13 @@ body{background:#fbfbfb;}
width: 74px;
}
}
.server_filter label{
font-size: 12px;
}
.ui-widget.server_filter{
font-size: 100%;
.ui-multiselect-checkboxes{
overflow-y: visible
}
}
\ No newline at end of file
rooms/index.html
View file @
89ea4df0
...
...
@@ -42,6 +42,23 @@
})();
</script>
<script
id=
"login_form_template"
type=
"text/x-mustache-template"
>
<
form
method
=
"
post
"
id
=
"
login-form
"
class
=
"
login-form
"
onsubmit
=
"
{{#displayUsername}}username.value = node.value + '@' + domain.value;{{/displayUsername}}Candy.Util.setCookie('jid', username.value, 365);Candy.Util.setCookie('username', node.value, 365);Candy.Util.setCookie('password', password.value, 365)
"
>
{{
#
displayUsername
}}
<
label
for
=
"
node
"
>
{{
_labelUsername
}}
<
/label
>
<
input
type
=
"
text
"
id
=
"
node
"
name
=
"
node
"
/>
<
label
for
=
"
domain
"
>
@
<
/label
>
<
select
id
=
"
domain
"
name
=
"
domain
"
tabindex
=
"
-1
"
>
<
option
label
=
"
my-card.in
"
value
=
"
my-card.in
"
selected
=
"
selected
"
/>
<
option
label
=
"
gmail.com
"
value
=
"
gmail.com
"
/>
<
/select
>
{{
/
displayUsername
}}
<
input
type
=
"
hidden
"
id
=
"
username
"
name
=
"
username
"
value
=
"
{{presetJid}}
"
/>
{{
#
displayPassword
}}
<
label
for
=
"
password
"
>
{{
_labelPassword
}}
<
/label><input type="password" id="password" name="password" /
>
{{
/
displayPassword
}}
<
input
type
=
"
submit
"
class
=
"
button
"
value
=
"
{{_loginSubmit}}
"
/>
<
/form
>
</script>
<script
id=
"room_template"
type=
"text/x-jquery-tmpl"
>
<
div
class
=
"
room
"
id
=
"
room_${id}
"
>
...
...
@@ -59,24 +76,11 @@
<
/div
>
<
/div
>
</script>
<script
id=
"server_template"
type=
"text/x-jquery-tmpl"
>
<
option
value
=
"
${id}
"
selected
=
"
selected
"
>
$
{
name
}
<
/option
>
</script>
<script
id=
"login_form_template"
type=
"text/x-mustache-template"
>
<
form
method
=
"
post
"
id
=
"
login-form
"
class
=
"
login-form
"
onsubmit
=
"
{{#displayUsername}}username.value = node.value + '@' + domain.value;{{/displayUsername}}Candy.Util.setCookie('jid', username.value, 365);Candy.Util.setCookie('username', node.value, 365);Candy.Util.setCookie('password', password.value, 365)
"
>
{{
#
displayUsername
}}
<
label
for
=
"
node
"
>
{{
_labelUsername
}}
<
/label
>
<
input
type
=
"
text
"
id
=
"
node
"
name
=
"
node
"
/>
<
label
for
=
"
domain
"
>
@
<
/label
>
<
select
id
=
"
domain
"
name
=
"
domain
"
tabindex
=
"
-1
"
>
<
option
label
=
"
my-card.in
"
value
=
"
my-card.in
"
selected
=
"
selected
"
/>
<
option
label
=
"
gmail.com
"
value
=
"
gmail.com
"
/>
<
/select
>
{{
/
displayUsername
}}
<
input
type
=
"
hidden
"
id
=
"
username
"
name
=
"
username
"
value
=
"
{{presetJid}}
"
/>
{{
#
displayPassword
}}
<
label
for
=
"
password
"
>
{{
_labelPassword
}}
<
/label><input type="password" id="password" name="password" /
>
{{
/
displayPassword
}}
<
input
type
=
"
submit
"
class
=
"
button
"
value
=
"
{{_loginSubmit}}
"
/>
<
/form
>
</script>
</head>
<body>
<!--[if lt IE 7]>
...
...
@@ -91,11 +95,7 @@
<img
src=
"/assets/images/logo.png"
alt=
""
/>
</div>
<div
class=
"card_form"
>
<select
name=
""
id=
""
>
<option
value=
""
>
选择分区
</option>
<option
value=
""
></option>
<option
value=
""
></option>
</select>
<select
id=
"servers"
multiple=
"multiple"
></select>
<input
id=
"new_room_button"
type=
"button"
value=
"建立房间"
/>
</div>
<div
class=
"log_reg"
>
...
...
@@ -107,16 +107,16 @@
<div
class=
"card_center"
>
<div
id=
"rooms"
></div>
<div
class=
"online_list"
>
<div
class=
"online_title"
>
在线用户
</div>
<div
class=
"online_title"
>
好友
</div>
<ul>
<li>
<
img
src=
"/assets/images/hs1.jpg"
alt=
""
/><span>
Yolua Mesiade
</span>
<
span>
这是开发中好友系统喵
</span>
</li>
<li>
<
img
src=
"/assets/images/hs1.jpg"
alt=
""
/><span>
Yolua Mesiade
</span>
<
span>
看起来比较麻烦样子于是先坑几天
</span>
</li>
<li>
<
img
src=
"/assets/images/hs1.jpg"
alt=
""
/><span>
Yolua Mesiade
</span>
<
span>
下面留的是管理员
</span>
</li>
<li>
<img
src=
"/assets/images/hs1.jpg"
alt=
""
/><span>
Yolua Mesiade
</span>
...
...
@@ -227,7 +227,9 @@
<script
src=
"/vendor/javascripts/jquery-ui-1.10.0.custom.min.js"
></script>
<script
src=
"/vendor/javascripts/jquery.cookie.js"
></script>
<script
src=
"/vendor/javascripts/jquery.select.js"
></script>
<script
src=
"/assets/javascripts/jquery-ui.cambobox.js"
></script>
<script
src=
"/vendor/javascripts/jquery.multiselect.min.js"
></script>
<link
rel=
"stylesheet"
type=
"text/css"
href=
"/vendor/stylesheets/jquery.multiselect.css"
/>
<script
src=
"/vendor/javascripts/spine/spine.js"
></script>
<script
src=
"/vendor/javascripts/spine/ajax.js"
></script>
...
...
@@ -247,13 +249,5 @@
<link
rel=
"stylesheet"
type=
"text/css"
href=
"/vendor/candy/plugins/inline-images/candy.css"
/>
<script
src=
"/assets/javascripts/rooms.js"
></script>
<!-- Google Analytics: change UA-XXXXX-X to be your site's ID. -->
<!--<script>
var _gaq=[['_setAccount','UA-XXXXX-X'],['_trackPageview']];
(function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];
g.src=('https:'==location.protocol?'//ssl':'//www')+'.google-analytics.com/ga.js';
s.parentNode.insertBefore(g,s)}(document,'script'));
</script>-->
</body>
</html>
vendor/javascripts/jquery.multiselect.min.js
0 → 100644
View file @
89ea4df0
/*
* jQuery MultiSelect UI Widget 1.13
* Copyright (c) 2012 Eric Hynds
*
* http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
*
* Depends:
* - jQuery 1.4.2+
* - jQuery UI 1.8 widget factory
*
* Optional:
* - jQuery UI effects
* - jQuery UI position utility
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
//(function(d){var k=0;d.widget("ech.multiselect",{options:{header:!0,height:175,minWidth:225,classes:"",checkAllText:"Check all",uncheckAllText:"Uncheck all",noneSelectedText:"Select options",selectedText:"# selected",selectedList:0,show:null,hide:null,autoOpen:!1,multiple:!0,position:{}},_create:function(){var a=this.element.hide(),b=this.options;this.speed=d.fx.speeds._default;this._isOpen=!1;a=(this.button=d('<button type="button"><span class="ui-icon ui-icon-triangle-2-n-s"></span></button>')).addClass("ui-multiselect ui-widget ui-state-default ui-corner-all").addClass(b.classes).attr({title:a.attr("title"),"aria-haspopup":!0,tabIndex:a.attr("tabIndex")}).insertAfter(a);(this.buttonlabel=d("<span />")).html(b.noneSelectedText).appendTo(a);var a=(this.menu=d("<div />")).addClass("ui-multiselect-menu ui-widget ui-widget-content ui-corner-all").addClass(b.classes).appendTo(document.body),c=(this.header=d("<div />")).addClass("ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix").appendTo(a);(this.headerLinkContainer=d("<ul />")).addClass("ui-helper-reset").html(function(){return!0===b.header?'<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>'+b.checkAllText+'</span></a></li><li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>'+b.uncheckAllText+"</span></a></li>":"string"===typeof b.header?"<li>"+b.header+"</li>":""}).append('<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon ui-icon-circle-close"></span></a></li>').appendTo(c);(this.checkboxContainer=d("<ul />")).addClass("ui-multiselect-checkboxes ui-helper-reset").appendTo(a);this._bindEvents();this.refresh(!0);b.multiple||a.addClass("ui-multiselect-single")},_init:function(){!1===this.options.header&&this.header.hide();this.options.multiple||this.headerLinkContainer.find(".ui-multiselect-all, .ui-multiselect-none").hide();this.options.autoOpen&&this.open();this.element.is(":disabled")&&this.disable()},refresh:function(a){var b=this.element,c=this.options,f=this.menu,h=this.checkboxContainer,g=[],e="",i=b.attr("id")||k++;b.find("option").each(function(b){d(this);var a=this.parentNode,f=this.innerHTML,h=this.title,k=this.value,b="ui-multiselect-"+(this.id||i+"-option-"+b),l=this.disabled,n=this.selected,m=["ui-corner-all"],o=(l?"ui-multiselect-disabled ":" ")+this.className,j;"OPTGROUP"===a.tagName&&(j=a.getAttribute("label"),-1===d.inArray(j,g)&&(e+='<li class="ui-multiselect-optgroup-label '+a.className+'"><a href="#">'+j+"</a></li>",g.push(j)));l&&m.push("ui-state-disabled");n&&!c.multiple&&m.push("ui-state-active");e+='<li class="'+o+'">';e+='<label for="'+b+'" title="'+h+'" class="'+m.join(" ")+'">';e+='<input id="'+b+'" name="multiselect_'+i+'" type="'+(c.multiple?"checkbox":"radio")+'" value="'+k+'" title="'+f+'"';n&&(e+=' checked="checked"',e+=' aria-selected="true"');l&&(e+=' disabled="disabled"',e+=' aria-disabled="true"');e+=" /><span>"+f+"</span></label></li>"});h.html(e);this.labels=f.find("label");this.inputs=this.labels.children("input");this._setButtonWidth();this._setMenuWidth();this.button[0].defaultValue=this.update();a||this._trigger("refresh")},update:function(){var a=this.options,b=this.inputs,c=b.filter(":checked"),f=c.length,a=0===f?a.noneSelectedText:d.isFunction(a.selectedText)?a.selectedText.call(this,f,b.length,c.get()):/\d/.test(a.selectedList)&&0<a.selectedList&&f<=a.selectedList?c.map(function(){return d(this).next().html()}).get().join(", "):a.selectedText.replace("#",f).replace("#",b.length);this.buttonlabel.html(a);return a},_bindEvents:function(){function a(){b[b._isOpen? "close":"open"]();return!1}var b=this,c=this.button;c.find("span").bind("click.multiselect",a);c.bind({click:a,keypress:function(a){switch(a.which){case 27:case 38:case 37:b.close();break;case 39:case 40:b.open()}},mouseenter:function(){c.hasClass("ui-state-disabled")||d(this).addClass("ui-state-hover")},mouseleave:function(){d(this).removeClass("ui-state-hover")},focus:function(){c.hasClass("ui-state-disabled")||d(this).addClass("ui-state-focus")},blur:function(){d(this).removeClass("ui-state-focus")}});this.header.delegate("a","click.multiselect",function(a){if(d(this).hasClass("ui-multiselect-close"))b.close();else b[d(this).hasClass("ui-multiselect-all")?"checkAll":"uncheckAll"]();a.preventDefault()});this.menu.delegate("li.ui-multiselect-optgroup-label a","click.multiselect",function(a){a.preventDefault();var c=d(this),g=c.parent().nextUntil("li.ui-multiselect-optgroup-label").find("input:visible:not(:disabled)"),e=g.get(),c=c.parent().text();!1!==b._trigger("beforeoptgrouptoggle",a,{inputs:e,label:c})&&(b._toggleChecked(g.filter(":checked").length!==g.length,g),b._trigger("optgrouptoggle",a,{inputs:e,label:c,checked:e[0].checked}))}).delegate("label","mouseenter.multiselect",function(){d(this).hasClass("ui-state-disabled")||(b.labels.removeClass("ui-state-hover"),d(this).addClass("ui-state-hover").find("input").focus())}).delegate("label","keydown.multiselect",function(a){a.preventDefault();switch(a.which){case 9:case 27:b.close();break;case 38:case 40:case 37:case 39:b._traverse(a.which,this);break;case 13:d(this).find("input")[0].click()}}).delegate('input[type="checkbox"], input[type="radio"]',"click.multiselect",function(a){var c=d(this),g=this.value,e=this.checked,i=b.element.find("option");this.disabled||!1===b._trigger("click",a,{value:g,text:this.title,checked:e})?a.preventDefault():(c.focus(),c.attr("aria-selected",e),i.each(function(){this.value===g?this.selected=e:b.options.multiple||(this.selected=!1)}),b.options.multiple||(b.labels.removeClass("ui-state-active"),c.closest("label").toggleClass("ui-state-active",e),b.close()),b.element.trigger("change"),setTimeout(d.proxy(b.update,b),10))});d(document).bind("mousedown.multiselect",function(a){b._isOpen&&(!d.contains(b.menu[0],a.target)&&!d.contains(b.button[0],a.target)&&a.target!==b.button[0])&&b.close()});d(this.element[0].form).bind("reset.multiselect",function(){setTimeout(d.proxy(b.refresh,b),10)})},_setButtonWidth:function(){var a=this.element.outerWidth(),b=this.options;/\d/.test(b.minWidth)&&a<b.minWidth&&(a=b.minWidth);this.button.width(a)},_setMenuWidth:function(){var a=this.menu,b=this.button.outerWidth()-parseInt(a.css("padding-left"),10)-parseInt(a.css("padding-right"),10)-parseInt(a.css("border-right-width"),10)-parseInt(a.css("border-left-width"),10);a.width(b||this.button.outerWidth())},_traverse:function(a,b){var c=d(b),f=38===a||37===a,c=c.parent()[f?"prevAll":"nextAll"]("li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)")[f?"last":"first"]();c.length?c.find("label").trigger("mouseover"):(c=this.menu.find("ul").last(),this.menu.find("label")[f? "last":"first"]().trigger("mouseover"),c.scrollTop(f?c.height():0))},_toggleState:function(a,b){return function(){this.disabled||(this[a]=b);b?this.setAttribute("aria-selected",!0):this.removeAttribute("aria-selected")}},_toggleChecked:function(a,b){var c=b&&b.length?b:this.inputs,f=this;c.each(this._toggleState("checked",a));c.eq(0).focus();this.update();var h=c.map(function(){return this.value}).get();this.element.find("option").each(function(){!this.disabled&&-1<d.inArray(this.value,h)&&f._toggleState("selected",a).call(this)});c.length&&this.element.trigger("change")},_toggleDisabled:function(a){this.button.attr({disabled:a,"aria-disabled":a})[a?"addClass":"removeClass"]("ui-state-disabled");var b=this.menu.find("input"),b=a?b.filter(":enabled").data("ech-multiselect-disabled",!0):b.filter(function(){return!0===d.data(this,"ech-multiselect-disabled")}).removeData("ech-multiselect-disabled");b.attr({disabled:a,"arial-disabled":a}).parent()[a?"addClass":"removeClass"]("ui-state-disabled");this.element.attr({disabled:a,"aria-disabled":a})},open:function(){var a=this.button,b=this.menu,c=this.speed,f=this.options,h=[];if(!(!1===this._trigger("beforeopen")||a.hasClass("ui-state-disabled")||this._isOpen)){var g=b.find("ul").last(),e=f.show,i=a.offset();d.isArray(f.show)&&(e=f.show[0],c=f.show[1]||this.speed);e&&(h=[e,c]);g.scrollTop(0).height(f.height);d.ui.position&&!d.isEmptyObject(f.position)?(f.position.of=f.position.of||a,b.show().position(f.position).hide()):b.css({top:i.top+a.outerHeight(),left:i.left});d.fn.show.apply(b,h);this.labels.eq(0).trigger("mouseover").trigger("mouseenter").find("input").trigger("focus");a.addClass("ui-state-active");this._isOpen=!0;this._trigger("open")}},close:function(){if(!1!==this._trigger("beforeclose")){var a=this.options,b=a.hide,c=this.speed,f=[];d.isArray(a.hide)&&(b=a.hide[0],c=a.hide[1]||this.speed);b&&(f=[b,c]);d.fn.hide.apply(this.menu,f);this.button.removeClass("ui-state-active").trigger("blur").trigger("mouseleave");this._isOpen=!1;this._trigger("close")}},enable:function(){this._toggleDisabled(!1)},disable:function(){this._toggleDisabled(!0)},checkAll:function(){this._toggleChecked(!0);this._trigger("checkAll")},uncheckAll:function(){this._toggleChecked(!1);this._trigger("uncheckAll")},getChecked:function(){return this.menu.find("input").filter(":checked")},destroy:function(){d.Widget.prototype.destroy.call(this);this.button.remove();this.menu.remove();this.element.show();return this},isOpen:function(){return this._isOpen},widget:function(){return this.menu},getButton:function(){return this.button},_setOption:function(a,b){var c=this.menu;switch(a){case "header":c.find("div.ui-multiselect-header")[b?"show":"hide"]();break;case "checkAllText":c.find("a.ui-multiselect-all span").eq(-1).text(b);break;case "uncheckAllText":c.find("a.ui-multiselect-none span").eq(-1).text(b);break;case "height":c.find("ul").last().height(parseInt(b,10));break;case "minWidth":this.options[a]=parseInt(b,10);this._setButtonWidth();this._setMenuWidth();break;case "selectedText":case "selectedList":case "noneSelectedText":this.options[a]=b;this.update();break;case "classes":c.add(this.button).removeClass(this.options.classes).addClass(b);break;case "multiple":c.toggleClass("ui-multiselect-single",!b),this.options.multiple=b,this.element[0].multiple=b,this.refresh()}d.Widget.prototype._setOption.apply(this,arguments)}})})(jQuery);
/* jshint forin:true, noarg:true, noempty:true, eqeqeq:true, boss:true, undef:true, curly:true, browser:true, jquery:true */
/*
* jQuery MultiSelect UI Widget 1.13
* Copyright (c) 2012 Eric Hynds
*
* http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/
*
* Depends:
* - jQuery 1.4.2+
* - jQuery UI 1.8 widget factory
*
* Optional:
* - jQuery UI effects
* - jQuery UI position utility
*
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
(
function
(
$
,
undefined
){
var
multiselectID
=
0
;
$
.
widget
(
"
ech.multiselect
"
,
{
// default options
options
:
{
header
:
true
,
height
:
175
,
minWidth
:
225
,
classes
:
''
,
checkAllText
:
'
Check all
'
,
uncheckAllText
:
'
Uncheck all
'
,
noneSelectedText
:
'
Select options
'
,
selectedText
:
'
# selected
'
,
selectedList
:
0
,
show
:
null
,
hide
:
null
,
autoOpen
:
false
,
multiple
:
true
,
position
:
{}
},
_create
:
function
(){
var
el
=
this
.
element
.
hide
(),
o
=
this
.
options
;
this
.
speed
=
$
.
fx
.
speeds
.
_default
;
// default speed for effects
this
.
_isOpen
=
false
;
// assume no
var
button
=
(
this
.
button
=
$
(
'
<button type="button"><span class="ui-icon ui-icon-triangle-2-n-s"></span></button>
'
))
.
addClass
(
'
ui-multiselect ui-widget ui-state-default ui-corner-all
'
)
.
addClass
(
o
.
classes
)
.
attr
({
'
title
'
:
el
.
attr
(
'
title
'
),
'
aria-haspopup
'
:
true
,
'
tabIndex
'
:
el
.
attr
(
'
tabIndex
'
)
})
.
insertAfter
(
el
),
buttonlabel
=
(
this
.
buttonlabel
=
$
(
'
<span />
'
))
.
html
(
o
.
noneSelectedText
)
.
appendTo
(
button
),
menu
=
(
this
.
menu
=
$
(
'
<div />
'
))
.
addClass
(
'
ui-multiselect-menu ui-widget ui-widget-content ui-corner-all
'
)
.
addClass
(
o
.
classes
)
.
appendTo
(
document
.
body
),
header
=
(
this
.
header
=
$
(
'
<div />
'
))
.
addClass
(
'
ui-widget-header ui-corner-all ui-multiselect-header ui-helper-clearfix
'
)
.
appendTo
(
menu
),
headerLinkContainer
=
(
this
.
headerLinkContainer
=
$
(
'
<ul />
'
))
.
addClass
(
'
ui-helper-reset
'
)
.
html
(
function
(){
if
(
o
.
header
===
true
){
return
'
<li><a class="ui-multiselect-all" href="#"><span class="ui-icon ui-icon-check"></span><span>
'
+
o
.
checkAllText
+
'
</span></a></li><li><a class="ui-multiselect-none" href="#"><span class="ui-icon ui-icon-closethick"></span><span>
'
+
o
.
uncheckAllText
+
'
</span></a></li>
'
;
}
else
if
(
typeof
o
.
header
===
"
string
"
){
return
'
<li>
'
+
o
.
header
+
'
</li>
'
;
}
else
{
return
''
;
}
})
.
append
(
'
<li class="ui-multiselect-close"><a href="#" class="ui-multiselect-close"><span class="ui-icon ui-icon-circle-close"></span></a></li>
'
)
.
appendTo
(
header
),
checkboxContainer
=
(
this
.
checkboxContainer
=
$
(
'
<ul />
'
))
.
addClass
(
'
ui-multiselect-checkboxes ui-helper-reset
'
)
.
appendTo
(
menu
);
// perform event bindings
this
.
_bindEvents
();
// build menu
this
.
refresh
(
true
);
// some addl. logic for single selects
if
(
!
o
.
multiple
){
menu
.
addClass
(
'
ui-multiselect-single
'
);
}
},
_init
:
function
(){
if
(
this
.
options
.
header
===
false
){
this
.
header
.
hide
();
}
if
(
!
this
.
options
.
multiple
){
this
.
headerLinkContainer
.
find
(
'
.ui-multiselect-all, .ui-multiselect-none
'
).
hide
();
}
if
(
this
.
options
.
autoOpen
){
this
.
open
();
}
if
(
this
.
element
.
is
(
'
:disabled
'
)
){
this
.
disable
();
}
},
refresh
:
function
(
init
){
var
el
=
this
.
element
,
o
=
this
.
options
,
menu
=
this
.
menu
,
checkboxContainer
=
this
.
checkboxContainer
,
optgroups
=
[],
html
=
""
,
id
=
el
.
attr
(
'
id
'
)
||
multiselectID
++
;
// unique ID for the label & option tags
// build items
el
.
find
(
'
option
'
).
each
(
function
(
i
){
var
$this
=
$
(
this
),
parent
=
this
.
parentNode
,
title
=
this
.
innerHTML
,
description
=
this
.
title
,
value
=
this
.
value
,
inputID
=
'
ui-multiselect-
'
+
(
this
.
id
||
id
+
'
-option-
'
+
i
),
isDisabled
=
this
.
disabled
,
isSelected
=
this
.
selected
,
labelClasses
=
[
'
ui-corner-all
'
],
liClasses
=
(
isDisabled
?
'
ui-multiselect-disabled
'
:
'
'
)
+
this
.
className
,
optLabel
;
// is this an optgroup?
if
(
parent
.
tagName
===
'
OPTGROUP
'
){
optLabel
=
parent
.
getAttribute
(
'
label
'
);
// has this optgroup been added already?
if
(
$
.
inArray
(
optLabel
,
optgroups
)
===
-
1
){
html
+=
'
<li class="ui-multiselect-optgroup-label
'
+
parent
.
className
+
'
"><a href="#">
'
+
optLabel
+
'
</a></li>
'
;
optgroups
.
push
(
optLabel
);
}
}
if
(
isDisabled
){
labelClasses
.
push
(
'
ui-state-disabled
'
);
}
// browsers automatically select the first option
// by default with single selects
if
(
isSelected
&&
!
o
.
multiple
){
labelClasses
.
push
(
'
ui-state-active
'
);
}
html
+=
'
<li class="
'
+
liClasses
+
'
">
'
;
// create the label
html
+=
'
<label for="
'
+
inputID
+
'
" title="
'
+
description
+
'
" class="
'
+
labelClasses
.
join
(
'
'
)
+
'
">
'
;
html
+=
'
<input id="
'
+
inputID
+
'
" name="multiselect_
'
+
id
+
'
" type="
'
+
(
o
.
multiple
?
"
checkbox
"
:
"
radio
"
)
+
'
" value="
'
+
value
+
'
" title="
'
+
title
+
'
"
'
;
// pre-selected?
if
(
isSelected
){
html
+=
'
checked="checked"
'
;
html
+=
'
aria-selected="true"
'
;
}
// disabled?
if
(
isDisabled
){
html
+=
'
disabled="disabled"
'
;
html
+=
'
aria-disabled="true"
'
;
}
// add the title and close everything off
html
+=
'
/><span>
'
+
title
+
'
</span></label></li>
'
;
});
// insert into the DOM
checkboxContainer
.
html
(
html
);
// cache some moar useful elements
this
.
labels
=
menu
.
find
(
'
label
'
);
this
.
inputs
=
this
.
labels
.
children
(
'
input
'
);
// set widths
this
.
_setButtonWidth
();
this
.
_setMenuWidth
();
// remember default value
this
.
button
[
0
].
defaultValue
=
this
.
update
();
// broadcast refresh event; useful for widgets
if
(
!
init
){
this
.
_trigger
(
'
refresh
'
);
}
},
// updates the button text. call refresh() to rebuild
update
:
function
(){
var
o
=
this
.
options
,
$inputs
=
this
.
inputs
,
$checked
=
$inputs
.
filter
(
'
:checked
'
),
numChecked
=
$checked
.
length
,
value
;
if
(
numChecked
===
0
){
value
=
o
.
noneSelectedText
;
}
else
{
if
(
$
.
isFunction
(
o
.
selectedText
)){
value
=
o
.
selectedText
.
call
(
this
,
numChecked
,
$inputs
.
length
,
$checked
.
get
());
}
else
if
(
/
\d
/
.
test
(
o
.
selectedList
)
&&
o
.
selectedList
>
0
&&
numChecked
<=
o
.
selectedList
){
value
=
$checked
.
map
(
function
(){
return
$
(
this
).
next
().
html
();
}).
get
().
join
(
'
,
'
);
}
else
{
value
=
o
.
selectedText
.
replace
(
'
#
'
,
numChecked
).
replace
(
'
#
'
,
$inputs
.
length
);
}
}
this
.
buttonlabel
.
html
(
value
);
return
value
;
},
// binds events
_bindEvents
:
function
(){
var
self
=
this
,
button
=
this
.
button
;
function
clickHandler
(){
self
[
self
.
_isOpen
?
'
close
'
:
'
open
'
]();
return
false
;
}
// webkit doesn't like it when you click on the span :(
button
.
find
(
'
span
'
)
.
bind
(
'
click.multiselect
'
,
clickHandler
);
// button events
button
.
bind
({
click
:
clickHandler
,
keypress
:
function
(
e
){
switch
(
e
.
which
){
case
27
:
// esc
case
38
:
// up
case
37
:
// left
self
.
close
();
break
;
case
39
:
// right
case
40
:
// down
self
.
open
();
break
;
}
},
mouseenter
:
function
(){
if
(
!
button
.
hasClass
(
'
ui-state-disabled
'
)
){
$
(
this
).
addClass
(
'
ui-state-hover
'
);
}
},
mouseleave
:
function
(){
$
(
this
).
removeClass
(
'
ui-state-hover
'
);
},
focus
:
function
(){
if
(
!
button
.
hasClass
(
'
ui-state-disabled
'
)
){
$
(
this
).
addClass
(
'
ui-state-focus
'
);
}
},
blur
:
function
(){
$
(
this
).
removeClass
(
'
ui-state-focus
'
);
}
});
// header links
this
.
header
.
delegate
(
'
a
'
,
'
click.multiselect
'
,
function
(
e
){
// close link
if
(
$
(
this
).
hasClass
(
'
ui-multiselect-close
'
)
){
self
.
close
();
// check all / uncheck all
}
else
{
self
[
$
(
this
).
hasClass
(
'
ui-multiselect-all
'
)
?
'
checkAll
'
:
'
uncheckAll
'
]();
}
e
.
preventDefault
();
});
// optgroup label toggle support
this
.
menu
.
delegate
(
'
li.ui-multiselect-optgroup-label a
'
,
'
click.multiselect
'
,
function
(
e
){
e
.
preventDefault
();
var
$this
=
$
(
this
),
$inputs
=
$this
.
parent
().
nextUntil
(
'
li.ui-multiselect-optgroup-label
'
).
find
(
'
input:visible:not(:disabled)
'
),
nodes
=
$inputs
.
get
(),
label
=
$this
.
parent
().
text
();
// trigger event and bail if the return is false
if
(
self
.
_trigger
(
'
beforeoptgrouptoggle
'
,
e
,
{
inputs
:
nodes
,
label
:
label
})
===
false
){
return
;
}
// toggle inputs
self
.
_toggleChecked
(
$inputs
.
filter
(
'
:checked
'
).
length
!==
$inputs
.
length
,
$inputs
);
self
.
_trigger
(
'
optgrouptoggle
'
,
e
,
{
inputs
:
nodes
,
label
:
label
,
checked
:
nodes
[
0
].
checked
});
})
.
delegate
(
'
label
'
,
'
mouseenter.multiselect
'
,
function
(){
if
(
!
$
(
this
).
hasClass
(
'
ui-state-disabled
'
)
){
self
.
labels
.
removeClass
(
'
ui-state-hover
'
);
$
(
this
).
addClass
(
'
ui-state-hover
'
).
find
(
'
input
'
).
focus
();
}
})
.
delegate
(
'
label
'
,
'
keydown.multiselect
'
,
function
(
e
){
e
.
preventDefault
();
switch
(
e
.
which
){
case
9
:
// tab
case
27
:
// esc
self
.
close
();
break
;
case
38
:
// up
case
40
:
// down
case
37
:
// left
case
39
:
// right
self
.
_traverse
(
e
.
which
,
this
);
break
;
case
13
:
// enter
$
(
this
).
find
(
'
input
'
)[
0
].
click
();
break
;
}
})
.
delegate
(
'
input[type="checkbox"], input[type="radio"]
'
,
'
click.multiselect
'
,
function
(
e
){
var
$this
=
$
(
this
),
val
=
this
.
value
,
checked
=
this
.
checked
,
tags
=
self
.
element
.
find
(
'
option
'
);
// bail if this input is disabled or the event is cancelled
if
(
this
.
disabled
||
self
.
_trigger
(
'
click
'
,
e
,
{
value
:
val
,
text
:
this
.
title
,
checked
:
checked
})
===
false
){
e
.
preventDefault
();
return
;
}
// make sure the input has focus. otherwise, the esc key
// won't close the menu after clicking an item.
$this
.
focus
();
// toggle aria state
$this
.
attr
(
'
aria-selected
'
,
checked
);
// change state on the original option tags
tags
.
each
(
function
(){
if
(
this
.
value
===
val
){
this
.
selected
=
checked
;
}
else
if
(
!
self
.
options
.
multiple
){
this
.
selected
=
false
;
}
});
// some additional single select-specific logic
if
(
!
self
.
options
.
multiple
){
self
.
labels
.
removeClass
(
'
ui-state-active
'
);
$this
.
closest
(
'
label
'
).
toggleClass
(
'
ui-state-active
'
,
checked
);
// close menu
self
.
close
();
}
// fire change on the select box
self
.
element
.
trigger
(
"
change
"
);
// setTimeout is to fix multiselect issue #14 and #47. caused by jQuery issue #3827
// http://bugs.jquery.com/ticket/3827
setTimeout
(
$
.
proxy
(
self
.
update
,
self
),
10
);
});
// close each widget when clicking on any other element/anywhere else on the page
$
(
document
).
bind
(
'
mousedown.multiselect
'
,
function
(
e
){
if
(
self
.
_isOpen
&&
!
$
.
contains
(
self
.
menu
[
0
],
e
.
target
)
&&
!
$
.
contains
(
self
.
button
[
0
],
e
.
target
)
&&
e
.
target
!==
self
.
button
[
0
]){
self
.
close
();
}
});
// deal with form resets. the problem here is that buttons aren't
// restored to their defaultValue prop on form reset, and the reset
// handler fires before the form is actually reset. delaying it a bit
// gives the form inputs time to clear.
$
(
this
.
element
[
0
].
form
).
bind
(
'
reset.multiselect
'
,
function
(){
setTimeout
(
$
.
proxy
(
self
.
refresh
,
self
),
10
);
});
},
// set button width
_setButtonWidth
:
function
(){
var
width
=
this
.
element
.
outerWidth
(),
o
=
this
.
options
;
if
(
/
\d
/
.
test
(
o
.
minWidth
)
&&
width
<
o
.
minWidth
){
width
=
o
.
minWidth
;
}
// set widths
this
.
button
.
width
(
width
);
},
// set menu width
_setMenuWidth
:
function
(){
var
m
=
this
.
menu
,
width
=
this
.
button
.
outerWidth
()
-
parseInt
(
m
.
css
(
'
padding-left
'
),
10
)
-
parseInt
(
m
.
css
(
'
padding-right
'
),
10
)
-
parseInt
(
m
.
css
(
'
border-right-width
'
),
10
)
-
parseInt
(
m
.
css
(
'
border-left-width
'
),
10
);
m
.
width
(
width
||
this
.
button
.
outerWidth
()
);
},
// move up or down within the menu
_traverse
:
function
(
which
,
start
){
var
$start
=
$
(
start
),
moveToLast
=
which
===
38
||
which
===
37
,
// select the first li that isn't an optgroup label / disabled
$next
=
$start
.
parent
()[
moveToLast
?
'
prevAll
'
:
'
nextAll
'
](
'
li:not(.ui-multiselect-disabled, .ui-multiselect-optgroup-label)
'
)[
moveToLast
?
'
last
'
:
'
first
'
]();
// if at the first/last element
if
(
!
$next
.
length
){
var
$container
=
this
.
menu
.
find
(
'
ul
'
).
last
();
// move to the first/last
this
.
menu
.
find
(
'
label
'
)[
moveToLast
?
'
last
'
:
'
first
'
]().
trigger
(
'
mouseover
'
);
// set scroll position
$container
.
scrollTop
(
moveToLast
?
$container
.
height
()
:
0
);
}
else
{
$next
.
find
(
'
label
'
).
trigger
(
'
mouseover
'
);
}
},
// This is an internal function to toggle the checked property and
// other related attributes of a checkbox.
//
// The context of this function should be a checkbox; do not proxy it.
_toggleState
:
function
(
prop
,
flag
){
return
function
(){
if
(
!
this
.
disabled
)
{
this
[
prop
]
=
flag
;
}
if
(
flag
){
this
.
setAttribute
(
'
aria-selected
'
,
true
);
}
else
{
this
.
removeAttribute
(
'
aria-selected
'
);
}
};
},
_toggleChecked
:
function
(
flag
,
group
){
var
$inputs
=
(
group
&&
group
.
length
)
?
group
:
this
.
inputs
,
self
=
this
;
// toggle state on inputs
$inputs
.
each
(
this
.
_toggleState
(
'
checked
'
,
flag
));
// give the first input focus
$inputs
.
eq
(
0
).
focus
();
// update button text
this
.
update
();
// gather an array of the values that actually changed
var
values
=
$inputs
.
map
(
function
(){
return
this
.
value
;
}).
get
();
// toggle state on original option tags
this
.
element
.
find
(
'
option
'
)
.
each
(
function
(){
if
(
!
this
.
disabled
&&
$
.
inArray
(
this
.
value
,
values
)
>
-
1
){
self
.
_toggleState
(
'
selected
'
,
flag
).
call
(
this
);
}
});
// trigger the change event on the select
if
(
$inputs
.
length
)
{
this
.
element
.
trigger
(
"
change
"
);
}
},
_toggleDisabled
:
function
(
flag
){
this
.
button
.
attr
({
'
disabled
'
:
flag
,
'
aria-disabled
'
:
flag
})[
flag
?
'
addClass
'
:
'
removeClass
'
](
'
ui-state-disabled
'
);
var
inputs
=
this
.
menu
.
find
(
'
input
'
);
var
key
=
"
ech-multiselect-disabled
"
;
if
(
flag
)
{
// remember which elements this widget disabled (not pre-disabled)
// elements, so that they can be restored if the widget is re-enabled.
inputs
=
inputs
.
filter
(
'
:enabled
'
)
.
data
(
key
,
true
)
}
else
{
inputs
=
inputs
.
filter
(
function
()
{
return
$
.
data
(
this
,
key
)
===
true
;
}).
removeData
(
key
);
}
inputs
.
attr
({
'
disabled
'
:
flag
,
'
arial-disabled
'
:
flag
})
.
parent
()[
flag
?
'
addClass
'
:
'
removeClass
'
](
'
ui-state-disabled
'
);
this
.
element
.
attr
({
'
disabled
'
:
flag
,
'
aria-disabled
'
:
flag
});
},
// open the menu
open
:
function
(
e
){
var
self
=
this
,
button
=
this
.
button
,
menu
=
this
.
menu
,
speed
=
this
.
speed
,
o
=
this
.
options
,
args
=
[];
// bail if the multiselectopen event returns false, this widget is disabled, or is already open
if
(
this
.
_trigger
(
'
beforeopen
'
)
===
false
||
button
.
hasClass
(
'
ui-state-disabled
'
)
||
this
.
_isOpen
){
return
;
}
var
$container
=
menu
.
find
(
'
ul
'
).
last
(),
effect
=
o
.
show
,
pos
=
button
.
offset
();
// figure out opening effects/speeds
if
(
$
.
isArray
(
o
.
show
)
){
effect
=
o
.
show
[
0
];
speed
=
o
.
show
[
1
]
||
self
.
speed
;
}
// if there's an effect, assume jQuery UI is in use
// build the arguments to pass to show()
if
(
effect
)
{
args
=
[
effect
,
speed
];
}
// set the scroll of the checkbox container
$container
.
scrollTop
(
0
).
height
(
o
.
height
);
// position and show menu
if
(
$
.
ui
.
position
&&
!
$
.
isEmptyObject
(
o
.
position
)
){
o
.
position
.
of
=
o
.
position
.
of
||
button
;
menu
.
show
()
.
position
(
o
.
position
)
.
hide
();
// if position utility is not available...
}
else
{
menu
.
css
({
top
:
pos
.
top
+
button
.
outerHeight
(),
left
:
pos
.
left
});
}
// show the menu, maybe with a speed/effect combo
$
.
fn
.
show
.
apply
(
menu
,
args
);
// select the first option
// triggering both mouseover and mouseover because 1.4.2+ has a bug where triggering mouseover
// will actually trigger mouseenter. the mouseenter trigger is there for when it's eventually fixed
this
.
labels
.
eq
(
0
).
trigger
(
'
mouseover
'
).
trigger
(
'
mouseenter
'
).
find
(
'
input
'
).
trigger
(
'
focus
'
);
button
.
addClass
(
'
ui-state-active
'
);
this
.
_isOpen
=
true
;
this
.
_trigger
(
'
open
'
);
},
// close the menu
close
:
function
(){
if
(
this
.
_trigger
(
'
beforeclose
'
)
===
false
){
return
;
}
var
o
=
this
.
options
,
effect
=
o
.
hide
,
speed
=
this
.
speed
,
args
=
[];
// figure out opening effects/speeds
if
(
$
.
isArray
(
o
.
hide
)
){
effect
=
o
.
hide
[
0
];
speed
=
o
.
hide
[
1
]
||
this
.
speed
;
}
if
(
effect
)
{
args
=
[
effect
,
speed
];
}
$
.
fn
.
hide
.
apply
(
this
.
menu
,
args
);
this
.
button
.
removeClass
(
'
ui-state-active
'
).
trigger
(
'
blur
'
).
trigger
(
'
mouseleave
'
);
this
.
_isOpen
=
false
;
this
.
_trigger
(
'
close
'
);
},
enable
:
function
(){
this
.
_toggleDisabled
(
false
);
},
disable
:
function
(){
this
.
_toggleDisabled
(
true
);
},
checkAll
:
function
(
e
){
this
.
_toggleChecked
(
true
);
this
.
_trigger
(
'
checkAll
'
);
},
uncheckAll
:
function
(){
this
.
_toggleChecked
(
false
);
this
.
_trigger
(
'
uncheckAll
'
);
},
getChecked
:
function
(){
return
this
.
menu
.
find
(
'
input
'
).
filter
(
'
:checked
'
);
},
destroy
:
function
(){
// remove classes + data
$
.
Widget
.
prototype
.
destroy
.
call
(
this
);
this
.
button
.
remove
();
this
.
menu
.
remove
();
this
.
element
.
show
();
return
this
;
},
isOpen
:
function
(){
return
this
.
_isOpen
;
},
widget
:
function
(){
return
this
.
menu
;
},
getButton
:
function
(){
return
this
.
button
;
},
// react to option changes after initialization
_setOption
:
function
(
key
,
value
){
var
menu
=
this
.
menu
;
switch
(
key
){
case
'
header
'
:
menu
.
find
(
'
div.ui-multiselect-header
'
)[
value
?
'
show
'
:
'
hide
'
]();
break
;
case
'
checkAllText
'
:
menu
.
find
(
'
a.ui-multiselect-all span
'
).
eq
(
-
1
).
text
(
value
);
break
;
case
'
uncheckAllText
'
:
menu
.
find
(
'
a.ui-multiselect-none span
'
).
eq
(
-
1
).
text
(
value
);
break
;
case
'
height
'
:
menu
.
find
(
'
ul
'
).
last
().
height
(
parseInt
(
value
,
10
)
);
break
;
case
'
minWidth
'
:
this
.
options
[
key
]
=
parseInt
(
value
,
10
);
this
.
_setButtonWidth
();
this
.
_setMenuWidth
();
break
;
case
'
selectedText
'
:
case
'
selectedList
'
:
case
'
noneSelectedText
'
:
this
.
options
[
key
]
=
value
;
// these all needs to update immediately for the update() call
this
.
update
();
break
;
case
'
classes
'
:
menu
.
add
(
this
.
button
).
removeClass
(
this
.
options
.
classes
).
addClass
(
value
);
break
;
case
'
multiple
'
:
menu
.
toggleClass
(
'
ui-multiselect-single
'
,
!
value
);
this
.
options
.
multiple
=
value
;
this
.
element
[
0
].
multiple
=
value
;
this
.
refresh
();
}
$
.
Widget
.
prototype
.
_setOption
.
apply
(
this
,
arguments
);
}
});
})(
jQuery
);
vendor/stylesheets/jquery.multiselect.css
0 → 100644
View file @
89ea4df0
.ui-multiselect
{
padding
:
2px
0
2px
4px
;
text-align
:
left
}
.ui-multiselect
span
.ui-icon
{
float
:
right
}
.ui-multiselect-single
.ui-multiselect-checkboxes
input
{
position
:
absolute
!important
;
top
:
auto
!important
;
left
:
-9999px
;
}
.ui-multiselect-single
.ui-multiselect-checkboxes
label
{
padding
:
5px
!important
}
.ui-multiselect-header
{
margin-bottom
:
3px
;
padding
:
3px
0
3px
4px
}
.ui-multiselect-header
ul
{
font-size
:
0.9em
}
.ui-multiselect-header
ul
li
{
float
:
left
;
padding
:
0
10px
0
0
}
.ui-multiselect-header
a
{
text-decoration
:
none
}
.ui-multiselect-header
a
:hover
{
text-decoration
:
underline
}
.ui-multiselect-header
span
.ui-icon
{
float
:
left
}
.ui-multiselect-header
li
.ui-multiselect-close
{
float
:
right
;
text-align
:
right
;
padding-right
:
0
}
.ui-multiselect-menu
{
display
:
none
;
padding
:
3px
;
position
:
absolute
;
z-index
:
10000
;
text-align
:
left
}
.ui-multiselect-checkboxes
{
position
:
relative
/* fixes bug in IE6/7 */
;
overflow-y
:
scroll
}
.ui-multiselect-checkboxes
label
{
cursor
:
default
;
display
:
block
;
border
:
1px
solid
transparent
;
padding
:
3px
1px
}
.ui-multiselect-checkboxes
label
input
{
position
:
relative
;
top
:
1px
}
.ui-multiselect-checkboxes
li
{
clear
:
both
;
font-size
:
0.9em
;
padding-right
:
3px
}
.ui-multiselect-checkboxes
li
.ui-multiselect-optgroup-label
{
text-align
:
center
;
font-weight
:
bold
;
border-bottom
:
1px
solid
}
.ui-multiselect-checkboxes
li
.ui-multiselect-optgroup-label
a
{
display
:
block
;
padding
:
3px
;
margin
:
1px
0
;
text-decoration
:
none
}
/* remove label borders in IE6 because IE6 does not support transparency */
*
html
.ui-multiselect-checkboxes
label
{
border
:
none
}
vendor/stylesheets/mod_websocket.lua
0 → 100644
View file @
89ea4df0
-- Prosody IM
-- Copyright (C) 2012 Florian Zeitz
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--
module
:
set_global
();
local
add_filter
=
require
"util.filters"
.
add_filter
;
local
sha1
=
require
"util.hashes"
.
sha1
;
local
base64
=
require
"util.encodings"
.
base64
.
encode
;
local
softreq
=
require
"util.dependencies"
.
softreq
;
local
portmanager
=
require
"core.portmanager"
;
local
bit
=
softreq
"bit"
or
softreq
"bit32"
or
module
:
log
(
"error"
,
"No bit module found. Either LuaJIT 2 or Lua 5.2 is required"
);
local
band
=
bit
.
band
;
local
bxor
=
bit
.
bxor
;
local
cross_domain
=
module
:
get_option
(
"cross_domain_websocket"
);
if
cross_domain
then
if
cross_domain
==
true
then
cross_domain
=
"*"
;
elseif
type
(
cross_domain
)
==
"table"
then
cross_domain
=
table.concat
(
cross_domain
,
", "
);
end
if
type
(
cross_domain
)
~=
"string"
then
cross_domain
=
nil
;
end
end
module
:
depends
(
"c2s"
)
local
sessions
=
module
:
shared
(
"c2s/sessions"
);
local
c2s_listener
=
portmanager
.
get_service
(
"c2s"
).
listener
;
-- Websocket helpers
local
function
parse_frame
(
frame
)
local
result
=
{};
local
pos
=
1
;
local
length_bytes
=
0
;
local
counter
=
0
;
local
tmp_byte
;
if
#
frame
<
2
then
return
;
end
tmp_byte
=
string.byte
(
frame
,
pos
);
result
.
FIN
=
band
(
tmp_byte
,
0x80
)
>
0
;
result
.
RSV1
=
band
(
tmp_byte
,
0x40
)
>
0
;
result
.
RSV2
=
band
(
tmp_byte
,
0x20
)
>
0
;
result
.
RSV3
=
band
(
tmp_byte
,
0x10
)
>
0
;
result
.
opcode
=
band
(
tmp_byte
,
0x0F
);
pos
=
pos
+
1
;
tmp_byte
=
string.byte
(
frame
,
pos
);
result
.
MASK
=
band
(
tmp_byte
,
0x80
)
>
0
;
result
.
length
=
band
(
tmp_byte
,
0x7F
);
if
result
.
length
==
126
then
length_bytes
=
2
;
result
.
length
=
0
;
elseif
result
.
length
==
127
then
length_bytes
=
8
;
result
.
length
=
0
;
end
if
#
frame
<
(
2
+
length_bytes
)
then
return
;
end
for
i
=
1
,
length_bytes
do
pos
=
pos
+
1
;
result
.
length
=
result
.
length
*
256
+
string.byte
(
frame
,
pos
);
end
if
#
frame
<
(
2
+
length_bytes
+
(
result
.
MASK
and
4
or
0
)
+
result
.
length
)
then
return
;
end
if
result
.
MASK
then
result
.
key
=
{
string.byte
(
frame
,
pos
+
1
),
string.byte
(
frame
,
pos
+
2
),
string.byte
(
frame
,
pos
+
3
),
string.byte
(
frame
,
pos
+
4
)}
pos
=
pos
+
5
;
result
.
data
=
""
;
for
i
=
pos
,
pos
+
result
.
length
-
1
do
result
.
data
=
result
.
data
..
string.char
(
bxor
(
result
.
key
[
counter
+
1
],
string.byte
(
frame
,
i
)));
counter
=
(
counter
+
1
)
%
4
;
end
else
result
.
data
=
frame
:
sub
(
pos
+
1
,
pos
+
result
.
length
);
end
return
result
,
2
+
length_bytes
+
(
result
.
MASK
and
4
or
0
)
+
result
.
length
;
end
local
function
build_frame
(
desc
)
local
length
;
local
result
=
""
;
local
data
=
desc
.
data
or
""
;
result
=
result
..
string.char
(
0x80
*
(
desc
.
FIN
and
1
or
0
)
+
desc
.
opcode
);
length
=
#
data
;
if
length
<=
125
then
-- 7-bit length
result
=
result
..
string.char
(
length
);
elseif
length
<=
0xFFFF
then
-- 2-byte length
result
=
result
..
string.char
(
126
);
result
=
result
..
string.char
(
length
/
0x100
)
..
string.char
(
length
%
0x100
);
else
-- 8-byte length
result
=
result
..
string.char
(
127
);
for
i
=
7
,
0
,
-
1
do
result
=
result
..
string.char
((
length
/
(
2
^
(
8
*
i
))
)
%
0x100
);
end
end
result
=
result
..
data
;
return
result
;
end
--- Filter stuff
function
handle_request
(
event
,
path
)
local
request
,
response
=
event
.
request
,
event
.
response
;
local
conn
=
response
.
conn
;
if
not
request
.
headers
.
sec_websocket_key
then
response
.
headers
.
content_type
=
"text/html"
;
return
[[<!DOCTYPE html><html><head><title>Websocket</title></head><body>
<p>It works! Now point your WebSocket client to this URL to connect to Prosody.</p>
</body></html>]]
;
end
local
wants_xmpp
=
false
;
(
request
.
headers
.
sec_websocket_protocol
or
""
):
gsub
(
"
([^
,
]
*
)
,
?
"
,
function
(
proto
)
if
proto
==
"xmpp"
then
wants_xmpp
=
true
;
end
end
);
if
not
wants_xmpp
then
return
501
;
end
local
function
websocket_close
(
code
,
message
)
local
data
=
string.char
(
code
/
0x100
)
..
string.char
(
code
%
0x100
)
..
message
;
conn
:
write
(
build_frame
({
opcode
=
0x8
,
FIN
=
true
,
data
=
data
}));
conn
:
close
();
end
local
dataBuffer
;
local
function
handle_frame
(
frame
)
module
:
log
(
"debug"
,
"Websocket received: %s (%i bytes)"
,
frame
.
data
,
#
frame
.
data
);
-- Error cases
if
frame
.
RSV1
or
frame
.
RSV2
or
frame
.
RSV3
then
-- Reserved bits non zero
websocket_close
(
1002
,
"Reserved bits not zero"
);
return
false
;
end
if
frame
.
opcode
>=
0x8
and
frame
.
length
>
125
then
-- Control frame with too much payload
websocket_close
(
1002
,
"Payload too large"
);
return
false
;
end
if
frame
.
opcode
>=
0x8
and
not
frame
.
FIN
then
-- Fragmented control frame
websocket_close
(
1002
,
"Fragmented control frame"
);
return
false
;
end
if
(
frame
.
opcode
>
0x2
and
frame
.
opcode
<
0x8
)
or
(
frame
.
opcode
>
0xA
)
then
websocket_close
(
1002
,
"Reserved opcode"
);
return
false
;
end
if
frame
.
opcode
==
0x0
and
not
dataBuffer
then
websocket_close
(
1002
,
"Unexpected continuation frame"
);
return
false
;
end
if
(
frame
.
opcode
==
0x1
or
frame
.
opcode
==
0x2
)
and
dataBuffer
then
websocket_close
(
1002
,
"Continuation frame expected"
);
return
false
;
end
-- Valid cases
if
frame
.
opcode
==
0x0
then
-- Continuation frame
dataBuffer
=
dataBuffer
..
frame
.
data
;
elseif
frame
.
opcode
==
0x1
then
-- Text frame
dataBuffer
=
frame
.
data
;
elseif
frame
.
opcode
==
0x2
then
-- Binary frame
websocket_close
(
1003
,
"Only text frames are supported"
);
return
;
elseif
frame
.
opcode
==
0x8
then
-- Close request
websocket_close
(
1000
,
"Goodbye"
);
return
;
elseif
frame
.
opcode
==
0x9
then
-- Ping frame
frame
.
opcode
=
0xA
;
conn
:
write
(
build_frame
(
frame
));
return
""
;
else
log
(
"warn"
,
"Received frame with unsupported opcode %i"
,
frame
.
opcode
);
return
""
;
end
if
frame
.
FIN
then
data
=
dataBuffer
;
dataBuffer
=
nil
;
return
data
;
end
return
""
;
end
conn
:
setlistener
(
c2s_listener
);
c2s_listener
.
onconnect
(
conn
);
local
frameBuffer
=
""
;
add_filter
(
sessions
[
conn
],
"bytes/in"
,
function
(
data
)
local
cache
=
""
;
frameBuffer
=
frameBuffer
..
data
;
local
frame
,
length
=
parse_frame
(
frameBuffer
);
while
frame
do
frameBuffer
=
frameBuffer
:
sub
(
length
+
1
);
local
result
=
handle_frame
(
frame
);
if
not
result
then
return
;
end
cache
=
cache
..
result
;
frame
,
length
=
parse_frame
(
frameBuffer
);
end
return
cache
;
end
);
add_filter
(
sessions
[
conn
],
"bytes/out"
,
function
(
data
)
return
build_frame
({
FIN
=
true
,
opcode
=
0x01
,
data
=
tostring
(
data
)});
end
);
response
.
status
=
"101 Switching Protocols"
;
response
.
headers
.
upgrade
=
"websocket"
;
response
.
headers
.
connection
=
"Upgrade"
;
response
.
headers
.
sec_webSocket_accept
=
base64
(
sha1
(
request
.
headers
.
sec_websocket_key
..
"258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
));
response
.
headers
.
sec_webSocket_protocol
=
"xmpp"
;
response
.
headers
.
access_control_allow_origin
=
cross_domain
;
return
""
;
end
function
module
.
add_host
(
module
)
module
:
depends
(
"http"
);
module
:
provides
(
"http"
,
{
name
=
"xmpp-websocket"
;
route
=
{
[
"GET"
]
=
handle_request
;
[
"GET /"
]
=
handle_request
;
};
});
end
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