Kimchi-devel
Threads by month
- ----- 2025 -----
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
August 2014
- 19 participants
- 110 discussions
This patch set enables Power(ppc) support by default on Kimchi.
Whithout them, the upstream code of Kimchi is not fully compatible
with the Power architecture.
Eli Qiao (2):
Add tablet to vmtemplate,to provides an absolute pointer device which
often helps with getting a consistent mouse cursor position in VNC.
Fix Bug 110927 - Sosreport throwing kimchi error Signed-off-by: Eli
Qiao <taget(a)linux.vnet.ibm.com>
Mark Wu (1):
Add sos plugin for kimchi
Paulo Vital (2):
PowerKVM: Workaround of numpy byte order bug on PowerPC
Add PowerKVM information as ISO otpion to installation.
contrib/kimchi.spec.fedora.in | 3 +++
src/kimchi/Makefile.am | 4 +++-
src/kimchi/isoinfo.py | 1 +
src/kimchi/osinfo.py | 5 +++--
src/kimchi/sos.py | 37 +++++++++++++++++++++++++++++++++++++
src/kimchi/vmtemplate.py | 5 +++++
src/kimchi/websocket.py | 7 +++++--
7 files changed, 57 insertions(+), 5 deletions(-)
create mode 100644 src/kimchi/sos.py
--
1.9.3
5
13
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Yu Xin Huo (2):
Passthrough: Add PCI Devices to VM
update po files
po/en_US.po | 15 ++++++
po/kimchi.pot | 15 ++++++
po/pt_BR.po | 15 ++++++
po/zh_CN.po | 15 ++++++
ui/css/theme-default/guest-edit.css | 86 ++++++++++++++++++++++++++++++++++-
ui/js/src/kimchi.api.js | 55 ++++++++++++++++++++++
ui/js/src/kimchi.guest_edit_main.js | 66 +++++++++++++++++++++++++++
ui/pages/guest-edit.html.tmpl | 28 +++++++++++
8 files changed, 293 insertions(+), 2 deletions(-)
3
4
Hi!
After creating a few patches for Kimchi, it occurred to me that the
mockmodel design has more cons than pros. So I started wondering why we
still use it.
I understand - and totally agree - that we should have an extensive test
infrastructure to make sure the code runs as designed and to make it
very easy to find regression bugs on every new feature we implement. And
as Kimchi works very integrated to its host system, it may not be easy
to create expected/failed situations for things we don't always have
control.
But for every new feature we implement, we currently have to:
1) Implement the feature itself
2) Create tests for (1)
3) Implement the feature in a mock environment
4) Create tests for (3)
And by looking at some existing code, many of the mock tests don't
actually test anything. They're just there so we can say we have a mock
implementation and a test for it. Why create a mock function and a test
for the mock function if what we need is to test the real code? And a
lot of times, the mock implementation (and the tests) are just
copy/paste of the original feature. For me, that seems very tedious and
useless.
Why don't we get rid of the mockmodel and keep only the "real" model? I
understand that it's not so simple to remove it right away, that's why
I'm starting this discussion so we could see how to do it better - if
we're actually going to do it. What would we miss if we didn't have the
mockmodel anymore? I believe we'll need to be more careful when writing
tests which run on a real environment (i.e. we can't "leak" VMs,
networks, templates, or anything else) but at least we'll always test
real code and we won't be writing code that won't be used.
Please share your opinion with us.
Best regards,
Crístian.
2
2

03 Oct '14
In order to allow a guest to use SMT/hyperthreading, we should
enable passing in of the sockets, cores, and threads values when
creating a template.
All three values must be specified, as per the topology descr at
http://libvirt.org/formatdomain.html#elementsCPU
Signed-off-by: Christy Perez <christy(a)linux.vnet.ibm.com>
---
docs/API.md | 7 +++++++
src/kimchi/API.json | 28 ++++++++++++++++++++++++++--
src/kimchi/control/templates.py | 11 ++++++++---
src/kimchi/osinfo.py | 2 +-
src/kimchi/vmtemplate.py | 22 ++++++++++++++++++++++
5 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/docs/API.md b/docs/API.md
index d75c55f..cb4d541 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -200,6 +200,13 @@ Represents a snapshot of the Virtual Machine's primary monitor.
Independent Computing Environments
* null: Graphics is disabled or type not supported
* listen: The network which the vnc/spice server listens on.
+ * cpu_info *(optional)*: CPU-specific information.
+ * topology: Specify sockets, threads, and cores to run the virtual CPU
+ threads on.
+ All three are required in order to specify cpu topology.
+ * sockets - The number of sockets to use.
+ * cores - The number of cores per socket.
+ * threads - The number of threads per core.
### Sub-Collection: Virtual Machine Network Interfaces
diff --git a/src/kimchi/API.json b/src/kimchi/API.json
index c3fc5e3..d510634 100644
--- a/src/kimchi/API.json
+++ b/src/kimchi/API.json
@@ -26,6 +26,28 @@
]
}
}
+ },
+ "cpu_info": {
+ "description": "Configure CPU specifics for a VM.",
+ "type": "object",
+ "properties": {
+ "topology": {
+ "description": "Configure the guest CPU topology.",
+ "type": "object",
+ "properties": {
+ "sockets": {
+ "type": "number"
+ },
+ "cores": {
+ "type": "number"
+ },
+ "threads": {
+ "type": "number"
+ }
+ },
+ "required": [ "sockets", "cores", "threads" ]
+ }
+ }
}
},
"properties": {
@@ -438,7 +460,8 @@
"type": "array",
"items": { "type": "string" }
},
- "graphics": { "$ref": "#/kimchitype/graphics" }
+ "graphics": { "$ref": "#/kimchitype/graphics" },
+ "cpu_info": { "$ref": "#/kimchitype/cpu_info" }
},
"additionalProperties": false,
"error": "KCHAPI0001E"
@@ -608,7 +631,8 @@
"type": "array",
"items": { "type": "string" }
},
- "graphics": { "$ref": "#/kimchitype/graphics" }
+ "graphics": { "$ref": "#/kimchitype/graphics" },
+ "cpu_info": { "$ref": "#/kimchitype/cpu_info" }
},
"additionalProperties": false,
"error": "KCHAPI0001E"
diff --git a/src/kimchi/control/templates.py b/src/kimchi/control/templates.py
index e17fa54..5ef35ce 100644
--- a/src/kimchi/control/templates.py
+++ b/src/kimchi/control/templates.py
@@ -38,13 +38,14 @@ def __init__(self, model, ident):
self.update_params = ["name", "folder", "icon", "os_distro",
"storagepool", "os_version", "cpus",
"memory", "cdrom", "disks", "networks",
- "graphics"]
+ "graphics", "cpu_info"]
self.uri_fmt = "/templates/%s"
self.clone = self.generate_action_handler('clone')
@property
def data(self):
- return {'name': self.ident,
+ return_data = {
+ 'name': self.ident,
'icon': self.info['icon'],
'invalid': self.info['invalid'],
'os_distro': self.info['os_distro'],
@@ -56,4 +57,8 @@ def data(self):
'storagepool': self.info['storagepool'],
'networks': self.info['networks'],
'folder': self.info.get('folder', []),
- 'graphics': self.info['graphics']}
+ 'graphics': self.info['graphics']
+ }
+ if (self.info.get('cpu_info')):
+ return_data['cpu_info'] = self.info['cpu_info']
+ return return_data
diff --git a/src/kimchi/osinfo.py b/src/kimchi/osinfo.py
index 1ad353c..8492bb7 100644
--- a/src/kimchi/osinfo.py
+++ b/src/kimchi/osinfo.py
@@ -32,7 +32,7 @@
'power': ('ppc', 'ppc64')}
-common_spec = {'cpus': 1, 'cpu_cores': 1, 'cpu_threads': 1, 'memory': 1024,
+common_spec = {'cpus': 1, 'cpu_sockets': '1', 'cpu_cores': 1, 'cpu_threads': 1, 'memory': 1024,
'disks': [{'index': 0, 'size': 10}], 'cdrom_bus': 'ide',
'cdrom_index': 2, 'mouse_bus': 'ps2'}
diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py
index 761bac5..c64e61d 100644
--- a/src/kimchi/vmtemplate.py
+++ b/src/kimchi/vmtemplate.py
@@ -359,6 +359,22 @@ def _get_input_output_xml(self):
input_output += sound % self.info
return input_output
+ def _get_cpu_xml(self):
+
+ cpu_info = self.info.get('cpu_info')
+ if cpu_info is not None:
+ cpu_topo = cpu_info.get('topology')
+ if cpu_topo is not None:
+ return """
+ <cpu>
+ <topology sockets='%(sockets)s'
+ cores='%(cores)s'
+ threads='%(threads)s'/>
+ </cpu>
+ """ % cpu_topo
+
+ return ""
+
def to_vm_xml(self, vm_name, vm_uuid, **kwargs):
params = dict(self.info)
params['name'] = vm_name
@@ -371,6 +387,11 @@ def to_vm_xml(self, vm_name, vm_uuid, **kwargs):
graphics = kwargs.get('graphics')
params['graphics'] = self._get_graphics_xml(graphics)
+ if params.get('cpu_info') is not None:
+ params['cpu_info'] = self._get_cpu_xml()
+ else:
+ params['cpu_info'] = ''
+
# Current implementation just allows to create disk in one single
# storage pool, so we cannot mix the types (scsi volumes vs img file)
storage_type = self._get_storage_type()
@@ -401,6 +422,7 @@ def to_vm_xml(self, vm_name, vm_uuid, **kwargs):
<uuid>%(uuid)s</uuid>
<memory unit='MiB'>%(memory)s</memory>
<vcpu>%(cpus)s</vcpu>
+ %(cpu_info)s
<os>
<type arch='%(arch)s'>hvm</type>
<boot dev='hd'/>
--
1.9.3
2
6
V1:
Kimchi is currently not able to configure and open help pages of any
plugin tab. This patch fixes this problem and removes the HELP button
if the plugin does not have a help configured properly.
Help pages for a plugin tab follow same Kimchi system:
- html help file should have the same name of plugin html tab file
- html files should be in plugin's " ui/pages/help/<LANG> " path
- plugin should add following lines to <PLUGIN_NAME>.conf:
* [/help]
* tools.staticdir.on = True
* tools.nocache.on = True
- plugins should add a <help> element with tab help html name in
"ui/config/tab-ext.xml", for each configured tab. If this
element does not exist, 'Help' will be disabled
V2:
- Changed the way that tab help html is being checked.
- Now, all tabs have the help html file checked (per language)
You can check enabling Sample plugin. Go to plugin Sample tab.
For en_US, the help button will be enabled. But if you logout
and change to other language, the help button will be disabled,
because the sample plugin does not have help in other languages.
Rodrigo Trujillo (1):
Detect and enable help pages for tabs and plugin tabs
plugins/sample/sample.conf.in | 4 ++++
plugins/sample/ui/pages/help/en_US/tab.html | 1 +
src/kimchi/config.py.in | 1 +
src/kimchi/utils.py | 9 ++++++--
ui/js/src/kimchi.main.js | 36 ++++++++++++++++++++++++++++-
5 files changed, 48 insertions(+), 3 deletions(-)
create mode 100644 plugins/sample/ui/pages/help/en_US/tab.html
--
1.9.3
2
4
This patch implements new functionality that allows user to create a
template based on a givem virtual machine. Template will have same
number of CPUS, amount of Memory, CDROM attached, disks, etc.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
src/kimchi/API.json | 6 ++++++
src/kimchi/i18n.py | 1 +
src/kimchi/model/templates.py | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 41 insertions(+)
diff --git a/src/kimchi/API.json b/src/kimchi/API.json
index c3fc5e3..e1e0218 100644
--- a/src/kimchi/API.json
+++ b/src/kimchi/API.json
@@ -385,6 +385,12 @@
"minimum": 512,
"error": "KCHTMPL0013E"
},
+ "vm": {
+ "description": "Virtual Machine which template will be based in",
+ "type": "string",
+ "minimum": 1,
+ "error": "KCHTMPL0025E"
+ },
"cdrom": {
"description": "Path for cdrom",
"type": "string",
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index 2eae7e8..8b9c713 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -130,6 +130,7 @@ messages = {
"KCHTMPL0022E": _("Disk size must be greater than 1GB."),
"KCHTMPL0023E": _("Template base image must be a valid local image file"),
"KCHTMPL0024E": _("Cannot identify base image %(path)s format"),
+ "KCHTMPL0025E": _("Virtual machine name must be a string"),
"KCHPOOL0001E": _("Storage pool %(name)s already exists"),
"KCHPOOL0002E": _("Storage pool %(name)s does not exist"),
diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py
index bf04304..15296b0 100644
--- a/src/kimchi/model/templates.py
+++ b/src/kimchi/model/templates.py
@@ -36,6 +36,7 @@ class TemplatesModel(object):
def __init__(self, **kargs):
self.objstore = kargs['objstore']
self.conn = kargs['conn']
+ self.kargs = kargs
def create(self, params):
name = params.get('name', '').strip()
@@ -68,6 +69,12 @@ class TemplatesModel(object):
except Exception:
raise InvalidParameter("KCHTMPL0003E", {'network': net_name,
'template': name})
+
+ # Template based in a given VM
+ vm = params.get('vm')
+ if vm:
+ params.update(self._get_vm_params(vm))
+
# Creates the template class with necessary information
# Checkings will be done while creating this class, so any exception
# will be raised here
@@ -83,6 +90,33 @@ class TemplatesModel(object):
return name
+ def _get_vm_params(self, vm):
+ from kimchi.model.vms import VMModel
+ vm_info = VMModel(**self.kargs).lookup(vm)
+ ret = {}
+
+ # Get CPUs, Memory, Graphics
+ ret['cpus'] = vm_info['cpus']
+ ret['memory'] = vm_info['memory']
+ ret['graphics'] = vm_info['graphics']
+
+ # CDROM
+ from kimchi.model.vmstorages import VMStoragesModel, VMStorageModel
+ vmStorages = VMStoragesModel(**self.kargs)
+ disks = vmStorages.get_list(vm)
+ vmStorage = VMStorageModel(**self.kargs)
+ for disk in disks:
+ disk_info = vmStorage.lookup(vm, disk)
+ if disk_info['type'] == 'cdrom':
+ # get first cdrom found
+ ret['cdrom'] = disk_info['path']
+ break
+ # ToDo:
+ # Get Disks
+ # Get StoragePool
+ # Get Networks
+ return ret
+
def get_list(self):
with self.objstore as session:
return session.get_list('template')
--
1.9.3
3
5
This patchset is a work in progress. I'm sending it now so we can get early feedback.
The following problems are known:
- The previous "create" function (which is now called "_create_volume_with_capacity")
should be updated to the Task API. This will cause errors in existing tests.
- There is one unfinished error message.
- The tests and mockmodel are missing.
Any feedback is welcome.
Crístian Viana (4):
storagevolume: Split "create" function based on the parameters
storagevolume: Use a Task to create a new volume
storagevolume: Download remote images to a storage pool
storagevolume: Add download progress to task
src/kimchi/control/storagevolumes.py | 4 +-
src/kimchi/model/storagevolumes.py | 71 +++++++++++++++++++++++++++++++++++-
2 files changed, 72 insertions(+), 3 deletions(-)
--
1.9.3
2
5
I haven't add the federation info to /config/capabilities because I thought in
always display the same message when any peers were found.
"No peers found.
More information at: <federation-help-page>"
Then we can use README-federation as the federation help page.
What do you think?
Aline Manera (5):
Update kimchi.config values according to command line input
Delete http_port from /config API as it is not in use anymore
Add federation option to Kimchi config file
Discover Kimchi peers using openSLP
Add documentation on how to enable federation on Kimchi
docs/API.md | 1 -
docs/README-federation.md | 27 +++++++++++++++++++++
src/kimchi.conf.in | 4 +++
src/kimchi/config.py.in | 1 +
src/kimchi/control/peers.py | 29 ++++++++++++++++++++++
src/kimchi/mockmodel.py | 11 ++++++---
src/kimchi/model/config.py | 3 +--
src/kimchi/model/peers.py | 59 +++++++++++++++++++++++++++++++++++++++++++++
src/kimchi/server.py | 1 -
src/kimchid.in | 36 +++++++++++++++++----------
tests/test_rest.py | 7 +++++-
11 files changed, 158 insertions(+), 21 deletions(-)
create mode 100644 docs/README-federation.md
create mode 100644 src/kimchi/control/peers.py
create mode 100644 src/kimchi/model/peers.py
--
1.9.3
3
17
Folks,
I'm about to send a patch to you guys that:
1) adds "serial" as a graphics type
2) allows you to edit the graphics type on a cold guest
In doing so and talking with Adam and Aline, it seems that a rework of
how the guest's xml is edited is in order. When you edit a template,
for example, the whole templates xml is basically re-instantiated and
only the values that kimchi cares about is added. The rest libvirt
fills in.
The editing of guest xmls differs from that mechanism. Right now the
actual xml is edited and resubmitted. If you think about editing
components of a guest (think about changing graphics types), you can see
where long-term there is an increased opportunity for the xml to become
complicated and maybe even incorrect. XML creep if you will.
So, we are thinking that the cold guest edition should more closely
follow how the template works, where critical parts that kimchi has
fields for is inserted and libvirt does the rest of the work. This will
also allow for future growth/change where new components are added.
I'll likely start a branch for it and get started on this. In the
meanwhile, I'll submit my graphics patches for a start.
Brent
2
1
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/topbar.css | 36 +++++++++++++++++++++++++++++++++++-
ui/js/src/kimchi.api.js | 14 ++++++++++++++
ui/js/src/kimchi.main.js | 14 ++++++++++++++
ui/pages/kimchi-ui.html.tmpl | 10 ++++++++++
4 files changed, 73 insertions(+), 1 deletions(-)
diff --git a/ui/css/theme-default/topbar.css b/ui/css/theme-default/topbar.css
index bed4e19..127c56a 100644
--- a/ui/css/theme-default/topbar.css
+++ b/ui/css/theme-default/topbar.css
@@ -114,7 +114,7 @@
cursor: pointer;
display: block;
position: relative;
- height: 48px;
+ height: 52px;
margin: 0 12px;
}
@@ -174,3 +174,37 @@ a#btn-logout:hover {
display: none;
}
}
+
+.peers {
+ color: white;
+ cursor: pointer;
+ height: 52px;
+ margin: 0 12px;
+}
+
+.peers span {
+ margin-top: 25px;
+}
+
+.peers .arrow {
+ border: 6px solid transparent;
+ border-bottom: none;
+ border-top-color: white;
+ display: inline-block;
+ width: 0;
+}
+
+.peers .drowdown {
+ top: 45px;
+ right: 110px;
+ color: black;
+ padding: 10px 15px;
+ white-space: nowrap;
+ line-height: 12px;
+ width: inherit;
+}
+
+.peers .drowdown a {
+ display: block;
+ padding: 10px;
+}
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 4562992..8966cff 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1098,5 +1098,19 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ getPeers : function(suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'peers',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.main.js b/ui/js/src/kimchi.main.js
index ba54b26..21ffca7 100644
--- a/ui/js/src/kimchi.main.js
+++ b/ui/js/src/kimchi.main.js
@@ -20,10 +20,24 @@ kimchi.tabMode = {};
kimchi.capabilities = undefined;
kimchi.getCapabilities(function(result) {
kimchi.capabilities = result;
+ kimchi.setupPeers();
}, function() {
kimchi.capabilities = {};
});
+kimchi.setupPeers = function(){
+ if(kimchi.capabilities.federation=="on"){
+ $('#peers').removeClass('hide-content');
+ kimchi.getPeers(function(data){
+ var hints = $('p', $('.drowdown', '#peers'));
+ data.length==0 ? hints.toggleClass('hide-content'): hints.addClass('hide-content');
+ for(var i=0; i<data.length; i++){
+ $('.drowdown', '#peers').append("<a href='"+data[i]+"' target='_blank'>"+data[i]+"</a>");
+ }
+ });
+ }
+};
+
kimchi.main = function() {
kimchi.popable();
diff --git a/ui/pages/kimchi-ui.html.tmpl b/ui/pages/kimchi-ui.html.tmpl
index 7bdf441..867ee36 100644
--- a/ui/pages/kimchi-ui.html.tmpl
+++ b/ui/pages/kimchi-ui.html.tmpl
@@ -71,6 +71,16 @@
<h1 id="logo"><img alt="Project Kimchi" src="images/theme-default/logo-white.png"></h1>
<ul class="nav-top">
<li>
+ <div id="peers" class="peers hide-content popable">
+ <span>$_("Peers")</span>
+ <span class="arrow"></span>
+ <div class="drowdown popover right-side">
+ <p>$_("Searching")...</p>
+ <p class="hide-content">$_("No peers found.")</p>
+ </div>
+ </div>
+ </li>
+ <li>
<div id="user" class="popable">
<span id="user-icon"></span>
<span id="user-name" class="empty-when-logged-off"></span>
--
1.7.1
2
2
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Iscsi volume fails to attach to vm because of its path is rejected
by previous cdrom validate logic,
and type 'unknown' forms wrong vm xml
This patchset fix the above bugs and form right vm xml in order to
start vm.
Royce Lv (2):
Guest storage: Fix attaching type judgement
Guest storage: fix volume format overwrite
src/kimchi/model/vmstorages.py | 16 +++++-----------
1 file changed, 5 insertions(+), 11 deletions(-)
--
1.8.3.2
3
8
Since the authorization method changed, kimchiauth() does not accept any
parameter. The admin methods should be listed on Collection and
Resource elements.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
plugins/sample/__init__.py | 2 ++
plugins/sample/sample.conf.in | 2 --
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/plugins/sample/__init__.py b/plugins/sample/__init__.py
index 4aca4e0..10c244c 100644
--- a/plugins/sample/__init__.py
+++ b/plugins/sample/__init__.py
@@ -64,12 +64,14 @@ class Circles(Collection):
def __init__(self, model):
super(Circles, self).__init__(model)
self.resource = Circle
+ self.admin_methods = ['POST', 'PUT']
class Rectangles(Collection):
def __init__(self, model):
super(Rectangles, self).__init__(model)
self.resource = Rectangle
+ self.admin_methods = ['POST', 'PUT']
class Circle(Resource):
diff --git a/plugins/sample/sample.conf.in b/plugins/sample/sample.conf.in
index 6e0908b..cf42467 100644
--- a/plugins/sample/sample.conf.in
+++ b/plugins/sample/sample.conf.in
@@ -17,8 +17,6 @@ tools.kimchiauth.on = True
[/rectangles]
tools.kimchiauth.on = True
-tools.kimchiauth.admin_methods = ['POST', 'PUT']
[/circles]
tools.kimchiauth.on = True
-tools.kimchiauth.admin_methods = ['POST', 'PUT']
--
1.9.3
2
2
v1 -> V2:
- Update docs/API.md
- Expose federation on /config/capabilities
Aline Manera (6):
Update kimchi.config values according to command line input
Delete http_port from /config API as it is not in use anymore
Add federation option to Kimchi config file
Discover Kimchi peers using openSLP
Add documentation on how to enable federation on Kimchi
Expose federation on /config/capabilities
docs/API.md | 11 ++++++++-
docs/README-federation.md | 27 +++++++++++++++++++++
src/kimchi.conf.in | 4 +++
src/kimchi/config.py.in | 1 +
src/kimchi/control/peers.py | 29 ++++++++++++++++++++++
src/kimchi/mockmodel.py | 14 ++++++++---
src/kimchi/model/config.py | 6 ++---
src/kimchi/model/peers.py | 59 +++++++++++++++++++++++++++++++++++++++++++++
src/kimchi/server.py | 1 -
src/kimchid.in | 36 +++++++++++++++++----------
tests/test_rest.py | 19 +++++++++------
11 files changed, 177 insertions(+), 30 deletions(-)
create mode 100644 docs/README-federation.md
create mode 100644 src/kimchi/control/peers.py
create mode 100644 src/kimchi/model/peers.py
--
1.9.3
2
8
The data structure to graphics is:
{
graphics: {'passwd': '12345', 'passwdValidTo': 30}
}
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
src/kimchi/model/vms.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index fb4cf22..8045b1e 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -321,7 +321,7 @@ class VMModel(object):
if password is not None:
graphics.attrib['passwd'] = password
- expire = params.get("passwdValidTo")
+ expire = params['graphics'].get("passwdValidTo")
to = graphics.attrib.get('passwdValidTo')
if to is not None:
if (time.mktime(time.strptime(to, '%Y-%m-%dT%H:%M:%S'))
--
1.9.3
3
3

[PATCH] model.host: changing listDevices() to listAllDevices()
by Daniel Henrique Barboza 01 Sep '14
by Daniel Henrique Barboza 01 Sep '14
01 Sep '14
The method listDevices() has unexpected behavior in certain
situations and architectures. According the libvirt API,
listAllDevices() delivers more control over the results and
it does not present the same problems.
For those reasons, this commit changes all the occurences of
listDevices() to use listAllDevices() instead.
Signed-off-by: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
---
src/kimchi/model/host.py | 33 ++++++++++++++++++++++++---------
1 file changed, 24 insertions(+), 9 deletions(-)
diff --git a/src/kimchi/model/host.py b/src/kimchi/model/host.py
index 39f45d8..553ad7c 100644
--- a/src/kimchi/model/host.py
+++ b/src/kimchi/model/host.py
@@ -18,6 +18,7 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
import grp
+import libvirt
import os
import time
import platform
@@ -277,31 +278,45 @@ class PartitionModel(object):
class DevicesModel(object):
def __init__(self, **kargs):
self.conn = kargs['conn']
+ self.cap_map = \
+ {'fc_host': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_FC_HOST,
+ 'net': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_NET,
+ 'pci': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_PCI_DEV,
+ 'scsi': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI,
+ 'scsi_host': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_SCSI_HOST,
+ 'storage': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_STORAGE,
+ 'usb_device': libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_DEV,
+ 'usb':
+ libvirt.VIR_CONNECT_LIST_NODE_DEVICES_CAP_USB_INTERFACE}
def get_list(self, _cap=None):
+ if _cap == 'fc_host':
+ return self._get_devices_fc_host()
+ return self._get_devices_with_capability(_cap)
+
+ def _get_devices_with_capability(self, cap):
conn = self.conn.get()
- if _cap is None:
- dev_names = [name.name() for name in conn.listAllDevices(0)]
- elif _cap == 'fc_host':
- dev_names = self._get_devices_fc_host()
+ if cap is None:
+ cap_flag = 0
else:
- # Get devices with required capability
- dev_names = conn.listDevices(_cap, 0)
- return dev_names
+ cap_flag = self.cap_map.get(cap)
+ if cap_flag is None:
+ return []
+ return [name.name() for name in conn.listAllDevices(cap_flag)]
def _get_devices_fc_host(self):
conn = self.conn.get()
# Libvirt < 1.0.5 does not support fc_host capability
if not CapabilitiesModel().fc_host_support:
ret = []
- scsi_hosts = conn.listDevices('scsi_host', 0)
+ scsi_hosts = self._get_devices_with_capability('scsi_host')
for host in scsi_hosts:
xml = conn.nodeDeviceLookupByName(host).XMLDesc(0)
path = '/device/capability/capability/@type'
if 'fc_host' in xmlutils.xpath_get_text(xml, path):
ret.append(host)
return ret
- return conn.listDevices('fc_host', 0)
+ return self._get_devices_with_capability('fc_host')
class DeviceModel(object):
--
1.8.3.1
3
2

[PATCH] bug fix: Auto-generate guest console password when the passed value is an empty string
by Aline Manera 01 Sep '14
by Aline Manera 01 Sep '14
01 Sep '14
As the password will be an empty string we need to check if its value is
not None, otherwise the password will not be generated.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
src/kimchi/model/vms.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index e91c0ed..d8a02cd 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -312,7 +312,7 @@ class VMModel(object):
return xml
password = params['graphics'].get("passwd")
- if password and len(password.strip()) == 0:
+ if password is not None and len(password.strip()) == 0:
password = "".join(random.sample(string.ascii_letters +
string.digits, 8))
--
1.9.3
2
2
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/storage.css | 31 ++++++++++-
ui/js/src/kimchi.api.js | 20 +++++++-
ui/js/src/kimchi.storagepool_add_main.js | 88 ++++++++++++++++++++++++++++++
ui/pages/storagepool-add.html.tmpl | 12 +++-
4 files changed, 146 insertions(+), 5 deletions(-)
diff --git a/ui/css/theme-default/storage.css b/ui/css/theme-default/storage.css
index 4f439e8..ab92de2 100644
--- a/ui/css/theme-default/storage.css
+++ b/ui/css/theme-default/storage.css
@@ -315,7 +315,7 @@
}
.hide-content {
- display: none;
+ display: none!important;
}
.volumeslist {
@@ -593,3 +593,32 @@
center no-repeat;
padding: 0 20px 0 26px;
}
+
+.storage-admin .filter-select {
+ display: inline-block;
+ position: relative;
+}
+
+#iscsiportId, .storage-admin .filter-select input {
+ border: 1px solid #CCCCCC;
+ border-radius: 1px;
+ font-size: 14px;
+ padding: 3px 3px 3px 10px;
+ height: 30px;
+}
+
+.storage-admin .filter-select input::-ms-clear {
+ display: none;
+}
+
+#iSCSIServer input {
+ width: 410px;
+}
+
+#iscsiportId {
+ width: 60px;
+}
+
+#iSCSITarget input {
+ width: 493px;
+}
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 4562992..b657aeb 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -774,7 +774,9 @@ var kimchi = {
contentType : 'application/json',
dataType : 'json',
success : suc,
- error : err
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
});
},
@@ -1098,5 +1100,21 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ getISCSITargets : function(server, port, suc, err) {
+ server = encodeURIComponent(server);
+ port = encodeURIComponent(port);
+ kimchi.requestJSON({
+ url : kimchi.url + 'storageservers/'+server+'/storagetargets?_target_type=iscsi&_server_port='+port,
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js
index ecbc682..dc1b1c6 100644
--- a/ui/js/src/kimchi.storagepool_add_main.js
+++ b/ui/js/src/kimchi.storagepool_add_main.js
@@ -34,6 +34,90 @@ kimchi.storagepool_add_main = function() {
});
};
+kimchi.storageFilterSelect = function(id, isUpdate) {
+ var input = $('input', '#'+id);
+ var options = $(".option", '#'+id);
+ var filter = function(container, key){
+ container.children().each(function(){
+ $(this).css("display", $(this).text().indexOf(key)==-1 ? "none" : "");
+ });
+ };
+ if(!isUpdate){
+ input.on("keyup", function(){
+ filter(options, input.val());
+ });
+ }
+ options.children().each(function(){
+ $(this).click(function(){
+ options.children().removeClass("active");
+ input.val($(this).text());
+ $(this).addClass("active");
+ filter(options, "");
+ });
+ });
+};
+
+kimchi.setupISCSI = function(){
+ var loadTargets = function(server, port, callback){
+ var isUpdate = $(".option", "#iSCSITarget").children().length > 0;
+ $(".option", "#iSCSITarget").empty();
+ $('input', "#iSCSITarget").attr("placeholder", "loading targets...");
+ kimchi.getISCSITargets(server, port, function(data){
+ if(data.length==0){
+ $('input', "#iSCSITarget").attr("placeholder", "no targets is got, please input one.");
+ }else{
+ for(var i=0; i<data.length; i++){
+ var itemNode = $.parseHTML("<li>"+data[i].target+"</li>");
+ $(".option", "#iSCSITarget").append(itemNode);
+ }
+ $('input', "#iSCSITarget").attr("placeholder", "");
+ $(".popover", "#iSCSITarget").css("display", "block");
+ }
+ kimchi.storageFilterSelect('iSCSITarget', isUpdate);
+ $('input', "#iSCSITarget").trigger("focus");
+ callback();
+ }, function(data){
+ $('input', "#iSCSITarget").attr("placeholder","failed to load targets.");
+ callback();
+ kimchi.message.error(data.responseJSON.reason);
+ });
+ };
+ var triggerLoadTarget = function(){
+ $('input', "#iSCSITarget").val("");
+ var server = $("#iscsiserverId").val().trim();
+ var port = $("#iscsiportId").val().trim();
+ if(server!="" && port!="" && !$("#iscsiserverId").hasClass("invalid-field") && !$("#iscsiportId").hasClass("invalid-field")){
+ $("#iscsiserverId").attr("disabled", true);
+ $("#iscsiportId").attr("disabled", true);
+ loadTargets(server, port, function(){
+ $("#iscsiserverId").attr("disabled", false);
+ $("#iscsiportId").attr("disabled", false);
+ });
+ }
+ };
+ $("#iscsiserverId").change(function(){
+ triggerLoadTarget();
+ });
+ $("#iscsiportId").change(function(){
+ triggerLoadTarget();
+ });
+ var initISCSIServers = function(){
+ kimchi.getStorageServers("iscsi", function(data){
+ for(var i=0;i<data.length;i++){
+ var itemNode = $.parseHTML("<li>"+data[i].host+"</li>");
+ $(".option", "#iSCSIServer").append(itemNode);
+ $(itemNode).click(function(){
+ $("#iscsiportId").val($(this).prop("port"));
+ $("#iscsiserverId").val($(this).text());
+ triggerLoadTarget();
+ }).prop("port", data[i].port);
+ }
+ kimchi.storageFilterSelect('iSCSIServer', false);
+ });
+ };
+ initISCSIServers();
+};
+
kimchi.initStorageAddPage = function() {
kimchi.listHostPartitions(function(data) {
if (data.length > 0) {
@@ -160,6 +244,10 @@ kimchi.initStorageAddPage = function() {
$('#iscsiportId').keyup(function(event) {
$(this).toggleClass("invalid-field",!/^[0-9]+$/.test($(this).val()));
});
+ $('#iscsiserverId').keyup(function(event) {
+ $(this).toggleClass("invalid-field",!kimchi.isServer($(this).val().trim()));
+ });
+ kimchi.setupISCSI();
};
/* Returns 'true' if all form fields were filled, 'false' if
diff --git a/ui/pages/storagepool-add.html.tmpl b/ui/pages/storagepool-add.html.tmpl
index 1eb2029..6f1861b 100644
--- a/ui/pages/storagepool-add.html.tmpl
+++ b/ui/pages/storagepool-add.html.tmpl
@@ -23,7 +23,7 @@
<!DOCTYPE html>
<html>
<body>
- <div class="window storage-window">
+ <div class="window storage-window storage-admin">
<header>
<h1 class="title">$_("Define a New Storage Pool")</h1>
<div class="close">X</div>
@@ -111,7 +111,10 @@
<div class="field">
<p class="text-help">
$_("iSCSI server IP or hostname. It should not be empty.")</p>
- <input id="iscsiserverId" placeholder="$_("Server")" type="text" class="text storage-base-input-width">
+ <span class="filter-select popable" id="iSCSIServer">
+ <input id="iscsiserverId" type="text" placeholder="$_("Server")">
+ <div class="popover"><ul class="option select-list"></ul></div>
+ </span>
<input id="iscsiportId" placeholder="$_("Port")" type="text" class="text storage-port-width" maxlength="4">
</div>
</section>
@@ -119,7 +122,10 @@
<h2>4. $_("Target")</h2>
<div class="field">
<p class="text-help">$_("The iSCSI target on iSCSI server")</p>
- <input id="iscsiTargetId" type="text" class="text storage-base-input-width">
+ <span class="filter-select popable" id="iSCSITarget">
+ <input id="iscsiTargetId" type="text">
+ <div class="popover"><ul class="option select-list"></ul></div>
+ </span>
</div>
</section>
<section class="form-section">
--
1.7.1
3
2
This patch set adds new languages to improve Kimchi's i18n support. The
new languages are: German (de_DE), Spanish (es_ES), Franch (fr_FR),
Italian (it_IT), Japanese (ja_JP), Korean (ko_KR), Russian (ru_RU) and
the Traditional Chinese (zh_TW).
Also, the patch set removed the symbolic link of plugins/sample/po/LINGUAS
to be a 'real' file containing only the three first languages in the
sample plugin.
Paulo Vital (9):
i18n support: Changed the file type of plugins/sample/po/LINGUAS
i18n support: Add German translation files.
i18n support: Add Spanish translation files.
i18n support: Add French translation files.
i18n support: Add Italian translation files.
i18n support: Add Japanese translation files.
18n support: Add Korean translation files.
i18n support: Add Russian translation files.
i18n support: Add Traditional Chinese translation files.
plugins/sample/po/LINGUAS | 4 +-
po/LINGUAS | 8 +
po/de_DE.po | 2256 ++++++++++++++++++++++++++++++++++++++++++++
po/es_ES.po | 2281 ++++++++++++++++++++++++++++++++++++++++++++
po/fr_FR.po | 2285 +++++++++++++++++++++++++++++++++++++++++++++
po/it_IT.po | 2248 ++++++++++++++++++++++++++++++++++++++++++++
po/ja_JP.po | 2229 +++++++++++++++++++++++++++++++++++++++++++
po/ko_KR.po | 2137 ++++++++++++++++++++++++++++++++++++++++++
po/ru_RU.po | 2140 ++++++++++++++++++++++++++++++++++++++++++
po/zh_TW.po | 2105 +++++++++++++++++++++++++++++++++++++++++
10 files changed, 17692 insertions(+), 1 deletion(-)
mode change 120000 => 100644 plugins/sample/po/LINGUAS
create mode 100644 po/de_DE.po
create mode 100644 po/es_ES.po
create mode 100644 po/fr_FR.po
create mode 100644 po/it_IT.po
create mode 100644 po/ja_JP.po
create mode 100644 po/ko_KR.po
create mode 100644 po/ru_RU.po
create mode 100644 po/zh_TW.po
--
1.9.3
2
11
Recently the ticket information was added to the vm to report the
graphics password and its expiration time.
That way, graphics information was found in 2 different data structuce:
some information under "graphics" and other ones under "ticket".
{
"graphics":{
"type":"vnc",
"port":null,
"listen":"127.0.0.1"
},
"ticket":{
"passwd":null,
"expire":null
},
"name":"Ubuntu14.04",
"uuid":"8522d82d-d51b-436f-88ff-aff1fed27613",
"access":"full",
"state":"shutoff",
"memory":1024
...
}
As "ticket" data structure only contains graphics information it makes
more sense to centralize it under "graphics" and also use the same
terminology used by libvirt (passwd and passwdValidTo)
"graphics":{
"type":"vnc",
"port":null,
"listen":"127.0.0.1"
"passwd":null,
"passwdValidTo":null
},
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
docs/API.md | 14 +++---
src/kimchi/API.json | 12 ++---
src/kimchi/control/vms.py | 2 +-
src/kimchi/i18n.py | 4 +-
src/kimchi/mockmodel.py | 38 +++++++++-------
src/kimchi/model/vms.py | 112 ++++++++++++++++++++++------------------------
tests/test_mockmodel.py | 2 +-
tests/test_model.py | 19 ++++----
tests/test_rest.py | 13 +++---
9 files changed, 111 insertions(+), 105 deletions(-)
diff --git a/docs/API.md b/docs/API.md
index d75c55f..7d5e804 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -94,9 +94,8 @@ the following general conventions:
* port: The real port number of the graphics, vnc or spice. Users
can use this port to connect to the vm with general vnc/spice
clients.
- * ticket: A ticket represents credentials to access VM.
- * password: the password of a ticket.
- * expire: lifetime of a ticket.
+ * passwd: console password
+ * passwdValidTo: lifetime for the console password.
* users: A list of system users who have permission to access the VM.
Default is: empty (i.e. only root-users may access).
* groups: A list of system groups whose users have permission to access
@@ -110,9 +109,12 @@ the following general conventions:
will take effect in next reboot)
* memory: New amount of memory (MB) for this VM (if VM is running, new
value will take effect in next reboot)
- * ticket: A ticket represents credentials to access VM.
- * password *(optional)*: the password of a ticket.
- * expire *(optional)*: lifetime of a ticket.
+ * graphics: A dict to show detail of VM graphics.
+ * passwd *(optional)*: console password. When omitted a random password
+ willbe generated.
+ * passwdValidTo *(optional)*: lifetime for the console password. When
+ omitted the password will be valid just
+ for 30 seconds.
* **POST**: *See Virtual Machine Actions*
diff --git a/src/kimchi/API.json b/src/kimchi/API.json
index c3fc5e3..67612f2 100644
--- a/src/kimchi/API.json
+++ b/src/kimchi/API.json
@@ -235,17 +235,17 @@
"error": "KCHVM0026E"
}
},
- "ticket": {
- "description": "A ticket represents credentials to access VM",
+ "graphics": {
+ "description": "Graphics information from guest",
"type": "object",
"properties": {
- "password": {
- "description": "the password of a ticket.",
+ "passwd": {
+ "description": "New graphics password.",
"type": "string",
"error": "KCHVM0031E"
},
- "size": {
- "description": "lifetime of a ticket.",
+ "passwdValidTo": {
+ "description": "Life time for the graphics password.",
"type": "number",
"error": "KCHVM0032E"
}
diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
index 6632e52..88d8a81 100644
--- a/src/kimchi/control/vms.py
+++ b/src/kimchi/control/vms.py
@@ -36,7 +36,7 @@ class VM(Resource):
super(VM, self).__init__(model, ident)
self.role_key = 'guests'
self.update_params = ["name", "users", "groups", "cpus", "memory",
- "ticket"]
+ "graphics"]
self.screenshot = VMScreenShot(model, ident)
self.uri_fmt = '/vms/%s'
for ident, node in sub_nodes.items():
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index 2eae7e8..c276b38 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -94,8 +94,8 @@ messages = {
"KCHVM0028E": _("Group(s) '%(groups)s' do not exist"),
"KCHVM0029E": _("Unable to shutdown virtual machine %(name)s. Details: %(err)s"),
"KCHVM0030E": _("Unable to get access metadata of virtual machine %(name)s. Details: %(err)s"),
- "KCHVM0031E": _("password of a vm ticket must be a string."),
- "KCHVM0032E": _("expire of a vm ticket must be a number."),
+ "KCHVM0031E": _("The guest console password must be a string."),
+ "KCHVM0032E": _("The life time for the guest console password must be a number."),
"KCHVMIF0001E": _("Interface %(iface)s does not exist in virtual machine %(name)s"),
"KCHVMIF0002E": _("Network %(network)s specified for virtual machine %(name)s does not exist"),
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py
index fc474de..1322def 100644
--- a/src/kimchi/mockmodel.py
+++ b/src/kimchi/mockmodel.py
@@ -91,15 +91,6 @@ class MockModel(object):
def _static_vm_update(self, dom, params):
state = dom.info['state']
- if 'ticket' in params:
- ticket = params.pop('ticket')
- passwd = ticket.get('passwd')
- dom.ticket["passwd"] = passwd if passwd is not None else "".join(
- random.sample(string.ascii_letters + string.digits, 8))
- expire = ticket.get('expire')
- dom.ticket["ValidTo"] = expire if expire is None else round(
- time.time()) + expire
-
for key, val in params.items():
if key == 'name':
if state == 'running' or params['name'] in self.vms_get_list():
@@ -126,7 +117,21 @@ class MockModel(object):
dom.info[key] = val
def _live_vm_update(self, dom, params):
- pass
+ if 'graphics' not in params:
+ return
+
+ graphics = params.pop('graphics')
+ passwd = graphics.get('passwd')
+ if passwd is None:
+ passwd = "".join(random.sample(string.ascii_letters +
+ string.digits, 8))
+
+ expire = graphics.get('passwdValidTo')
+ if expire is not None:
+ expire = round(time.time()) + expire
+
+ dom.info['graphics']["passwd"] = passwd
+ dom.info['graphics']["passwdValidTo"] = expire
def vm_update(self, name, params):
dom = self._get_vm(name)
@@ -141,10 +146,11 @@ class MockModel(object):
vm.info['screenshot'] = self.vmscreenshot_lookup(name)
else:
vm.info['screenshot'] = None
- vm.info['ticket'] = {"passwd": vm.ticket["passwd"]}
- validTo = vm.ticket["ValidTo"]
- vm.info['ticket']["expire"] = (validTo - round(time.time())
- if validTo is not None else None)
+
+ validTo = vm.info['graphics']['passwdValidTo']
+ validTo = (validTo - round(time.time()) if validTo is not None
+ else None)
+ vm.info['graphics']['passwdValidTo'] = validTo
return vm.info
def vm_delete(self, name):
@@ -1050,7 +1056,6 @@ class MockVM(object):
self.networks = template_info['networks']
ifaces = [MockVMIface(net) for net in self.networks]
self.storagedevices = {}
- self.ticket = {"passwd": "123456", "ValidTo": None}
self.ifaces = dict([(iface.info['mac'], iface) for iface in ifaces])
stats = {'cpu_utilization': 20,
@@ -1066,7 +1071,8 @@ class MockVM(object):
'cpus': self.cpus,
'icon': None,
'graphics': {'type': 'vnc', 'listen': '127.0.0.1',
- 'port': None},
+ 'port': None, 'passwd': '123456',
+ 'passwdValidTo': None},
'users': ['user1', 'user2', 'root'],
'groups': ['group1', 'group2', 'admin'],
'access': 'full'
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index 476e4ac..0a05b76 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -251,12 +251,6 @@ class VMModel(object):
self.groups = import_class('kimchi.model.host.GroupsModel')(**kargs)
def update(self, name, params):
- if 'ticket' in params:
- password = params['ticket'].get("passwd")
- password = password if password is not None else "".join(
- random.sample(string.ascii_letters + string.digits, 8))
- params['ticket']['passwd'] = password
-
dom = self.get_vm(name, self.conn)
dom = self._static_vm_update(dom, params)
self._live_vm_update(dom, params)
@@ -315,14 +309,15 @@ class VMModel(object):
os_elem = E.os({"distro": distro, "version": version})
set_metadata_node(dom, os_elem)
- def _set_ticket(self, xml, params, flag=0):
+ def _set_graphics_passwd(self, xml, params, flag=0):
DEFAULT_VALID_TO = 30
password = params.get("passwd")
- expire = params.get("expire")
+ expire = params.get("passwdValidTo")
root = objectify.fromstring(xml)
graphic = root.devices.find("graphics")
if graphic is None:
return None
+
graphic.attrib['passwd'] = password
to = graphic.attrib.get('passwdValidTo')
if to is not None:
@@ -337,27 +332,27 @@ class VMModel(object):
return root if flag == 0 else graphic
- def _set_persistent_ticket(self, dom, xml, params):
- if 'ticket' not in params:
- # store the password for copy
- ticket = self._get_ticket(dom)
- if ticket['passwd'] is None:
- return xml
- else:
- params['ticket'] = ticket
- node = self._set_ticket(xml, params['ticket'])
- return xml if node is None else ET.tostring(node, encoding="utf-8")
+ def _update_graphics(self, dom, xml, params):
+ if 'graphics' not in params:
+ return xml
- def _set_live_ticket(self, dom, params):
- if 'ticket' not in params:
- return
- if dom.isActive():
- xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE)
- flag = libvirt.VIR_DOMAIN_XML_SECURE
- node = self._set_ticket(xml, params['ticket'], flag)
- if node is not None:
- dom.updateDeviceFlags(etree.tostring(node),
- libvirt.VIR_DOMAIN_AFFECT_LIVE)
+ password = params['graphics'].get("passwd")
+ if password is None:
+ password = "".join(random.sample(string.ascii_letters +
+ string.digits, 8))
+ params['graphics']['passwd'] = password
+
+ if not dom.isActive():
+ node = self._set_graphics_passwd(xml, params['graphics'])
+ return xml if node is None else ET.tostring(node, encoding="utf-8")
+
+ xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE)
+ flag = libvirt.VIR_DOMAIN_XML_SECURE
+ node = self._set_graphics_passwd(xml, params['graphics'], flag)
+ if node is not None:
+ dom.updateDeviceFlags(etree.tostring(node),
+ libvirt.VIR_DOMAIN_AFFECT_LIVE)
+ return xml
def _static_vm_update(self, dom, params):
state = DOM_STATE_MAP[dom.info()[0]]
@@ -374,7 +369,7 @@ class VMModel(object):
xpath = VM_STATIC_UPDATE_PARAMS[key]
new_xml = xmlutils.xml_item_update(new_xml, xpath, val)
- new_xml = self._set_persistent_ticket(dom, new_xml, params)
+ new_xml = self._update_graphics(dom, new_xml, params)
conn = self.conn.get()
try:
@@ -397,35 +392,19 @@ class VMModel(object):
def _live_vm_update(self, dom, params):
self._vm_update_access_metadata(dom, params)
- self._set_live_ticket(dom, params)
def _has_video(self, dom):
dom = ElementTree.fromstring(dom.XMLDesc(0))
return dom.find('devices/video') is not None
- def _get_ticket(self, dom):
- xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE)
- root = objectify.fromstring(xml)
- graphic = root.devices.find("graphics")
- if graphic is None:
- return {"passwd": None, "expire": None}
- passwd = graphic.attrib.get('passwd')
- ticket = {"passwd": passwd}
- valid_to = graphic.attrib.get('passwdValidTo')
- ticket['expire'] = None
- if valid_to is not None:
- to = time.mktime(time.strptime(valid_to, '%Y-%m-%dT%H:%M:%S'))
- ticket['expire'] = to - time.mktime(time.gmtime())
-
- return ticket
-
def lookup(self, name):
dom = self.get_vm(name, self.conn)
info = dom.info()
state = DOM_STATE_MAP[info[0]]
screenshot = None
+ # (type, listen, port, passwd, passwdValidTo)
graphics = self._vm_get_graphics(name)
- graphics_type, graphics_listen, graphics_port = graphics
+ graphics_port = graphics[2]
graphics_port = graphics_port if state == 'running' else None
try:
if state == 'running' and self._has_video(dom):
@@ -465,10 +444,12 @@ class VMModel(object):
'cpus': info[3],
'screenshot': screenshot,
'icon': icon,
- 'graphics': {"type": graphics_type,
- "listen": graphics_listen,
- "port": graphics_port},
- 'ticket': self._get_ticket(dom),
+ # (type, listen, port, passwd, passwdValidTo)
+ 'graphics': {"type": graphics[0],
+ "listen": graphics[1],
+ "port": graphics_port,
+ "passwd": graphics[3],
+ "passwdValidTo": graphics[4]},
'users': users,
'groups': groups,
'access': 'full'
@@ -567,23 +548,38 @@ class VMModel(object):
def _vm_get_graphics(self, name):
dom = self.get_vm(name, self.conn)
- xml = dom.XMLDesc(0)
+ xml = dom.XMLDesc(libvirt.VIR_DOMAIN_XML_SECURE)
+
expr = "/domain/devices/graphics/@type"
res = xmlutils.xpath_get_text(xml, expr)
graphics_type = res[0] if res else None
+
expr = "/domain/devices/graphics/@listen"
res = xmlutils.xpath_get_text(xml, expr)
graphics_listen = res[0] if res else None
- graphics_port = None
+
+ graphics_port = graphics_passwd = graphics_passwdValidTo = None
if graphics_type:
- expr = "/domain/devices/graphics[@type='%s']/@port" % graphics_type
- res = xmlutils.xpath_get_text(xml, expr)
+ expr = "/domain/devices/graphics[@type='%s']/@port"
+ res = xmlutils.xpath_get_text(xml, expr % graphics_type)
graphics_port = int(res[0]) if res else None
- return graphics_type, graphics_listen, graphics_port
+
+ expr = "/domain/devices/graphics[@type='%s']/@passwd"
+ res = xmlutils.xpath_get_text(xml, expr % graphics_type)
+ graphics_passwd = res[0] if res else None
+
+ expr = "/domain/devices/graphics[@type='%s']/@passwdValidTo"
+ res = xmlutils.xpath_get_text(xml, expr % graphics_type)
+ if res:
+ to = time.mktime(time.strptime(res[0], '%Y-%m-%dT%H:%M:%S'))
+ graphics_passwdValidTo = to - time.mktime(time.gmtime())
+
+ return (graphics_type, graphics_listen, graphics_port,
+ graphics_passwd, graphics_passwdValidTo)
def connect(self, name):
- graphics = self._vm_get_graphics(name)
- graphics_type, graphics_listen, graphics_port = graphics
+ # (type, listen, port, passwd, passwdValidTo)
+ graphics_port = self._vm_get_graphics(name)[2]
if graphics_port is not None:
vnc.add_proxy_token(name, graphics_port)
else:
diff --git a/tests/test_mockmodel.py b/tests/test_mockmodel.py
index b7e6a48..8de7ce9 100644
--- a/tests/test_mockmodel.py
+++ b/tests/test_mockmodel.py
@@ -143,7 +143,7 @@ class MockModelTests(unittest.TestCase):
keys = set(('name', 'state', 'stats', 'uuid', 'memory', 'cpus',
'screenshot', 'icon', 'graphics', 'users', 'groups',
- 'access', 'ticket'))
+ 'access'))
stats_keys = set(('cpu_utilization',
'net_throughput', 'net_throughput_peak',
diff --git a/tests/test_model.py b/tests/test_model.py
index f5b13d0..5090395 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -65,7 +65,7 @@ class ModelTests(unittest.TestCase):
keys = set(('name', 'state', 'stats', 'uuid', 'memory', 'cpus',
'screenshot', 'icon', 'graphics', 'users', 'groups',
- 'access', 'ticket'))
+ 'access'))
stats_keys = set(('cpu_utilization',
'net_throughput', 'net_throughput_peak',
@@ -764,24 +764,25 @@ class ModelTests(unittest.TestCase):
vms = inst.vms_get_list()
self.assertTrue('kimchi-vm1' in vms)
- # update vm ticket when vm is not running
+ # update vm graphics when vm is not running
inst.vm_update(u'kimchi-vm1',
- {"ticket": {"passwd": "123456"}})
+ {"graphics": {"passwd": "123456"}})
inst.vm_start('kimchi-vm1')
rollback.prependDefer(self._rollback_wrapper, inst.vm_poweroff,
'kimchi-vm1')
vm_info = inst.vm_lookup(u'kimchi-vm1')
- self.assertEquals('123456', vm_info['ticket']["passwd"])
- self.assertEquals(None, vm_info['ticket']["expire"])
+ self.assertEquals('123456', vm_info['graphics']["passwd"])
+ self.assertEquals(None, vm_info['graphics']["passwdValidTo"])
- # update vm ticket when vm is running
+ # update vm graphics when vm is running
inst.vm_update(u'kimchi-vm1',
- {"ticket": {"passwd": "abcdef", "expire": 20}})
+ {"graphics": {"passwd": "abcdef",
+ "passwdValidTo": 20}})
vm_info = inst.vm_lookup(u'kimchi-vm1')
- self.assertEquals('abcdef', vm_info['ticket']["passwd"])
- self.assertGreaterEqual(20, vm_info['ticket']['expire'])
+ self.assertEquals('abcdef', vm_info['graphics']["passwd"])
+ self.assertGreaterEqual(20, vm_info['graphics']['passwdValidTo'])
info = inst.vm_lookup('kimchi-vm1')
self.assertEquals('running', info['state'])
diff --git a/tests/test_rest.py b/tests/test_rest.py
index 2117399..0bf5997 100644
--- a/tests/test_rest.py
+++ b/tests/test_rest.py
@@ -221,20 +221,21 @@ class RestTests(unittest.TestCase):
resp = self.request('/vms/vm-1', req, 'PUT')
self.assertEquals(200, resp.status)
- req = json.dumps({"ticket": {'passwd': "abcdef"}})
+ req = json.dumps({"graphics": {'passwd': "abcdef"}})
resp = self.request('/vms/vm-1', req, 'PUT')
info = json.loads(resp.read())
- self.assertEquals('abcdef', info["ticket"]["passwd"])
- self.assertEquals(None, info["ticket"]["expire"])
+ self.assertEquals('abcdef', info["graphics"]["passwd"])
+ self.assertEquals(None, info["graphics"]["passwdValidTo"])
resp = self.request('/vms/vm-1/poweroff', '{}', 'POST')
self.assertEquals(200, resp.status)
- req = json.dumps({"ticket": {'passwd': "123456", "expire": 20}})
+ req = json.dumps({"graphics": {'passwd': "123456",
+ 'passwdValidTo': 20}})
resp = self.request('/vms/vm-1', req, 'PUT')
info = json.loads(resp.read())
- self.assertEquals('123456', info["ticket"]["passwd"])
- self.assertGreaterEqual(20, info["ticket"]["expire"])
+ self.assertEquals('123456', info["graphics"]["passwd"])
+ self.assertGreaterEqual(20, info["graphics"]["passwdValidTo"])
req = json.dumps({'name': 12})
resp = self.request('/vms/vm-1', req, 'PUT')
--
1.9.3
2
3
Problems to solve:
When we generate the debug reports (or upload/download a storage
volume), only the request initiator gets task id, and is able to query
task status. We need other users from other browser to have same view as
the initiator, which is not possible now.
Solution:
1. We add async event information, events will also be classified
so that users can subscribe events interest them
2. We enable reverse mapping (task to resource which generate them)
query for users. So that user can find tasks related to a collection:
GET /tasks?init_collection=debugreports
after take a look at current implementation, we can also query
taget_uri
GET /tasks?target_uri=r(^/debugreports/*) (need to be encoded)
Since we currently out of support of async events, we may want to add
task query for this purpose.
Welcome comments!
2
1
Adding the 'Deutsch (Deutschland)' German, 'Español (España)'
Spanish, 'Français (France)' French, 'Italiano (Italia)' Italian,
'日本語 (日本)' Japanese, '한국어 (대한민국)' Korean,
'Русский (Россия)' Russian and '中文(繁體)' Traditional Chinese
languages as options in the login page to be selected before use
Kimchi.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.lang.js | 10 +++++++++-
ui/pages/login.html.tmpl | 8 ++++++++
2 files changed, 17 insertions(+), 1 deletion(-)
diff --git a/ui/js/src/kimchi.lang.js b/ui/js/src/kimchi.lang.js
index da55307..1d2de4e 100644
--- a/ui/js/src/kimchi.lang.js
+++ b/ui/js/src/kimchi.lang.js
@@ -20,7 +20,15 @@ kimchi.lang = {
return {
'en_US': 'English (US)',
'zh_CN': '中文(简体)',
- 'pt_BR': 'Português (Brasil)'
+ 'pt_BR': 'Português (Brasil)',
+ 'de_DE': 'Deutsch (Deutschland)',
+ 'es_ES': 'Español (España)',
+ 'fr_FR': 'Français (France)',
+ 'it_IT': 'Italiano (Italia)',
+ 'ja_JP': '日本語 (日本)',
+ 'ko_KR': '한국어 (대한민국)',
+ 'ru_RU': 'Русский (Россия)',
+ 'zh_TW': '中文(繁體)'
};
},
diff --git a/ui/pages/login.html.tmpl b/ui/pages/login.html.tmpl
index 7d61a6a..d4d5b7e 100644
--- a/ui/pages/login.html.tmpl
+++ b/ui/pages/login.html.tmpl
@@ -65,6 +65,14 @@
<option value="en_US">English (US)</option>
<option value="zh_CN">中文(简体)</option>
<option value="pt_BR">Português (Brasil)</option>
+ <option value="de_DE">Deutsch (Deutschland)</option>
+ <option value="es_ES">Español (España)</option>
+ <option value="fr_FR">Français (France)</option>
+ <option value="it_IT">Italiano (Italia)</option>
+ <option value="ja_JP">日本語 (日本)</option>
+ <option value="ko_KR">한국어 (대한민국)</option>
+ <option value="ru_RU">Русский (Россия)</option>
+ <option value="zh_TW">中文(繁體)</option>
</select>
</div>
<div id="login-window" class="login-area">
--
1.9.3
2
1
V1 -> V2:
- Allow user updates the passwd expiration time without changing the passwd
Aline Manera (2):
Centralize graphics information
Allow user updates the passwd expiration time without changing the
passwd
docs/API.md | 14 +++---
src/kimchi/API.json | 12 ++---
src/kimchi/control/vms.py | 2 +-
src/kimchi/i18n.py | 4 +-
src/kimchi/mockmodel.py | 38 ++++++++------
src/kimchi/model/vms.py | 124 ++++++++++++++++++++--------------------------
tests/test_mockmodel.py | 2 +-
tests/test_model.py | 19 +++----
tests/test_rest.py | 13 ++---
9 files changed, 112 insertions(+), 116 deletions(-)
--
1.9.3
2
4

29 Aug '14
When creating a VM using a iSCSI volume, the volume XML was like below:
<disk type='volume' device='disk'>
<driver name='qemu' type='raw'/>
<source pool='TEST-ISCSI1' volume='unit:0:0:2' mode='host'/>
<target dev='vda' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x05' function='0x0'/>
</disk>
But to attach a new iSCSI volume, the XML is:
<disk type='block' device='disk'>
<driver name='qemu' type='raw'/>
<source dev='/dev/disk/by-id/wwn-0x60000000000000000e00000000010001'/>
<target dev='vdb' bus='virtio'/>
<address type='pci' domain='0x0000' bus='0x00' slot='0x07' function='0x0'/>
</disk>
We should keep the same XML structure in both cases to avoid problems
while listing the storage volumes and getting their information.
To solve that, this patch uses the latter XML also while creating a VM
from a iSCSI volume.
It eliminates the needs to have the authentication setting in the
volume, because that the _get_storage_auth() function was replaced by
_get_volume_path()
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
src/kimchi/mockmodel.py | 3 +++
src/kimchi/model/templates.py | 15 ++++++---------
src/kimchi/model/vms.py | 6 +-----
src/kimchi/vmtemplate.py | 16 +++++-----------
4 files changed, 15 insertions(+), 25 deletions(-)
diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py
index fc474de..1bd54cc 100644
--- a/src/kimchi/mockmodel.py
+++ b/src/kimchi/mockmodel.py
@@ -997,6 +997,9 @@ class MockVMTemplate(VMTemplate):
pool = self._storage_validate()
return pool.info['path']
+ def _get_volume_path(self, pool, vol):
+ return self.model.storagevolume_lookup(pool, vol)['path']
+
def fork_vm_storage(self, vm_name):
pool = self._storage_validate()
volumes = self.to_volume_list(vm_name)
diff --git a/src/kimchi/model/templates.py b/src/kimchi/model/templates.py
index bf04304..9278cdc 100644
--- a/src/kimchi/model/templates.py
+++ b/src/kimchi/model/templates.py
@@ -29,7 +29,6 @@ from kimchi.kvmusertests import UserTests
from kimchi.utils import pool_name_from_uri
from kimchi.utils import probe_file_permission_as_user
from kimchi.vmtemplate import VMTemplate
-from lxml import objectify
class TemplatesModel(object):
@@ -243,15 +242,13 @@ class LibvirtVMTemplate(VMTemplate):
xml = pool.XMLDesc(0)
return xmlutils.xpath_get_text(xml, "/pool/@type")[0]
- def _get_storage_auth(self):
+ def _get_volume_path(self, pool, vol):
pool = self._storage_validate()
- xml = pool.XMLDesc(0)
- root = objectify.fromstring(xml)
- auth = root.source.find("auth")
- if auth is None:
- return auth
- au = auth.attrib
- return au.update(auth.secret.attrib)
+ try:
+ return pool.storageVolLookupByName(vol).path()
+ except:
+ raise NotFoundError("KCHVOL0002E", {'name': vol,
+ 'pool': pool})
def fork_vm_storage(self, vm_uuid):
# Provision storage:
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index 5721b48..4787840 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -203,11 +203,7 @@ class VMsModel(object):
# If storagepool is SCSI, volumes will be LUNs and must be passed by
# the user from UI or manually.
vol_list = []
- if t._get_storage_type() in ["iscsi", "scsi"]:
- # FIXME: iscsi and scsi storage work with base image needs to be
- # fixed.
- vol_list = []
- else:
+ if t._get_storage_type() not in ["iscsi", "scsi"]:
vol_list = t.fork_vm_storage(vm_uuid)
graphics = params.get('graphics')
diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py
index ddb1f35..c5bb7b3 100644
--- a/src/kimchi/vmtemplate.py
+++ b/src/kimchi/vmtemplate.py
@@ -251,25 +251,19 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/>
def _get_iscsi_disks_xml(self):
def build_disk_xml(children=[]):
- disk = E.disk(type='volume', device='disk')
+ disk = E.disk(type='block', device='disk')
disk.extend(children)
return etree.tostring(disk)
ret = ""
children = []
- auth = self._get_storage_auth()
- if auth:
- etree_auth = E.auth(username=auth['username'])
- etree_auth.append(E.secret(type=auth['type'],
- usage=auth['libvirtiscsi']))
- children.append(etree_auth)
children.append(E.driver(name='qemu', type='raw'))
disk_bus = self.info['disk_bus']
dev_prefix = self._bus_to_dev[disk_bus]
pool_name = pool_name_from_uri(self.info['storagepool'])
for i, d in enumerate(self.info['disks']):
- source = E.source(pool=pool_name,
- volume=d.get('volume'), mode='host')
+ source = E.source(dev=self._get_volume_path(pool_name,
+ d.get('volume')))
# FIXME if more than 26 disks
target = E.target(dev=dev_prefix + string.lowercase[i],
bus=disk_bus)
@@ -455,8 +449,8 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/>
def _get_storage_type(self):
return ''
- def _get_storage_auth(self):
- return None
+ def _get_volume_path(self):
+ return ''
def _get_all_networks_name(self):
return []
--
1.9.3
2
2
V1 -> V2:
* fixed wrong python variables in translated strings
* removed string without msgid
V1:
This patch set adds new languages to improve Kimchi's i18n support. The
new languages are: German (de_DE), Spanish (es_ES), Franch (fr_FR),
Italian (it_IT), Japanese (ja_JP), Korean (ko_KR), Russian (ru_RU) and
the Traditional Chinese (zh_TW).
Also, the patch set removed the symbolic link of plugins/sample/po/LINGUAS
to be a 'real' file containing only the three first languages in the
sample plugin.
Paulo Vital (9):
i18n support: Changed the file type of plugins/sample/po/LINGUAS
i18n support: Add German translation files.
i18n support: Add Spanish translation files.
i18n support: Add French translation files.
i18n support: Add Italian translation files.
i18n support: Add Japanese translation files.
18n support: Add Korean translation files.
i18n support: Add Russian translation files.
i18n support: Add Traditional Chinese translation files.
plugins/sample/po/LINGUAS | 4 +-
po/LINGUAS | 8 +
po/de_DE.po | 2256 ++++++++++++++++++++++++++++++++++++++++++++
po/es_ES.po | 2281 ++++++++++++++++++++++++++++++++++++++++++++
po/fr_FR.po | 2285 +++++++++++++++++++++++++++++++++++++++++++++
po/it_IT.po | 2248 ++++++++++++++++++++++++++++++++++++++++++++
po/ja_JP.po | 2227 +++++++++++++++++++++++++++++++++++++++++++
po/ko_KR.po | 2137 ++++++++++++++++++++++++++++++++++++++++++
po/ru_RU.po | 2138 ++++++++++++++++++++++++++++++++++++++++++
po/zh_TW.po | 2054 ++++++++++++++++++++++++++++++++++++++++
10 files changed, 17637 insertions(+), 1 deletion(-)
mode change 120000 => 100644 plugins/sample/po/LINGUAS
create mode 100644 po/de_DE.po
create mode 100644 po/es_ES.po
create mode 100644 po/fr_FR.po
create mode 100644 po/it_IT.po
create mode 100644 po/ja_JP.po
create mode 100644 po/ko_KR.po
create mode 100644 po/ru_RU.po
create mode 100644 po/zh_TW.po
--
1.9.3
3
12
The en_US.po file has the same message string to two different message
ID's. The two message ID's are used in different situations and linked
to different messages codes in i18n.pu file. Changed to use the same
content of msgid in msgstr.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
po/en_US.po | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/po/en_US.po b/po/en_US.po
index ef4b127..1cef680 100644
--- a/po/en_US.po
+++ b/po/en_US.po
@@ -856,8 +856,8 @@ msgid ""
"Only one of path or pool/volume can be specified to add a new virtual "
"machine disk"
msgstr ""
-"Specify type and path or type and pool/volume to add a new virtual machine "
-"disk"
+"Only one of path or pool/volume can be specified to add a new virtual "
+"machine disk"
msgid "YUM Repository ID must be one word only string."
msgstr "YUM Repository ID must be one word only string."
--
1.9.3
3
2
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/storage.css | 69 +++++++++++++++++++++-
ui/js/src/kimchi.api.js | 30 ++++++++++
ui/js/src/kimchi.storagepool_add_main.js | 94 ++++++++++++++++++++++++++++++
ui/pages/storagepool-add.html.tmpl | 12 +++-
4 files changed, 201 insertions(+), 4 deletions(-)
diff --git a/ui/css/theme-default/storage.css b/ui/css/theme-default/storage.css
index 4f439e8..1b71d82 100644
--- a/ui/css/theme-default/storage.css
+++ b/ui/css/theme-default/storage.css
@@ -315,7 +315,7 @@
}
.hide-content {
- display: none;
+ display: none!important;
}
.volumeslist {
@@ -593,3 +593,70 @@
center no-repeat;
padding: 0 20px 0 26px;
}
+
+.storage-admin .filter-select {
+ display: inline-block;
+ position: relative;
+}
+
+#iscsiportId, .storage-admin .filter-select input {
+ border: 1px solid #CCCCCC;
+ border-radius: 1px;
+ font-size: 14px;
+ padding: 3px 3px 3px 10px;
+ height: 30px;
+}
+
+.storage-admin .filter-select input::-ms-clear {
+ display: none;
+}
+
+.storage-admin .filter-select .arrow {
+ display: inline-block;
+ vertical-align: middle;
+ position: relative;
+ left: -25px;
+}
+
+.storage-admin .filter-select .option {
+ border-style: solid;
+ border-color: #CCCCCC;
+ border-width: 0px 1px 1px 1px;
+ border-radius: 1px;
+ font-size: 14px;
+ background-color: white;
+ max-height: 140px;
+ overflow: auto;
+ color: #666666;
+ position: absolute;
+ z-index: 1000;
+}
+
+.storage-admin .filter-select .option .item {
+ padding: 5px 10px;
+ cursor: pointer;
+}
+
+.storage-admin .filter-select .option .item:hover {
+ background-color: #DDDDDD;
+}
+
+#iSCSIServer input {
+ width: 410px;
+}
+
+#iSCSIServer .option {
+ width: 423px;
+}
+
+#iscsiportId {
+ width: 60px;
+}
+
+#iSCSITarget input {
+ width: 493px;
+}
+
+#iSCSITarget .option {
+ width: 506px;
+}
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 4562992..9de1b08 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1098,5 +1098,35 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ getISCSIServers : function(suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'storageservers?_target_type=iscsi',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ getISCSITargets : function(server, port, suc, err) {
+ server = encodeURIComponent(server);
+ port = encodeURIComponent(port);
+ kimchi.requestJSON({
+ url : kimchi.url + 'storageservers/'+server+'/storagetargets?_target_type=iscsi&_server_port='+port,
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js
index ecbc682..affb714 100644
--- a/ui/js/src/kimchi.storagepool_add_main.js
+++ b/ui/js/src/kimchi.storagepool_add_main.js
@@ -34,6 +34,96 @@ kimchi.storagepool_add_main = function() {
});
};
+kimchi.storageFilterSelect = function(id, isUpdate) {
+ var input = $('input', '#'+id);
+ var options = $(".option", '#'+id);
+ $('.arrow', '#'+id).toggleClass("hide-content", options.children().length==0);
+ var filter = function(container, key){
+ container.children().each(function(){
+ $(this).css("display", $(this).text().indexOf(key)==-1 ? "none" : "");
+ });
+ };
+ if(!isUpdate){
+ $('.arrow', '#'+id).click(function(){
+ options.toggleClass("hide-content");
+ });
+ input.focus(function(){
+ options.removeClass("hide-content");
+ }).on("keyup", function(){
+ filter(options, input.val());
+ });
+ }
+ options.children().each(function(){
+ $(this).click(function(){
+ input.val($(this).text());
+ $('.option', '#'+id).addClass("hide-content");
+ filter(options, "");
+ });
+ });
+};
+
+kimchi.setupISCSI = function(){
+ var loadTargets = function(server, port, callback){
+ var isUpdate = $(".option", "#iSCSITarget").children().length > 0;
+ $(".option", "#iSCSITarget").empty();
+ $('input', "#iSCSITarget").attr("placeholder", "loading targets...");
+ kimchi.getISCSITargets(server, port, function(data){
+ if(data.length==0){
+ $('input', "#iSCSITarget").attr("placeholder", "no targets is got, please input one.");
+ }else{
+ for(var i=0; i<data.length; i++){
+ var itemNode = $.parseHTML("<div class='item'>"+data[i].target+"</div>");
+ $(".option", "#iSCSITarget").append(itemNode);
+ }
+ $('input', "#iSCSITarget").attr("placeholder", "");
+ }
+ kimchi.storageFilterSelect('iSCSITarget', isUpdate);
+ $('input', "#iSCSITarget").trigger("focus");
+ callback();
+ }, function(data){
+ $('input', "#iSCSITarget").attr("placeholder","failed to load targets.");
+ callback();
+ kimchi.message.error(data.responseJSON.reason);
+ });
+ };
+ var triggerLoadTarget = function(){
+ $('input', "#iSCSITarget").val("");
+ var server = $("#iscsiserverId").val().trim();
+ var port = $("#iscsiportId").val().trim();
+ if(server!="" && port!="" && !$("#iscsiserverId").hasClass("invalid-field") && !$("#iscsiportId").hasClass("invalid-field")){
+ $("#iscsiserverId").attr("disabled", true);
+ $("#iscsiportId").attr("disabled", true);
+ $('.arrow', '#iSCSIServer').addClass("hide-content");
+ loadTargets(server, port, function(){
+ $("#iscsiserverId").attr("disabled", false);
+ $("#iscsiportId").attr("disabled", false);
+ $('.arrow', '#iSCSIServer').removeClass("hide-content");
+ });
+ }
+ };
+ $("#iscsiserverId").change(function(){
+ triggerLoadTarget();
+ });
+ $("#iscsiportId").change(function(){
+ triggerLoadTarget();
+ });
+ var initISCSIServers = function(){
+ kimchi.getISCSIServers(function(data){
+ for(var i=0;i<data.length;i++){
+ var itemNode = $.parseHTML("<div class='item'>"+data[i].host+"</div>");
+ $(".option", "#iSCSIServer").append(itemNode);
+ $(itemNode).click(function(){
+ $("#iscsiportId").val($(this).prop("port"));
+ $("#iscsiserverId").val($(this).text());
+ triggerLoadTarget();
+ }).prop("port", data[i].port);
+ }
+ kimchi.storageFilterSelect('iSCSIServer', false);
+ });
+ };
+ initISCSIServers();
+};
+
kimchi.initStorageAddPage = function() {
kimchi.listHostPartitions(function(data) {
if (data.length > 0) {
@@ -160,6 +250,10 @@ kimchi.initStorageAddPage = function() {
$('#iscsiportId').keyup(function(event) {
$(this).toggleClass("invalid-field",!/^[0-9]+$/.test($(this).val()));
});
+ $('#iscsiserverId').keyup(function(event) {
+ $(this).toggleClass("invalid-field",!kimchi.isServer($(this).val().trim()));
+ });
+ kimchi.setupISCSI();
};
/* Returns 'true' if all form fields were filled, 'false' if
diff --git a/ui/pages/storagepool-add.html.tmpl b/ui/pages/storagepool-add.html.tmpl
index 1eb2029..6b526a7 100644
--- a/ui/pages/storagepool-add.html.tmpl
+++ b/ui/pages/storagepool-add.html.tmpl
@@ -23,7 +23,7 @@
<!DOCTYPE html>
<html>
<body>
- <div class="window storage-window">
+ <div class="window storage-window storage-admin">
<header>
<h1 class="title">$_("Define a New Storage Pool")</h1>
<div class="close">X</div>
@@ -111,7 +111,10 @@
<div class="field">
<p class="text-help">
$_("iSCSI server IP or hostname. It should not be empty.")</p>
- <input id="iscsiserverId" placeholder="$_("Server")" type="text" class="text storage-base-input-width">
+ <span class="filter-select" id="iSCSIServer">
+ <div><input id="iscsiserverId" type="text" placeholder="$_("Server")"><a class="ui-icon ui-icon-triangle-1-s arrow hide-content"></a></div>
+ <div class="option hide-content"></div>
+ </span>
<input id="iscsiportId" placeholder="$_("Port")" type="text" class="text storage-port-width" maxlength="4">
</div>
</section>
@@ -119,7 +122,10 @@
<h2>4. $_("Target")</h2>
<div class="field">
<p class="text-help">$_("The iSCSI target on iSCSI server")</p>
- <input id="iscsiTargetId" type="text" class="text storage-base-input-width">
+ <span class="filter-select" id="iSCSITarget">
+ <div><input id="iscsiTargetId" type="text"><a class="ui-icon ui-icon-triangle-1-s arrow hide-content"></a></div>
+ <div class="option hide-content"></div>
+ </span>
</div>
</section>
<section class="form-section">
--
1.7.1
2
1
v1 -> v2:
* Re-wrote commit message for patch 2/5 (now 2/4) with 80 characters/line
* Joined patches 3/5 and 4/5 in only one (now 3/4) about sosreport support
V1:
This patch set enables Power(ppc) support by default on Kimchi.
Whithout them, the upstream code of Kimchi is not fully compatible
with the Power architecture.
Eli Qiao (1):
Support tablet type as input device in VM's XML.
Mark Wu (1):
Add sos plugin for kimchi
Paulo Vital (2):
PowerKVM: Workaround of numpy byte order bug on PowerPC
Add PowerKVM information as ISO otpion to installation.
contrib/kimchi.spec.fedora.in | 3 +++
src/kimchi/Makefile.am | 4 +++-
src/kimchi/isoinfo.py | 1 +
src/kimchi/osinfo.py | 5 +++--
src/kimchi/sos.py | 37 +++++++++++++++++++++++++++++++++++++
src/kimchi/vmtemplate.py | 5 +++++
src/kimchi/websocket.py | 7 +++++--
7 files changed, 57 insertions(+), 5 deletions(-)
create mode 100644 src/kimchi/sos.py
--
1.9.3
3
10
From: Mark Wu <wudxw(a)linux.vnet.ibm.com>
This patches adds a sos plugin to collect kimchi's configuration,
log files and other information for diagnosis.
Signed-off-by: Mark Wu <wudxw(a)linux.vnet.ibm.com>
Signed-off-by: Eli Qiao <taget(a)linux.vnet.ibm.com>
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
Makefile.am | 1 +
contrib/kimchi.spec.fedora.in | 3 +++
src/kimchi/Makefile.am | 4 +++-
src/kimchi/sos.py | 43 +++++++++++++++++++++++++++++++++++++++++++
4 files changed, 50 insertions(+), 1 deletion(-)
create mode 100644 src/kimchi/sos.py
diff --git a/Makefile.am b/Makefile.am
index 3293d9e..8776760 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -69,6 +69,7 @@ PEP8_WHITELIST = \
src/kimchi/root.py \
src/kimchi/scan.py \
src/kimchi/server.py \
+ src/kimchi/sos.py \
src/kimchi/swupdate.py \
src/kimchi/template.py \
src/kimchi/utils.py \
diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in
index 5766784..78dd423 100644
--- a/contrib/kimchi.spec.fedora.in
+++ b/contrib/kimchi.spec.fedora.in
@@ -78,6 +78,8 @@ make
%install
rm -rf %{buildroot}
make DESTDIR=%{buildroot} install
+install -Dm 0644 src/kimchi/sos.py \
+ %{buildroot}/%{python_sitelib}/sos/plugins/kimchi.py
%if 0%{?with_systemd}
# Install the systemd scripts
@@ -158,6 +160,7 @@ rm -rf $RPM_BUILD_ROOT
%{python_sitelib}/kimchi/model/*.py*
%{python_sitelib}/kimchi/API.json
%{python_sitelib}/kimchi/plugins/*.py*
+%{python_sitelib}/sos/plugins/kimchi.py*
%{_datadir}/kimchi/doc/API.md
%{_datadir}/kimchi/doc/README.md
%{_datadir}/kimchi/doc/kimchi-guest.png
diff --git a/src/kimchi/Makefile.am b/src/kimchi/Makefile.am
index e2b5bea..a405782 100644
--- a/src/kimchi/Makefile.am
+++ b/src/kimchi/Makefile.am
@@ -19,10 +19,12 @@
SUBDIRS = control model
-kimchi_PYTHON = $(filter-out config.py, $(wildcard *.py))
+kimchi_PYTHON = $(filter-out config.py sos.py, $(wildcard *.py))
nodist_kimchi_PYTHON = config.py
+dist_noinst_PYTHON = sos.py
+
EXTRA_DIST = \
API.json \
config.py.in
diff --git a/src/kimchi/sos.py b/src/kimchi/sos.py
new file mode 100644
index 0000000..5d1fa3f
--- /dev/null
+++ b/src/kimchi/sos.py
@@ -0,0 +1,43 @@
+#
+# Project Kimchi
+#
+# Copyright IBM, Corp. 2014
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+from sos.plugins import Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin
+from sos.utilities import sos_get_command_output
+
+
+class Kimchi(Plugin, RedHatPlugin, UbuntuPlugin, DebianPlugin):
+ """
+ kimchi-related information
+ """
+
+ plugin_name = 'kimchi'
+
+ def setup(self):
+ self.add_copy_specs([
+ "/etc/kimchi/",
+ "/var/log/kimchi*"
+ ])
+ self.add_cmd_output("virsh pool-list --details")
+ rc, out, _ = sos_get_command_output('virsh pool-list')
+ if rc == 0:
+ for pool in out.splitlines()[2:]:
+ if pool:
+ pool_name = pool.split()[0]
+ self.add_cmd_output("virsh vol-list --pool %s --details"
+ % pool_name)
--
1.9.3
2
1
When a guest has an ISCSI volume attached as disk, backend is not
returning path correctly, it return a blank field. This patch fixes
this problem, returning the path = <iscsi pool> - <volume unit>.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
src/kimchi/vmdisks.py | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/kimchi/vmdisks.py b/src/kimchi/vmdisks.py
index 6012ada..6835172 100644
--- a/src/kimchi/vmdisks.py
+++ b/src/kimchi/vmdisks.py
@@ -53,6 +53,9 @@ def get_vm_disk(dom, dev_name):
path = (source.attrib['protocol'] + '://' +
host.attrib['name'] + ':' +
host.attrib['port'] + source.attrib['name'])
+ # ISCSI, Fibre Channel
+ elif src_type == 'volume':
+ path = source.attrib['pool'] + ' - ' + source.attrib['volume']
else:
path = source.attrib[DEV_TYPE_SRC_ATTR_MAP[src_type]]
# Retrieve storage bus type
--
1.9.3
2
4
Hi all,
I recently added Kimchi translation file to Transifex.
Transifex is a collaborative tool for translation. Its message is clear:
"100+ languages? Only a few developers? No problem."
I think that way we can support more and more languages in Kimchi once
people get involved.
https://www.transifex.com/organization/kimchi/dashboard
Please, join the Kimchi translation team on Transifex and share it
globally. :-D
Regards,
Aline Manera
2
1
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/topbar.css | 37 ++++++++++++++++++++++++++++++++++++-
ui/js/src/kimchi.api.js | 14 ++++++++++++++
ui/js/src/kimchi.main.js | 13 +++++++++++++
ui/pages/kimchi-ui.html.tmpl | 7 +++++++
4 files changed, 70 insertions(+), 1 deletions(-)
diff --git a/ui/css/theme-default/topbar.css b/ui/css/theme-default/topbar.css
index bed4e19..8f2f79a 100644
--- a/ui/css/theme-default/topbar.css
+++ b/ui/css/theme-default/topbar.css
@@ -114,7 +114,7 @@
cursor: pointer;
display: block;
position: relative;
- height: 48px;
+ height: 52px;
margin: 0 12px;
}
@@ -174,3 +174,38 @@ a#btn-logout:hover {
display: none;
}
}
+
+.peers {
+ color: white;
+ cursor: pointer;
+ height: 52px;
+ margin: 0 12px;
+ display: none;
+}
+
+.peers span {
+ margin-top: 25px;
+}
+
+.peers .arrow {
+ border: 6px solid transparent;
+ border-bottom: none;
+ border-top-color: white;
+ display: inline-block;
+ width: 0;
+}
+
+.peers .drowdown {
+ top: 45px;
+ right: 110px;
+ color: black;
+ padding: 10px 15px;
+ white-space: nowrap;
+ line-height: 12px;
+ width: inherit;
+}
+
+.peers .drowdown a {
+ display: block;
+ padding: 10px;
+}
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 4562992..8966cff 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1098,5 +1098,19 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ getPeers : function(suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'peers',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.main.js b/ui/js/src/kimchi.main.js
index ba54b26..ea9951b 100644
--- a/ui/js/src/kimchi.main.js
+++ b/ui/js/src/kimchi.main.js
@@ -20,10 +20,23 @@ kimchi.tabMode = {};
kimchi.capabilities = undefined;
kimchi.getCapabilities(function(result) {
kimchi.capabilities = result;
+ kimchi.setupPeers();
}, function() {
kimchi.capabilities = {};
});
+kimchi.setupPeers = function(){
+ if(kimchi.capabilities.federation=="enable"){
+ kimchi.getPeers(function(data){
+ if(data.length>0)
+ $('#peers').css("display", "block");
+ for(var i=0; i<data.length; i++){
+ $('.drowdown', '#peers').append("<a href='"+data[i]+"' target='_blank'>"+data[i]+"</a>");
+ }
+ });
+ }
+};
+
kimchi.main = function() {
kimchi.popable();
diff --git a/ui/pages/kimchi-ui.html.tmpl b/ui/pages/kimchi-ui.html.tmpl
index 7bdf441..4e7b3af 100644
--- a/ui/pages/kimchi-ui.html.tmpl
+++ b/ui/pages/kimchi-ui.html.tmpl
@@ -71,6 +71,13 @@
<h1 id="logo"><img alt="Project Kimchi" src="images/theme-default/logo-white.png"></h1>
<ul class="nav-top">
<li>
+ <div id="peers" class="peers popable">
+ <span>$_("Peers")</span>
+ <span class="arrow"></span>
+ <div class="drowdown popover right-side"></div>
+ </div>
+ </li>
+ <li>
<div id="user" class="popable">
<span id="user-icon"></span>
<span id="user-name" class="empty-when-logged-off"></span>
--
1.7.1
3
6

27 Aug '14
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Note:
The UI patch is just used for testing.
Problem remains:
1. POST return 404 because the UI patch does not include accept json
2. task id does not returned in vol data and progress has not been added
3. Haven't tested large file, so not sure whether we need to split large file into peices
and call POST multi-times.
Royce Lv (3):
Storage volume upload: Dispatch volume create to right handler
Storage volume upload: Parse params for upload formdata
Storage volume upload: add model function of upload
ssdxiao (1):
Support to upload ISO
contrib/kimchi.spec.fedora.in | 3 +
contrib/kimchi.spec.suse.in | 3 +
po/en_US.po | 2 +
po/pt_BR.po | 3 +
po/zh_CN.po | 3 +
src/kimchi/API.json | 1 -
src/kimchi/control/storagepools.py | 4 +-
src/kimchi/control/utils.py | 2 +
src/kimchi/i18n.py | 1 +
src/kimchi/model/storagevolumes.py | 34 ++
src/nginx.conf.in | 1 +
ui/css/theme-default/upload.css | 43 ++
ui/js/resumable.js | 816 ++++++++++++++++++++++++++++++++++
ui/js/src/kimchi.template_add_main.js | 27 ++
ui/pages/kimchi-ui.html.tmpl | 1 +
ui/pages/template-add.html.tmpl | 13 +
16 files changed, 954 insertions(+), 3 deletions(-)
create mode 100644 ui/css/theme-default/upload.css
create mode 100644 ui/js/resumable.js
--
1.8.3.2
4
20

26 Aug '14
The old way does just consider if the interface has connectivity, not
the interface status. The new file can determine if the interface is up
or down and if cable is connected or not.
Signed-off-by: Ramon Medeiros <ramonn(a)linux.vnet.ibm.com>
---
src/kimchi/netinfo.py | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/src/kimchi/netinfo.py b/src/kimchi/netinfo.py
index 5fd6130..2d5cf29 100644
--- a/src/kimchi/netinfo.py
+++ b/src/kimchi/netinfo.py
@@ -29,7 +29,7 @@ BONDING_PATH = '/sys/class/net/*/bonding'
WLAN_PATH = '/sys/class/net/*/wireless'
NET_BRPORT = '/sys/class/net/%s/brport'
NET_MASTER = '/sys/class/net/%s/master'
-NET_STATE = '/sys/class/net/%s/operstate'
+NET_STATE = '/sys/class/net/%s/carrier'
PROC_NET_VLAN = '/proc/net/vlan/'
BONDING_SLAVES = '/sys/class/net/%s/bonding/slaves'
BRIDGE_PORTS = '/sys/class/net/%s/brif'
@@ -101,8 +101,18 @@ def is_bondlave(nic):
def operstate(dev):
- return open(NET_STATE % dev).readline().strip()
+
+ # try to read interface status
+ try:
+ open(NET_STATE % dev).readline().strip()
+
+ # when IOError is raised, interface is down
+ except IOError:
+ return "down"
+ # if value is 1, interface up with cable connected
+ # 0 corresponds to interface up with cable disconnected
+ return "up"
def get_vlan_device(vlan):
""" Return the device of the given VLAN. """
--
1.8.3.1
2
1
Kimchi can manage guests not created by Kimchi. If a user creates a
non-persistent domain and uses the Power Off option, it will destroy
the user's domain. In order to warn users with non-persistent guests
on Power Off, this patch adds a 'persistent' field (like the one for
networks and storage pools) to a vm's JSON representation.
Signed-off-by: Christy Perez <christy(a)linux.vnet.ibm.com>
---
docs/API.md | 2 ++
src/kimchi/model/vms.py | 3 ++-
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/docs/API.md b/docs/API.md
index d75c55f..ebb6e61 100644
--- a/docs/API.md
+++ b/docs/API.md
@@ -45,6 +45,8 @@ the following general conventions:
* **POST**: Create a new Virtual Machine
* name *(optional)*: The name of the VM. Used to identify the VM in this
API. If omitted, a name will be chosen based on the template used.
+ * persistent: If 'true', vm will persist after a Power Off or host reboot.
+ All virtual machines created by Kimchi are persistent.
* template: The URI of a Template to use when building the VM
* storagepool *(optional)*: Assign a specific Storage Pool to the new VM
* graphics *(optional)*: Specify the graphics paramenter for this vm
diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
index 476e4ac..5721b48 100644
--- a/src/kimchi/model/vms.py
+++ b/src/kimchi/model/vms.py
@@ -471,7 +471,8 @@ def lookup(self, name):
'ticket': self._get_ticket(dom),
'users': users,
'groups': groups,
- 'access': 'full'
+ 'access': 'full',
+ 'persistent': True if dom.isPersistent() else False
}
def _vm_get_disk_paths(self, dom):
--
1.9.3
5
15
Kimchi is currently not able to configure and open help pages of any
plugin tab. This patch fixes this problem and removes the HELP button
if the plugin does not have a help configured properly.
Help pages for a plugin tab follow same Kimchi system:
- html help file should have the same name of plugin html tab file
- html files should be in plugin's " ui/pages/help/<LANG> " path
- plugin should add following lines to <PLUGIN_NAME>.conf:
* [/help]
* tools.staticdir.on = True
* tools.nocache.on = True
- plugins should add a <help> element with tab help html name in
"ui/config/tab-ext.xml", for each configured tab. If this
element does not exist, 'Help' will be disabled
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
plugins/sample/sample.conf.in | 4 ++++
plugins/sample/ui/config/tab-ext.xml | 1 +
plugins/sample/ui/pages/help/en_US/tab.html | 1 +
src/kimchi/config.py.in | 1 +
src/kimchi/utils.py | 9 +++++++--
ui/js/src/kimchi.main.js | 26 ++++++++++++++++++++++++--
6 files changed, 38 insertions(+), 4 deletions(-)
create mode 100644 plugins/sample/ui/pages/help/en_US/tab.html
diff --git a/plugins/sample/sample.conf.in b/plugins/sample/sample.conf.in
index 6e0908b..181616d 100644
--- a/plugins/sample/sample.conf.in
+++ b/plugins/sample/sample.conf.in
@@ -22,3 +22,7 @@ tools.kimchiauth.admin_methods = ['POST', 'PUT']
[/circles]
tools.kimchiauth.on = True
tools.kimchiauth.admin_methods = ['POST', 'PUT']
+
+[/help]
+tools.staticdir.on = True
+tools.nocache.on = True
diff --git a/plugins/sample/ui/config/tab-ext.xml b/plugins/sample/ui/config/tab-ext.xml
index a1fb1c2..5ca6e6b 100644
--- a/plugins/sample/ui/config/tab-ext.xml
+++ b/plugins/sample/ui/config/tab-ext.xml
@@ -6,5 +6,6 @@
<title>SampleTab</title>
<path>plugins/sample/tab.html</path>
+ <help>tab</help>
</tab>
</tabs-ext>
diff --git a/plugins/sample/ui/pages/help/en_US/tab.html b/plugins/sample/ui/pages/help/en_US/tab.html
new file mode 100644
index 0000000..cd32b47
--- /dev/null
+++ b/plugins/sample/ui/pages/help/en_US/tab.html
@@ -0,0 +1 @@
+Help page for Kimchi's Sample plugin.
diff --git a/src/kimchi/config.py.in b/src/kimchi/config.py.in
index fca32ee..e10f1f6 100644
--- a/src/kimchi/config.py.in
+++ b/src/kimchi/config.py.in
@@ -145,6 +145,7 @@ class PluginPaths(Paths):
self.ui_dir = self.add_prefix(os.path.join(self.plugin_dir, 'ui'))
self.mo_dir = self.add_prefix(os.path.join(self.plugin_dir, 'mo'))
self.conf_file = os.path.join(self.conf_dir, '%s.conf' % name)
+ self.help_dir = os.path.join(self.ui_dir + '/pages/help')
class UIConfig(dict):
diff --git a/src/kimchi/utils.py b/src/kimchi/utils.py
index 0977b9f..9e80e06 100644
--- a/src/kimchi/utils.py
+++ b/src/kimchi/utils.py
@@ -82,13 +82,18 @@ def is_digit(value):
def _load_plugin_conf(name):
- plugin_conf = PluginPaths(name).conf_file
+ plugin_paths = PluginPaths(name)
+ plugin_conf = plugin_paths.conf_file
if not os.path.exists(plugin_conf):
cherrypy.log.error_log.error("Plugin configuration file %s"
" doesn't exist." % plugin_conf)
return
try:
- return Parser().dict_from_file(plugin_conf)
+ plugin_dict = Parser().dict_from_file(plugin_conf)
+ if (os.path.exists(plugin_paths.help_dir) and '/help' in plugin_dict):
+ plugin_dict['/help']['tools.staticdir.dir'] = \
+ plugin_paths.help_dir
+ return plugin_dict
except ValueError as e:
cherrypy.log.error_log.error("Failed to load plugin "
"conf from %s: %s" %
diff --git a/ui/js/src/kimchi.main.js b/ui/js/src/kimchi.main.js
index ba54b26..71a8280 100644
--- a/ui/js/src/kimchi.main.js
+++ b/ui/js/src/kimchi.main.js
@@ -34,9 +34,15 @@ kimchi.main = function() {
var path = tab['path'];
var mode = tab['mode'];
if (mode != 'none') {
+ var disableHelp = "";
+ if (/^plugins/.test(path)) {
+ if (!tab['pluginTabHelp']) {
+ disableHelp = " disableHelp";
+ }
+ }
tabsHtml.push(
'<li>',
- '<a class="item" href="', path, '">',
+ '<a class="item', disableHelp,'" href="', path, '">',
title,
'</a>',
'</li>'
@@ -53,6 +59,7 @@ kimchi.main = function() {
var titleKey = $tab.find('title').text();
var title = i18n[titleKey] ? i18n[titleKey] : titleKey;
var path = $tab.find('path').text();
+ var pluginTabHelp = $tab.find('help').text();
var roles = kimchi.cookie.get('roles');
if (roles) {
var role = JSON.parse(roles)[titleKey.toLowerCase()];
@@ -61,7 +68,8 @@ kimchi.main = function() {
tabs.push({
title: title,
path: path,
- mode: mode
+ mode: mode,
+ pluginTabHelp: pluginTabHelp
});
} else {
document.location.href = 'login.html';
@@ -158,6 +166,15 @@ kimchi.main = function() {
$(tab).addClass('current');
$(tab).focus();
+ if ($(tab).hasClass("disableHelp")) {
+ $('#btn-help').prop('disabled', true);
+ $('#btn-help').hide();
+ }
+ else {
+ $('#btn-help').prop('disabled', false);
+ $('#btn-help').show();
+ }
+
// Load page content.
loadPage(url);
};
@@ -297,6 +314,11 @@ kimchi.getHelp = function(e) {
url = url.replace("#tabs", "/help/" + lang);
if (url == "/help" + lang)
url = url + "/index.html"
+ else if (/^#plugin/.test(url)) {
+ var plugin = url.split("/").pop();
+ url = url.replace("#", "");
+ url = url.replace(plugin, "help/" + lang + "/" + plugin + ".html");
+ }
else
url = url + ".html";
--
1.9.3
2
4
v3:
- changed the code to be generic (treat both netfs and iscsi targets)
v2:
- fixed "make check" issue
The following patch fixes the issue described in Github #354:
"storage: used nfs source path need to be filtered out in creation page"
https://github.com/kimchi-project/kimchi/issues/354
*** BLURB HERE ***
Daniel Henrique Barboza (1):
model/storagetargets: filtering used nfs paths
src/kimchi/model/storagetargets.py | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)
--
1.8.3.1
4
4
https://github.com/kimchi-project/kimchi/issues/405
Patches fixes problem (1) and (3).
Problem (2) is caused by libvirt. Error has already been reported
to libvirt community.
Rodrigo Trujillo (2):
Fix cancel button in edit guest storage tab
Fix enter hit in storage tab under guest edit window
ui/js/src/kimchi.guest_edit_main.js | 1 +
ui/pages/guest-edit.html.tmpl | 12 ++++++------
2 files changed, 7 insertions(+), 6 deletions(-)
--
1.9.3
4
5

25 Aug '14
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/storage.css | 50 +++++++++++++++++++
ui/js/src/kimchi.api.js | 16 ++++++
ui/js/src/kimchi.storagepool_add_main.js | 76 ++++++++++++++++++++++++++++++
ui/pages/storagepool-add.html.tmpl | 7 ++-
4 files changed, 147 insertions(+), 2 deletions(-)
diff --git a/ui/css/theme-default/storage.css b/ui/css/theme-default/storage.css
index 4f439e8..cb0caee 100644
--- a/ui/css/theme-default/storage.css
+++ b/ui/css/theme-default/storage.css
@@ -593,3 +593,53 @@
center no-repeat;
padding: 0 20px 0 26px;
}
+
+.storage-admin .filter-select {
+ display: inline-block;
+ position: relative;
+}
+
+.storage-admin .filter-select input {
+ border: 1px solid #CCCCCC;
+ border-radius: 1px;
+ font-size: 14px;
+ color: #666666;
+ padding: 3px 3px 3px 10px;
+ height: 30px;
+ width: 493px;
+}
+
+.storage-admin .filter-select input::-ms-clear {
+ display: none;
+}
+
+.storage-admin .filter-select .arrow {
+ display: inline-block;
+ vertical-align: middle;
+ position: relative;
+ left: -25px;
+}
+
+.storage-admin .filter-select .option {
+ border-style: solid;
+ border-color: #CCCCCC;
+ border-width: 0px 1px 1px 1px;
+ border-radius: 1px;
+ font-size: 14px;
+ background-color: white;
+ width: 506px;
+ max-height: 140px;
+ overflow: auto;
+ color: #666666;
+ position: absolute;
+ z-index: 1000;
+}
+
+.storage-admin .filter-select .option .item {
+ padding: 5px 10px;
+ cursor: pointer;
+}
+
+.storage-admin .filter-select .option .item:hover {
+ background-color: #DDDDDD;
+}
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 4562992..d89919e 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1098,5 +1098,21 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ getISCSITargets : function(server, port, suc, err) {
+ server = encodeURIComponent(server);
+ port = encodeURIComponent(port);
+ kimchi.requestJSON({
+ url : kimchi.url + 'storageservers/'+server+'/storagetargets?_target_type=iscsi&_server_port='+port,
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js
index ecbc682..49ba9e1 100644
--- a/ui/js/src/kimchi.storagepool_add_main.js
+++ b/ui/js/src/kimchi.storagepool_add_main.js
@@ -34,6 +34,78 @@ kimchi.storagepool_add_main = function() {
});
};
+kimchi.storageFilterSelect = function(id, isUpdate) {
+ var input = $('input', '#'+id);
+ var options = $(".option", '#'+id);
+ var filter = function(container, key){
+ container.children().each(function(){
+ $(this).css("display", $(this).text().indexOf(key)==-1 ? "none" : "");
+ });
+ };
+ if(!isUpdate){
+ $('.arrow', '#'+id).click(function(){
+ options.toggleClass("hide-content");
+ });
+ input.focus(function(){
+ options.removeClass("hide-content");
+ }).on("keyup", function(){
+ filter(options, input.val());
+ });
+ }
+ options.children().each(function(){
+ $(this).click(function(){
+ input.val($(this).text());
+ $('.option', "#iSCSITarget").addClass("hide-content");
+ filter(options, "");
+ });
+ });
+};
+
+kimchi.initISCSITargets = function(){
+ var loadTargets = function(server, port, callback){
+ var isUpdate = $(".option", "#iSCSITarget").children().length > 0;
+ $(".option", "#iSCSITarget").empty();
+ $('input', "#iSCSITarget").val("loading targets...");
+ kimchi.getISCSITargets(server, port, function(data){
+ if(data.length==0){
+ $('input', "#iSCSITarget").val("no targets is got.");
+ }else{
+ for(var i=0; i<data.length; i++){
+ var itemNode = $.parseHTML("<div class='item'>"+data[i].target+"</div>");
+ $(".option", "#iSCSITarget").append(itemNode);
+ }
+ kimchi.storageFilterSelect('iSCSITarget', isUpdate);
+ $('input', "#iSCSITarget").attr("readonly", false);
+ $('input', "#iSCSITarget").val("");
+ $('input', "#iSCSITarget").trigger("focus");
+ }
+ callback();
+ }, function(data){
+ $('input', "#iSCSITarget").val("failed to load targets.");
+ kimchi.message.error(data.responseJSON.reason);
+ callback();
+ });
+ };
+ var triggerLoadTarget = function(){
+ var server = $("#iscsiserverId").val().trim();
+ var port = $("#iscsiportId").val().trim();
+ if(server!="" && port!="" && !$("#iscsiserverId").hasClass("invalid-field") && !$("#iscsiportId").hasClass("invalid-field")){
+ $("#iscsiserverId").attr("disabled", true);
+ $("#iscsiportId").attr("disabled", true);
+ loadTargets(server, port, function(){
+ $("#iscsiserverId").attr("disabled", false);
+ $("#iscsiportId").attr("disabled", false);
+ });
+ }
+ };
+ $("#iscsiserverId").change(function(){
+ triggerLoadTarget();
+ });
+ $("#iscsiportId").change(function(){
+ triggerLoadTarget();
+ });
+};
+
kimchi.initStorageAddPage = function() {
kimchi.listHostPartitions(function(data) {
if (data.length > 0) {
@@ -160,6 +232,10 @@ kimchi.initStorageAddPage = function() {
$('#iscsiportId').keyup(function(event) {
$(this).toggleClass("invalid-field",!/^[0-9]+$/.test($(this).val()));
});
+ $('#iscsiserverId').keyup(function(event) {
+ $(this).toggleClass("invalid-field",!kimchi.isServer($(this).val().trim()));
+ });
+ kimchi.initISCSITargets();
};
/* Returns 'true' if all form fields were filled, 'false' if
diff --git a/ui/pages/storagepool-add.html.tmpl b/ui/pages/storagepool-add.html.tmpl
index 1eb2029..ee19ed5 100644
--- a/ui/pages/storagepool-add.html.tmpl
+++ b/ui/pages/storagepool-add.html.tmpl
@@ -23,7 +23,7 @@
<!DOCTYPE html>
<html>
<body>
- <div class="window storage-window">
+ <div class="window storage-window storage-admin">
<header>
<h1 class="title">$_("Define a New Storage Pool")</h1>
<div class="close">X</div>
@@ -119,7 +119,10 @@
<h2>4. $_("Target")</h2>
<div class="field">
<p class="text-help">$_("The iSCSI target on iSCSI server")</p>
- <input id="iscsiTargetId" type="text" class="text storage-base-input-width">
+ <span class="filter-select" id="iSCSITarget">
+ <div><input id="iscsiTargetId" type="text" readonly><a class="ui-icon ui-icon-triangle-1-s arrow"></a></div>
+ <div class="option hide-content"></div>
+ </span>
</div>
</section>
<section class="form-section">
--
1.7.1
3
2

25 Aug '14
Changes:
1) Have the "Manage Media" function deleted from "Action" list
2) Enable "Edit" whether VM is running or off
3) Disable edit function of "General", "Storage", "Interface" except
"replace" of the cdrom under "Storage".
Wen Wang (2):
UI: Delete Manage Media function from action list
Allow admin user change permission settings when VM is running
ui/js/src/kimchi.guest_edit_main.js | 54 ++++++++++++++++++++++----------
ui/js/src/kimchi.guest_main.js | 14 --------
ui/pages/guest-edit.html.tmpl | 2 +-
ui/pages/guest-media.html.tmpl | 57 -----------------------------------
ui/pages/guest.html.tmpl | 1 -
5 files changed, 38 insertions(+), 90 deletions(-)
delete mode 100644 ui/pages/guest-media.html.tmpl
3
10

25 Aug '14
Changes:
V1 -> V2:
1) Have the "ui/css/theme-default/guest-media.css" deleted
2) Have the "edit" button in interface tab deleted when VM is running
1) Have the "Manage Media" function deleted from "Action" list
2) Enable "Edit" whether VM is running or off
3) Disable edit function of "General", "Storage", "Interface" except
"replace" of the cdrom under "Storage".
Wen Wang (2):
UI: Delete Manage Media function from action list
Allow admin user change permission settings when VM is running
ui/css/theme-default/guest-media.css | 42 -------------------------
ui/js/src/kimchi.guest_edit_main.js | 56 ++++++++++++++++++++++-----------
ui/js/src/kimchi.guest_main.js | 14 --------
ui/pages/guest-edit.html.tmpl | 2 +-
ui/pages/guest-media.html.tmpl | 57 ----------------------------------
ui/pages/guest.html.tmpl | 1 -
6 files changed, 39 insertions(+), 133 deletions(-)
delete mode 100644 ui/css/theme-default/guest-media.css
delete mode 100644 ui/pages/guest-media.html.tmpl
2
7
Adding a new field for VM properties, and a new error message.
Note: The error message needs Portuguese and Chinese translation.
Christy Perez (2):
Add persistent flag to VM info
Display appropriate warning for Power Off of non-persistent VM
docs/API.md | 2 ++
po/en_US.po | 3 +++
po/pt_BR.po | 3 +++
po/zh_CN.po | 3 +++
src/kimchi/model/vms.py | 3 ++-
ui/js/src/kimchi.guest_main.js | 6 +++++-
ui/pages/i18n.json.tmpl | 1 +
7 files changed, 19 insertions(+), 2 deletions(-)
--
1.9.3
3
9

[PATCH V2] Bugfix UI: Change button text to indicate user network is generating
by Wen Wang 21 Aug '14
by Wen Wang 21 Aug '14
21 Aug '14
V1 -> V2:
1)Change the create button from "Creating..." to "Create" when error
occurs.
2)Disable input when creating a new network.
When network is generating, if the network is not up, we bring the
network up first, then we create the network. During the time we bring
the network up(less than 10s), we change the text as well as the layout
of the button to indicate this process is working.
Signed-off-by: Wen Wang <wenwang(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.network.js | 10 ++++++++++
ui/pages/i18n.json.tmpl | 1 +
2 files changed, 11 insertions(+), 0 deletions(-)
diff --git a/ui/js/src/kimchi.network.js b/ui/js/src/kimchi.network.js
index c8f983f..4ee7249 100644
--- a/ui/js/src/kimchi.network.js
+++ b/ui/js/src/kimchi.network.js
@@ -207,6 +207,11 @@ kimchi.initNetworkCreation = function() {
network.persistent = result.persistent;
kimchi.addNetworkItem(network);
$("#networkConfig").dialog("close");
+ }, function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ $("#networkFormOk").button("enable");
+ $("#networkName").removeAttr("readonly");
+ $("#networkFormOk span").text(i18n.KCHAPI6005M);
});
});
});
@@ -270,6 +275,9 @@ kimchi.openNetworkDialog = function(okCallback) {
title : i18n.KCHNET6003M
});
$("#networkFormOk").on("click", function() {
+ $("#networkFormOk").button("disable");
+ $("#networkName").prop("readonly", "readonly");
+ $("#networkFormOk span").text(i18n.KCHAPI6008M);
okCallback();
});
$("#enableVlan").on("click", function() {
@@ -337,6 +345,8 @@ kimchi.cleanNetworkDialog = function() {
$("#networkDestinationLabel").text($("#networkDestinationID li:first-child").text());
$("#networkFormOk").off("click");
$("#networkFormOk").button("disable");
+ $("#networkFormOk span").text(i18n.KCHAPI6005M);
+ $("#networkName").removeAttr("readonly");
$("#networkVlanID").toggle(false);
$("#labelNetworkVlanID").toggle(false);
$("#enableVlan").prop("checked", false);
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index ccfb081..cbcd077 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -49,6 +49,7 @@
"KCHAPI6005M": "$_("Create")",
"KCHAPI6006M": "$_("Warning")",
"KCHAPI6007M": "$_("Save")",
+ "KCHAPI6008M": "$_("Creating...")",
"KCHGRD6001M": "$_("Loading...")",
"KCHGRD6002M": "$_("An error occurs while checking for packages update.")",
--
1.7.1
3
3
v2:
- fixed "make check" issue
The following patch fixes the issue described in Github #354:
"storage: used nfs source path need to be filtered out in creation page"
https://github.com/kimchi-project/kimchi/issues/354
Daniel Henrique Barboza (1):
model/storagetargets: filtering used nfs paths
src/kimchi/model/storagetargets.py | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
--
1.8.3.1
4
4

21 Aug '14
LTC Bugzilla #115031 - LTCTest: build-8: List interfaces which are
active
The old way does just consider if the interface has connectivity, not
the interface status. The new file can determine if the interface is up
or down and if cable is connected or not.
Signed-off-by: Ramon Medeiros <ramonn(a)linux.vnet.ibm.com>
---
src/kimchi/netinfo.py | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/src/kimchi/netinfo.py b/src/kimchi/netinfo.py
index 5fd6130..276c0b7 100644
--- a/src/kimchi/netinfo.py
+++ b/src/kimchi/netinfo.py
@@ -29,7 +29,7 @@ BONDING_PATH = '/sys/class/net/*/bonding'
WLAN_PATH = '/sys/class/net/*/wireless'
NET_BRPORT = '/sys/class/net/%s/brport'
NET_MASTER = '/sys/class/net/%s/master'
-NET_STATE = '/sys/class/net/%s/operstate'
+NET_STATE = '/sys/class/net/%s/carrier'
PROC_NET_VLAN = '/proc/net/vlan/'
BONDING_SLAVES = '/sys/class/net/%s/bonding/slaves'
BRIDGE_PORTS = '/sys/class/net/%s/brif'
@@ -101,8 +101,21 @@ def is_bondlave(nic):
def operstate(dev):
- return open(NET_STATE % dev).readline().strip()
+ status = None
+
+ # try to read interface status
+ try:
+ status = open(NET_STATE % dev).readline().strip()
+ except IOError:
+ pass
+
+ # when IOError is raised, interface is down
+ if status == None:
+ return "down"
+ # if value is 1, cable is connected
+ # 0 corresponds to no interface up with no cable
+ return "up"
def get_vlan_device(vlan):
""" Return the device of the given VLAN. """
--
1.8.3.1
2
2

21 Aug '14
This bug turns out to be caused by we change the login format from
login-window to tranditional login format. After the logout, we can no
longer and there is no need to update the pages and popup a login
window. Now we redirect to login.html after logout.
Signed-off-by: Wen Wang <wenwang(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.main.js | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/ui/js/src/kimchi.main.js b/ui/js/src/kimchi.main.js
index 613ab41..ba54b26 100644
--- a/ui/js/src/kimchi.main.js
+++ b/ui/js/src/kimchi.main.js
@@ -239,7 +239,7 @@ kimchi.main = function() {
// Perform logging out via Ajax request.
$('#btn-logout').on('click', function() {
kimchi.logout(function() {
- updatePage();
+ document.location.href = "login.html";
}, function(err) {
kimchi.message.error(err.responseJSON.reason);
});
--
1.7.1
3
5
Agree with Aline's proposal to add a button in ISO line of storage pool tab:
And then pop out a window:
And then show progress. Here we'll have 2 options: progress with
percentage indication and only running/finished status.
Option 1:
Option 2:
For option 1, we need back-end support that enables in-time notification
of completion percentage when uploading a file with 2 pieces of
information: total file size, and received file size.
5
10

21 Aug '14
Someone reported that their guests (not created by Kimchi) were being deleted if
they used Kimchi to shut them down. This small patchset adds a warning message
and also increases the size of the confirmation dialog in the UI to fit the
new text.
Note: It still needs the Chinese translation.
Christy Perez (2):
Add Power-Off warning for non-persistent guests
Make the confirmation dialog bigger
po/en_US.po | 15 ++++++++++++---
po/kimchi.pot | 10 ++++++++--
po/pt_BR.po | 16 +++++++++++++---
po/zh_CN.po | 10 ++++++++--
ui/css/theme-default/message.css | 4 ++--
ui/pages/i18n.json.tmpl | 1 +
6 files changed, 44 insertions(+), 12 deletions(-)
--
1.9.3
3
5

20 Aug '14
Hi all,
I have worked to fix https://github.com/kimchi-project/kimchi/issues/405
In problem (2) the cause seems to be an error in libvirt, it is saving
the device with
wrong protocol, which makes kimchi not return the path.
Reproduce:
1- Create a virtual machine with a local cdrom iso configure. You will
see a device such as ("virsh edit" shows):
<disk type='file' device='cdrom'>
<driver name='qemu' type='raw'/>
<source file='/ISOS/ubuntu-14.04-server-amd64.iso'/>
<target dev='hdc' bus='ide'/>
<readonly/>
<address type='drive' controller='0' bus='1' target='0' unit='0'/>
</disk>
2- Create a file with new device (same dev name) configuration, such as
(/tmp/new-cdrom):
<disk device="cdrom" type="network">
<driver type="raw" name="qemu"/>
<target bus="ide" dev="hdc"/>
<source protocol="http"
name="/releases/14.04.1/release/ubuntu-14.04.1-desktop-amd64+mac.iso">
<host port="80" name="cdimages.ubuntu.com"/>
</source>
</disk>
3- Update the virtual machine device with virsh:
sudo virsh update-device <VM-NAME> /tmp/new-cdrom
4- Edit the vm xml and you will see that the device was added with "NBD"
protocol and without HOST information!!! :
<disk type='network' device='cdrom'>
<driver name='qemu' type='raw'/>
<source protocol='nbd'
name='/releases/14.04.1/release/ubuntu-14.04.1-desktop-amd64+mac.iso'/>
<target dev='hdc' bus='ide'/>
<readonly/>
<address type='drive' controller='0' bus='1' target='0' unit='0'/>
</disk>
----------------------
Kimchi uses "dom.updateDeviceFlags", the same API.
Notice that this happens when changing from local to remote ISO only.
Remote to remote works ... and local to local too.
Does anyone have idea why this happens ?
As workaround I can detach, then attach the device again. Thoughts ?
Rodrigo Trujillo
1
0

20 Aug '14
From: Wen Wang <wenwang(a)linux.vnet.ibm.com>
When network is generating, if the network is not up, we bring the
network up first, then we create the network. During the time we bring
the network up(less than 10s), we change the text as well as the layout
of the button to indicate this process is working.
Signed-off-by: Wen Wang <wenwang(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.network.js | 3 +++
ui/pages/i18n.json.tmpl | 1 +
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/ui/js/src/kimchi.network.js b/ui/js/src/kimchi.network.js
index c8f983f..cee5864 100644
--- a/ui/js/src/kimchi.network.js
+++ b/ui/js/src/kimchi.network.js
@@ -270,6 +270,8 @@ kimchi.openNetworkDialog = function(okCallback) {
title : i18n.KCHNET6003M
});
$("#networkFormOk").on("click", function() {
+ $("#networkFormOk").button("disable");
+ $("#networkFormOk span").text(i18n.KCHAPI6008M);
okCallback();
});
$("#enableVlan").on("click", function() {
@@ -337,6 +339,7 @@ kimchi.cleanNetworkDialog = function() {
$("#networkDestinationLabel").text($("#networkDestinationID li:first-child").text());
$("#networkFormOk").off("click");
$("#networkFormOk").button("disable");
+ $("#networkFormOk span").text(i18n.KCHAPI6005M);
$("#networkVlanID").toggle(false);
$("#labelNetworkVlanID").toggle(false);
$("#enableVlan").prop("checked", false);
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index ccfb081..cbcd077 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -49,6 +49,7 @@
"KCHAPI6005M": "$_("Create")",
"KCHAPI6006M": "$_("Warning")",
"KCHAPI6007M": "$_("Save")",
+ "KCHAPI6008M": "$_("Creating...")",
"KCHGRD6001M": "$_("Loading...")",
"KCHGRD6002M": "$_("An error occurs while checking for packages update.")",
--
1.7.1
4
5
The following API changes should be made to implement the remote
download ISO feature:
1) POST /storagepools/ISO/storagevolumes/download {'url':
<http|https|ftp|ftps>, 'name': <volume name>}
Description: Starts downloading a remote ISO image to Kimchi's
storage pool.
Parameters:
'url': specifies where the image is hosted. Required.
'name': specifies the name of the local file when the download
finishes. If this parameter is omitted, Kimchi will try to use the same
file name as the remote file. Optional.
Return codes:
200: the remote URL is valid and the download started successfully.
400: the remote URL is not valid;
the provided 'name' is already in use (this error
shouldn't happen if 'name' is omitted).
the storage pool doesn't have enough free space to hold
the remote file.
Return data:
{'name': <vol-name>}
2) POST /storagepools/ISO/storagevolumes/download/pause {'name': <volume
name>}
Description: Pauses a remote ISO download.
Parameters:
'name': the remote volume name to be paused. Required.
Return codes:
200: 'name' is an ongoing download file and the operation was paused
successfully.
400: 'name' is an invalid volume name (doesn't exist / isn't an ongoing
download);
download referred by 'name' cannot be paused.
Return data:
{}
3) POST /storagepools/ISO/storagevolumes/download/resume {'name':
<volume name>}
Description: Resumes a paused remote ISO download.
Parameters:
'name': the remote volume name to be resumed. Required.
Return codes:
200: 'name' is a paused download file and the operation was resumed
successfully.
400: 'name' is an invalid volume name (doesn't exist / isn't a paused
download).
Return data:
{}
4) POST /storagepools/ISO/storagevolumes/download/cancel {'name':
<volume name>}
Description: Cancels a remote ISO download.
Parameters:
'name': the remote volume name to be canceled. Required.
Return codes:
200: 'name' is an ongoing download file and the operation was canceled
successfully. Whatever data has been downloaded so far should be deleted.
400: 'name' is an invalid volume name (doesn't exist / isn't an ongoing
download).
Return data:
{}
5) POST /storagepools/ISO/storagevolumes/download/status {'name':
<volume name>}
Description: Provides status of a remote ISO download.
Parameters:
'name': the remote volume name. Required.
Return codes:
200: 'name' is a download file and its status information was returned
successfully.
400: 'name' is an invalid volume name (doesn't exist / isn't a remote
download).
Return data:
{'name': <volume name>, 'status': [downloading|paused], 'total_size':
<size in bytes>, 'downloaded_size': <size in bytes>}
I tried to put together the feedback given in another e-mail thread,
especially by Aline, Royce and Yu Xin Huo, along with some ideas of my
own. This RFC doesn't support choosing a different storage pool other
than "ISO" to host the downloaded files. If we're doing this, wel need
to update this new API accordingly.
The implementation details will be discussed later.
Please share your feedback with us.
3
10
The following patch fixes the issue described in Github #354:
"storage: used nfs source path need to be filtered out in creation page"
https://github.com/kimchi-project/kimchi/issues/354
Daniel Henrique Barboza (1):
model/storagetargets: filtering used nfs paths
src/kimchi/model/storagetargets.py | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
--
1.8.3.1
4
6
The small icons on each template indicate whether they are local or
remote but that may not be obvious to everyone.
Show a description when the user hovers the mouse over the template icons
indicating what they mean: local or remote template. Also, the same
descriptive text will be shown in the icon's place if the browser is not
able to load images.
Signed-off-by: Crístian Viana <vianac(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.template_main.js | 2 ++
ui/pages/i18n.json.tmpl | 2 ++
ui/pages/tabs/templates.html.tmpl | 2 +-
3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/ui/js/src/kimchi.template_main.js b/ui/js/src/kimchi.template_main.js
index 3c8421d..ae3f290 100644
--- a/ui/js/src/kimchi.template_main.js
+++ b/ui/js/src/kimchi.template_main.js
@@ -25,8 +25,10 @@ kimchi.doListTemplates = function() {
var isLocal = /^\//.test(value['cdrom']);
if(isLocal){
value.location = "images/theme-default/icon-local.png";
+ value.iconDescription = i18n['KCHTMPL6004M'];
}else{
value.location = "images/theme-default/icon-remote.png";
+ value.iconDescription = i18n['KCHTMPL6005M'];
}
listHtml += kimchi.substitute(templateHtml, value);
});
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index f1478a7..8e6fcd6 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -61,6 +61,8 @@
"KCHTMPL6002M": "$_("It will take long time. Do you want to continue?")",
"KCHTMPL6003M": "$_("This will permanently delete the template. Would you like to continue?")",
+ "KCHTMPL6004M": "$_("Local template")",
+ "KCHTMPL6005M": "$_("Remote template")",
"KCHHOST6001E": "$_("Unable to shut down system as there are some virtual machines running!")",
diff --git a/ui/pages/tabs/templates.html.tmpl b/ui/pages/tabs/templates.html.tmpl
index 7cf7fcd..4fe1d73 100644
--- a/ui/pages/tabs/templates.html.tmpl
+++ b/ui/pages/tabs/templates.html.tmpl
@@ -50,7 +50,7 @@
<div class="template-icon template-icon-position">
<img alt="" src="{icon}">
- <img alt="" src="{location}" class="template-type-icon-position">
+ <img alt="{iconDescription}" title="{iconDescription}" src="{location}" class="template-type-icon-position">
</div>
<div class="template-general template-title template-title-position">
<h2 class="title" title="{name}">{name}</h2>
--
1.9.3
3
3
4
6
4
5

[RFC] Mock for Authorization: Allow admin user change permission settings when VM is running
by Wen Wang 19 Aug '14
by Wen Wang 19 Aug '14
19 Aug '14
Hi all,
Here is the new design of the "Edit" function. We are going to enable
user viewing VM details when VMs are running and "Permission" is always
available whether a VM is running or not.
Here's the detail of the design:
1) Enable "Edit" in "Action" whether VM is running or not.
2) Delete "Manage Media" button and move it to a tab in "Edit"
3) Enable "Permission" & "Manage Media" and provide read-only privilege
of "General", "Storage", "Interface" when a VM is running.
4) Enable "General", "Storage", "Interface", "Permission" and provide
read-only privilege of "Manage Media"when a VM is not running.
Mocks are added below:
Delete "Manage Media" and enable "Edit" all the time.
When a VM is running, tabs are presented:
When a VM is not running, tabs are presented all like what is right now
only with a new tab of "Manage Media" read-only:
Any comment is welcomed
Best Regards
Wang Wen
3
6
Hi all,
Back-end:
STOMP <http://stomp.github.com/> is a simple text-orientated messaging
protocol, based on which, we can accomplish the "TCP for the web".
What we need is a message server and a client that communicate with the
server. We can send messages to server using this protocol and receive
messages from server, which needs a subscription to the server first and
then server gives the callback.
Here are the possible STOMP servers:
Apache ActiveMQ the most popular and powerful open source
messaging and Integration Patterns server
Apache Apollo a redesigned version of ActiveMQ
CoilMQ a lightweight pure Python STOMP broker
inspired by StompServer
Gozirra a lightweight Java STOMP broker
HornetQ puts the buzz in messaging
MorbidQ a STOMP publish/subscribe server with
absolutely no potential to cluster
RabbitMQ an Erlang-based, multi-protocol broker
with full support for STOMP via a plugin
Sprinkle written in Python and runs on Unix
type platforms
Stampy a Java implementation of the STOMP 1.2
specification
StompConnect provides a bridge to any other JMS provider
StompServer a lightweight pure Ruby STOMP server
And the client:
pyactivemq module for communicating with the ActiveMQ message broker
stomper a client implementation of the STOMP protocol
stompest a full-featured STOMP implementation for Python
including both synchronous and asynchronous clients
stompy implementation of the STOMP protocol in Python
stomp.py a Python client library which can also be run as a
standalone, command-line client for testing.
Front-end:
As I mentioned before. Front-end only change the way that the long time
task communicate with the back-end. UI should look just the same with a
more enhanced display. Also, more function will be enabled according to
this like we can enable actions when generating a debug report, etc.
Best Regards
Wang Wen
1
0
1. Backend:
(1)API definition:
POST /storagepools/isos/storagevolumes/upload {'file': fileobject}
-- This is from backend view, as cherrypy's embedded support wraps
fileobject.
REF:http://docs.cherrypy.org/en/latest/pkg/cherrypy.tutorial.html?highlight…
(2) implementation:
We can get the file handler from cherrypy's embedded scheme,
and use it to read and write to a local file, ref:
https://groups.google.com/forum/#!topic/cherrypy-users/xGvxBGgQ90k
The only thing we need to concern at backend is the file size,
cherrypy demo has proposed a example leverage cgi lib:
http://tools.cherrypy.org/wiki/DirectToDiskFileUpload
2. For UI, we need to supply user with a form which input type is
'file', or we construct formdata in ajax(for IE only supported in IE10+):
REF:
http://stackoverflow.com/questions/6974684/how-to-send-formdata-objects-wit…
3. To support the progress bar in UI, we can utilize resumable js:
http://github.com/23/resumable.js
2
2
Complementing what Aline has already pointed (thanks Aline)
=== Create template from VM ===
This will use the same backing storage mechanism used to create a
template from a IMG file.
POST /templates {'name': <template-name>, 'vm': <vm-name>}
** change 'create' in src/kimchi/models/template.py:
- check if template is based on ISO or VM
- fetch VM information
- create new LibvirtVMTemplate
** change api.json
- Documentation
** New error messages
UI:
1) When creating a template:
Add an option "From guest"; then list all guests to user choose one
or multiples and do the requests
** Add the new option in /ui/pages/template-add.html.tmpl
<a id="iso-local" class="local">$_("Local ISO
Image")</a> ...
<a id="iso-remote" class="remote">$_("Remote ISO
Image")</a>
---> <a id="guest-template" class="guest">$_("From
Guest")</a> <------- NEW
** List VMs and allow select more then one. (use same behaviour like in
multiple local or remote isos )
1
0
V2-V1:
- Put new password input box on 'general' tab instead of being a
sperate tab.
- Rename the input label as 'Console password'.
Simon Jin (1):
UI: set ticket password
ui/css/theme-default/guest-edit.css | 10 +++++-----
ui/js/src/kimchi.guest_edit_main.js | 4 ++++
ui/pages/guest-edit.html.tmpl | 13 +++++++++++++
3 files changed, 22 insertions(+), 5 deletions(-)
--
1.8.5.3
2
2
Hi all,
As agreed in the last scrum meeting
(http://kimchi-project.github.io/kimchi/meetings/kimchi.scrum.2014-08-13-13.…)
I am sending to you an overview about the sprint 2 tasks.
That way everyone can be aware of what is expected in each task and we
don't block the UI development.
Just to make clear, this is my view on each task!
Maybe you - the task owner - have a different view on that. In this
case, share your ideas with us.
And *don't forget to send a RFC, V1 patch, UI mockup *or something
related to your task by *next Monday (08/18)*
This email is just a summary to motivate you! ;-)
*ISO pool: Support to download ISO from URL (vianac)*
The idea here is to provide a way to user download a ISO from URL to the
OOTB pool (ISO pool)
POST /storagepools/ISO/storagevolumes/ {'name': <vol-name>, 'url':
<http|https|ftp://url>}
- name can be optional. We can generate one automatically based on ISO
path when any name is provided by user.
- url is required.
Questions:
- Which protocols will we support? http, https, ftp? Any other?
- About the UI, how do we show this feature to user?
Basically we have 2 options:
1) In the storage tab:
Add an option "Add ISO" to the ISO pool (or any pool ?); get the
URL and display a progress bar.
2) While creating a template:
Add an option "Download ISO"; get the URL and display a progress bar.
When the download is completed, we create a template using the new
ISO.
I prefer the first option (storage tab) as it can also be used for
upload ISO (see below)
*ISO pool: Support to upload ISO*
This has a similar idea from downloading ISO from URL, but instead of
providing a URL the user will select a local file on client side.
POST /storagepools/ISO/storagevolumes/ {'name': <vol-name>, 'filepath':
filepath}
For the UI we also have the same 2 options from downloading ISO from URL
feature
I prefer the first one (storage tab) as we can use the same UI for both
cases, download and upload.
While selection "Add ISO" option, a new dialog will be displayed with a
radio box: a browser input box (for upload) and a simple input box for
URL (for download). The user can only select one option and confirm the
operation.
*Create template from VM (rotru)*
This will use the same backing storage mechanism used to create a
template from a IMG file.
POST /templates {'name': <template-name>, 'vm': <vm-name>}
About the UI:
We have 2 options:
1) When creating a template:
Add an option "From guest"; then list all guests to user choose one
(or multiples ? ) and do the requests
2) On guests Actions menu
Add an option "Create template" and do the request
I prefer the first option (while creating a template)
*Discover Kimchi peers(alinefm)*
This will use openSLP to register and discover Kimchi peers in the same
network.
GET /peers will return the Kimchi peers
[{server: "https://1.2.3.4:8001"}, {server: "https://1.2.3.5:8001"}...]
I think this feature should be optional, ie, we will have a config file
entry that can be enabled/disabled - as this feature is not critical for
virtualization and requires additional software installation (openSLP)
and configuration.
federation = enable|disable
When enabled, the kimchi server will be register in the openSLP tool and
when requesting /peers we will search for peers
When disabled, /peers will return an empty list. Maybe it would be good
to also display a message on UI "Federation feature is disabled for your
Kimchi server."
To display that message, we need to update /config/capabilities to also
return this info.
GET /config/capabilities
{ ...
federation: enable|disable
}
*Migration(Simon Jin)*
This task was added after we have finished the 1.3 Plan. So it would be
difficult to accommodate the UI for it in out current plan.
So I'd say I only expect to have the backend done for 1.3 release.
POST /vms/<name>/migrate {server: <other-kimchi-server-ip>}
*Asynchronous event notify(Sheldon Feng/Zhengsheng Zhou)[backend]*
*Asynchronous event notify(WenWang)[UI]*
This is an important and big task. But I am not sure we will be able to
finish it on time for 1.3 release.
We have already started some discussion on ML "[Kimchi-devel] [RFC]
Improve task management for kimchi"
(http://lists.ovirt.org/pipermail/kimchi-devel/2014-June/006230.html)
*Authorization: Allow admin user change permission settings when VM is
running (WenWang)
*The idea is display the same "Edit" dialog view when vm is running or not.
And when vm is running only enable the information that can be updated
with running vm for editing.
That way we eliminate the "Manage Media" option and keep the UI
consistent. And also provide a way to user checks the vm configuration
(https://github.com/kimchi-project/kimchi/issues/387)
*VM ticket: update novnc/spice code (sheldon)*
POST /vms/<name>/connect will automatically set a random console
password for the VM and sets its expiration time to 10 seconds later.
When accessing the console URL, we need to pass the password as a
parameter (password=...) so noVNC/Spice can do the connection to the
guest console.
*Display iSCSI targets when iSCSI server is provided (backend done in
3c6c5c) (Yu Xin)*
It is similar to what we did for NFS servers. But in this case we will
list the iSCSI targets for a given server.
GET /storageservers?_target_type=iscsi
[
{ "host":"127.0.0.1"
"port":"3260"
}
]
GET
/storageservers/<server>/storagetargets?_target_type=iscsi&_server_port=<port>
[
{
"host":"127.0.0.1",
"target":"iqn.2003-01.org.linux-iscsi.localhost.x8664:sn.edb1a004dc57",
"target_type":"iscsi"
}
]
-----------
And thanks for your patience if you were reading this line now! :-)
I know it is a big email but it could not be different based on the
tasks we have for sprint 2.
Feel free to use it as base for your RFC email.
I am looking forward to see all that done.
Regards,
Aline Manera
4
9
GET /peers
[
"https://ubuntu-vm:8001",
"https://rhel-vm:8001",
]
How to test it?
- Install openslp and openslp-server
- Start opnslp service: servicd slpd start
- Make sure to open openSLP port (427 UDP and TCP) on firewall
I tested it using VMs as they are in the same host network.
TODO:
#1: Add option on Kimchi config file to enable/disable this feature
#2: Update /config/capabilities to return if federation is enabled or not
GET /config/capabilities
{ ...
federation: enable|disable
}
#3: Create README-federation file to provide details on how to enable this feature
#4: Update API.md, mockmodel and test cases
Aline Manera (1):
Discover Kimchi peers using openSLP
src/kimchi/control/peers.py | 29 ++++++++++++++++++++++++++
src/kimchi/model/peers.py | 50 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 79 insertions(+)
create mode 100644 src/kimchi/control/peers.py
create mode 100644 src/kimchi/model/peers.py
--
1.9.3
2
3

14 Aug '14
Whenever an user tries to detach a storage from a guest, the
confirmation message always contains "CDROM". This patch fix this
problem, so when the storage type is "disk", it shows the right
message.
Signed-off-by: Rodrigo Trujillo <rodrigo.trujillo(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.guest_edit_main.js | 2 ++
ui/pages/guest-edit.html.tmpl | 4 ++--
ui/pages/i18n.json.tmpl | 1 +
3 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 7d24b44..01d8045 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -107,6 +107,8 @@ kimchi.guest_edit_main = function() {
confirm : i18n['KCHAPI6002M'],
cancel : i18n['KCHAPI6003M']
};
+ if ($(this).data('type') == "disk")
+ settings['content'] = i18n['KCHVMCD6009M'];
var dev = $(this).data('dev');
kimchi.confirm(settings, function() {
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index f24f7de..ed7ddeb 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -169,7 +169,7 @@
title='$_("Replace")'>
</button>
<button class="guest-edit-cdrom-button detach"
- data-vm="{vm}" data-dev="{dev}"
+ data-vm="{vm}" data-dev="{dev}" data-type="{type}"
title='$_("Detach")'>
</button>
<button class="guest-edit-cdrom-button save hidden"
@@ -213,7 +213,7 @@
</span>
<span class="action-area">
<button class="guest-edit-cdrom-button detach"
- data-vm="{vm}" data-dev="{dev}"
+ data-vm="{vm}" data-dev="{dev}" data-type="{type}"
title="$_("Detach")">
</button>
</span>
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index 697f946..ccfb081 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -135,6 +135,7 @@
"KCHVMCD6006M": "$_("Successfully attached!")",
"KCHVMCD6007M": "$_("Successfully replaced!")",
"KCHVMCD6008M": "$_("Successfully detached!")",
+ "KCHVMCD6009M": "$_("This disk will be detached permanently and you can re-attach it. Continue to detach it?")",
"KCHNET6001E": "$_("The VLAN id must be between 1 and 4094.")",
--
1.9.3
3
2
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
We generated all storage name with 'hd' prefix,
which is not proper for scsi and virtio disk.
So refactor name generation to produce proper device name.
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
src/kimchi/model/vmstorages.py | 46 ++++++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 22 deletions(-)
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py
index b5311db..513f756 100644
--- a/src/kimchi/model/vmstorages.py
+++ b/src/kimchi/model/vmstorages.py
@@ -38,6 +38,7 @@ from kimchi.vmdisks import get_device_xml, get_vm_disk, get_vm_disk_list
from kimchi.vmdisks import DEV_TYPE_SRC_ATTR_MAP
HOTPLUG_TYPE = ['scsi', 'virtio']
+PREFIX_MAP = {'ide': 'hd', 'virtio': 'vd', 'scsi': 'sd'}
def _get_device_bus(dev_type, dom):
@@ -140,17 +141,8 @@ class VMStoragesModel(object):
def create(self, vm_name, params):
dom = VMModel.get_vm(vm_name, self.conn)
- # Use device name passed or pick next
- dev_name = params.get('dev', None)
- if dev_name is None:
- params['dev'] = self._get_storage_device_name(vm_name)
- else:
- devices = self.get_list(vm_name)
- if dev_name in devices:
- raise OperationFailed(
- 'KCHVMSTOR0004E',
- {'dev_name': dev_name, 'vm_name': vm_name})
-
+ params['bus'] = _get_device_bus(params['type'], dom)
+ self._get_storage_device_name(vm_name, params)
# Path will never be blank due to API.json verification.
# There is no need to cover this case here.
params['format'] = 'raw'
@@ -171,7 +163,6 @@ class VMStoragesModel(object):
params['format'] = vol_info['format']
params['path'] = vol_info['path']
params['src_type'] = _check_path(params['path'])
- params['bus'] = _get_device_bus(params['type'], dom)
if (params['bus'] not in HOTPLUG_TYPE
and DOM_STATE_MAP[dom.info()[0]] != 'shutoff'):
raise InvalidOperation('KCHVMSTOR0011E')
@@ -187,16 +178,27 @@ class VMStoragesModel(object):
raise OperationFailed("KCHVMSTOR0008E", {'error': e.message})
return params['dev']
- def _get_storage_device_name(self, vm_name):
- dev_list = [dev for dev in self.get_list(vm_name)
- if dev.startswith('hd')]
- if len(dev_list) == 0:
- return 'hda'
- dev_list.sort()
- last_dev = dev_list.pop()
- # TODO: Improve to device names "greater then" hdz
- next_dev_letter_pos = string.ascii_lowercase.index(last_dev[2]) + 1
- return 'hd' + string.ascii_lowercase[next_dev_letter_pos]
+ def _get_storage_device_name(self, vm_name, params):
+ if params.get('dev') is None:
+ bus_prefix = PREFIX_MAP[params['bus']]
+ dev_list = [dev for dev in self.get_list(vm_name)
+ if dev.startswith(bus_prefix)]
+ if len(dev_list) == 0:
+ params['dev'] = bus_prefix + 'a'
+ else:
+ dev_list.sort()
+ last_dev = dev_list.pop()
+ # TODO: Improve to device names "greater then" hdz
+ next_dev_letter_pos =\
+ string.ascii_lowercase.index(last_dev[2]) + 1
+ params['dev'] =\
+ bus_prefix + string.ascii_lowercase[next_dev_letter_pos]
+
+ devices = self.get_list(vm_name)
+ if params['dev'] in devices:
+ raise OperationFailed(
+ 'KCHVMSTOR0004E',
+ {'dev_name': params['dev'], 'vm_name': vm_name})
def get_list(self, vm_name):
dom = VMModel.get_vm(vm_name, self.conn)
--
1.8.3.2
2
1
lxml only accepts strings as parameters when looking
up xml elements. The port for remote ISO was being
passed in as an int, and a KeyError was thrown.
This patch just casts the ints to strings for the
lookups.
Signed-off-by: Christy Perez <christy(a)linux.vnet.ibm.com>
---
src/kimchi/model/vmstorages.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py
index cd985fa..5543f40 100644
--- a/src/kimchi/model/vmstorages.py
+++ b/src/kimchi/model/vmstorages.py
@@ -68,7 +68,8 @@ def _get_storage_xml(params, ignore_source=False):
if src_type == 'network':
output = urlparse.urlparse(params.get('path'))
host = E.host(name=output.hostname, port=
- output.port or socket.getservbyname(output.scheme))
+ str(output.port) or
+ str(socket.getservbyname(output.scheme)))
source = E.source(protocol=output.scheme, name=output.path)
source.append(host)
disk.append(source)
--
1.9.3
3
4
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
We generated all storage name with 'hd' prefix,
which is not proper for scsi and virtio disk.
So refactor name generation to produce proper device name.
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
src/kimchi/model/vmstorages.py | 46 ++++++++++++++++++++++--------------------
1 file changed, 24 insertions(+), 22 deletions(-)
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py
index b5311db..9ae8584 100644
--- a/src/kimchi/model/vmstorages.py
+++ b/src/kimchi/model/vmstorages.py
@@ -38,6 +38,7 @@ from kimchi.vmdisks import get_device_xml, get_vm_disk, get_vm_disk_list
from kimchi.vmdisks import DEV_TYPE_SRC_ATTR_MAP
HOTPLUG_TYPE = ['scsi', 'virtio']
+prefix_map = {'ide': 'hd', 'virtio': 'vd', 'scsi': 'sd'}
def _get_device_bus(dev_type, dom):
@@ -140,17 +141,8 @@ class VMStoragesModel(object):
def create(self, vm_name, params):
dom = VMModel.get_vm(vm_name, self.conn)
- # Use device name passed or pick next
- dev_name = params.get('dev', None)
- if dev_name is None:
- params['dev'] = self._get_storage_device_name(vm_name)
- else:
- devices = self.get_list(vm_name)
- if dev_name in devices:
- raise OperationFailed(
- 'KCHVMSTOR0004E',
- {'dev_name': dev_name, 'vm_name': vm_name})
-
+ params['bus'] = _get_device_bus(params['type'], dom)
+ self._get_storage_device_name(vm_name, params)
# Path will never be blank due to API.json verification.
# There is no need to cover this case here.
params['format'] = 'raw'
@@ -171,7 +163,6 @@ class VMStoragesModel(object):
params['format'] = vol_info['format']
params['path'] = vol_info['path']
params['src_type'] = _check_path(params['path'])
- params['bus'] = _get_device_bus(params['type'], dom)
if (params['bus'] not in HOTPLUG_TYPE
and DOM_STATE_MAP[dom.info()[0]] != 'shutoff'):
raise InvalidOperation('KCHVMSTOR0011E')
@@ -187,16 +178,27 @@ class VMStoragesModel(object):
raise OperationFailed("KCHVMSTOR0008E", {'error': e.message})
return params['dev']
- def _get_storage_device_name(self, vm_name):
- dev_list = [dev for dev in self.get_list(vm_name)
- if dev.startswith('hd')]
- if len(dev_list) == 0:
- return 'hda'
- dev_list.sort()
- last_dev = dev_list.pop()
- # TODO: Improve to device names "greater then" hdz
- next_dev_letter_pos = string.ascii_lowercase.index(last_dev[2]) + 1
- return 'hd' + string.ascii_lowercase[next_dev_letter_pos]
+ def _get_storage_device_name(self, vm_name, params):
+ if params.get('dev') is None:
+ bus_prefix = prefix_map[params['bus']]
+ dev_list = [dev for dev in self.get_list(vm_name)
+ if dev.startswith(bus_prefix)]
+ if len(dev_list) == 0:
+ params['dev'] = bus_prefix + 'a'
+ else:
+ dev_list.sort()
+ last_dev = dev_list.pop()
+ # TODO: Improve to device names "greater then" hdz
+ next_dev_letter_pos =\
+ string.ascii_lowercase.index(last_dev[2]) + 1
+ params['dev'] =\
+ bus_prefix + string.ascii_lowercase[next_dev_letter_pos]
+
+ devices = self.get_list(vm_name)
+ if params['dev'] in devices:
+ raise OperationFailed(
+ 'KCHVMSTOR0004E',
+ {'dev_name': params['dev'], 'vm_name': vm_name})
def get_list(self, vm_name):
dom = VMModel.get_vm(vm_name, self.conn)
--
1.8.3.2
2
2

[PATCHv2] Reject cdrom update when protocol is not supported by libvirt
by lvroyce@linux.vnet.ibm.com 14 Aug '14
by lvroyce@linux.vnet.ibm.com 14 Aug '14
14 Aug '14
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
v1>v2, fix pep8.
When remote iso protocol is not supported by libvirt,
even if we work around by qemu command,
it will mess up device list logic.
So reject this use case.
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
src/kimchi/i18n.py | 1 +
src/kimchi/model/vmstorages.py | 4 ++++
tests/test_model.py | 16 +++++++++++++++-
3 files changed, 20 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index 2eae7e8..f5ef275 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -255,6 +255,7 @@ messages = {
"KCHVMSTOR0015E": _("Cannot lookup disk path information by given pool/volume: %(error)s"),
"KCHVMSTOR0016E": _("Volume already been used by other vm"),
"KCHVMSTOR0017E": _("Only one of path or pool/volume can be specified to add a new virtual machine disk"),
+ "KCHVMSTOR0018E": _("Remote ISO update/attach is not supported by your libvirt version"),
"KCHREPOS0001E": _("YUM Repository ID must be one word only string."),
"KCHREPOS0002E": _("Repository URL must be an http://, ftp:// or file:// URL."),
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py
index e5be17c..72bdccc 100644
--- a/src/kimchi/model/vmstorages.py
+++ b/src/kimchi/model/vmstorages.py
@@ -29,6 +29,7 @@ from lxml.builder import E
from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
from kimchi.exception import OperationFailed
+from kimchi.model.config import CapabilitiesModel
from kimchi.model.vms import DOM_STATE_MAP, VMModel
from kimchi.model.storagevolumes import StorageVolumeModel
from kimchi.model.utils import get_vm_config_flag
@@ -38,6 +39,7 @@ from kimchi.vmdisks import get_device_xml, get_vm_disk, get_vm_disk_list
from kimchi.vmdisks import DEV_TYPE_SRC_ATTR_MAP
HOTPLUG_TYPE = ['scsi', 'virtio']
+caps = CapabilitiesModel()
def _get_device_bus(dev_type, dom):
@@ -69,6 +71,8 @@ def _get_storage_xml(params, ignore_source=False):
output = urlparse.urlparse(params.get('path'))
port = str(output.port or socket.getservbyname(output.scheme))
host = E.host(name=output.hostname, port=port)
+ if output.scheme not in caps.libvirt_stream_protocols:
+ raise InvalidOperation("KCHVMSTOR0018E")
source = E.source(protocol=output.scheme, name=output.path)
source.append(host)
disk.append(source)
diff --git a/tests/test_model.py b/tests/test_model.py
index 7fd0f00..ef0a0e6 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -29,6 +29,7 @@ import tempfile
import threading
import time
import unittest
+import urlparse
import uuid
@@ -38,6 +39,7 @@ import utils
from kimchi import netinfo
from kimchi.exception import InvalidOperation, InvalidParameter
from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.featuretests import FeatureTests
from kimchi.iscsi import TargetClient
from kimchi.model import model
from kimchi.rollbackcontext import RollbackContext
@@ -397,13 +399,25 @@ class ModelTests(unittest.TestCase):
valid_remote_iso_path = utils.get_remote_iso_path()
cdrom_args = {"type": "cdrom",
"path": valid_remote_iso_path}
+ protocol = urlparse.urlparse(valid_remote_iso_path).scheme
+ if not FeatureTests.libvirt_supports_iso_stream(protocol):
+ self.assertRaises(InvalidOperation, inst.vmstorages_create,
+ vm_name, cdrom_args)
+ return
+
cdrom_dev = inst.vmstorages_create(vm_name, cdrom_args)
storage_list = inst.vmstorages_get_list(vm_name)
self.assertEquals(prev_count + 1, len(storage_list))
# Update remote-backed cdrom with the same ISO
+ protocol = urlparse.urlparse(valid_remote_iso_path).scheme
+ if not FeatureTests.libvirt_supports_iso_stream(protocol):
+ self.assertRaises(InvalidOperation, inst.vmstorage_update,
+ vm_name, cdrom_dev,
+ {'path': valid_remote_iso_path})
+ return
inst.vmstorage_update(vm_name, cdrom_dev,
- {'path': valid_remote_iso_path})
+ {'path': valid_remote_iso_path})
cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev)
cur_cdrom_path = re.sub(":80/", '/', cdrom_info['path'])
self.assertEquals(valid_remote_iso_path, cur_cdrom_path)
--
1.8.3.2
4
3

[PATCH] Remote ISO attachment: fix UI to accept remote ISO link for cdrom attachment
by lvroyce@linux.vnet.ibm.com 14 Aug '14
by lvroyce@linux.vnet.ibm.com 14 Aug '14
14 Aug '14
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
UI just accept local ISO as cdrom attachment before,
enable remote ISO link for it.
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.guest_storage_add.main.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ui/js/src/kimchi.guest_storage_add.main.js b/ui/js/src/kimchi.guest_storage_add.main.js
index 1f4c4bd..a8c5acb 100644
--- a/ui/js/src/kimchi.guest_storage_add.main.js
+++ b/ui/js/src/kimchi.guest_storage_add.main.js
@@ -109,7 +109,7 @@ kimchi.guest_storage_add_main = function() {
});
var validateCDROM = function(settings) {
- if (/^(\/.*)+$/.test(settings['path']))
+ if (/^((https|http|ftp|ftps|tftp|\/).*)+$/.test(settings['path']))
return true;
else {
kimchi.message.error.code('KCHVMSTOR0001E');
--
1.8.3.2
2
2
This patchset adds changes to allow a user to update a CD ROM created
with a remote ISO. It fixes a KeyError issue, improves the way a remote
ISO file is verified, and adds unit tests.
Note: Currently updating a CD ROM created with a local iso using a remote
ISO is not supported. This patchset does not address that issue,
but does add a test for it. This patchset is primarily to correct the
KeyError and prevent a regression when trying to update a remote ISO's
file.
Christy Perez (3):
Fix Key Error when editing CD ROM path
Fix verification of remote ISO
Add unit tests for remote-backed CD ROM updates.
src/kimchi/model/vmstorages.py | 2 +-
src/kimchi/utils.py | 21 +++++++++++-----
tests/test_model.py | 55 +++++++++++++++++++++++++++++++++++++++++-
tests/utils.py | 21 +++++++++++++++-
4 files changed, 90 insertions(+), 9 deletions(-)
--
1.9.3
3
9
From: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>
We don not need a special "save" button for permission form.
All form in guest edit tab can share the same "save" button.
also, we will add a password form, it will also share this button.
Signed-off-by: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>
Signed-off-by: Simon Jin <simonjin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/guest-edit.css | 5 -----
ui/js/src/kimchi.guest_edit_main.js | 40 +++++++++++++++++++++----------------
ui/pages/guest-edit.html.tmpl | 3 ---
3 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/ui/css/theme-default/guest-edit.css b/ui/css/theme-default/guest-edit.css
index 1092cc9..74c2237 100644
--- a/ui/css/theme-default/guest-edit.css
+++ b/ui/css/theme-default/guest-edit.css
@@ -261,8 +261,3 @@
width: 46%;
float: right;
}
-
-#form-guest-edit-permission-save {
- float: right;
- margin-right: 10px;
-}
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 38a2bc0..7d24b44 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -19,13 +19,11 @@ kimchi.guest_edit_main = function() {
var buttonContainer = $('#action-button-container');
$('#guest-edit-tabs').tabs({
beforeActivate: function(event, ui) {
+ var display_list = ['form-guest-edit-general', 'form-guest-edit-permission']
$(buttonContainer).addClass('hidden');
- $("#form-guest-edit-permission-save").addClass('hidden');
var deactivated = ui['newPanel'];
- if($(deactivated).attr('id') === 'form-guest-edit-general') {
+ if(display_list.indexOf($(deactivated).attr('id')) >= 0) {
$(buttonContainer).removeClass('hidden');
- }else if($(deactivated).attr('id') === 'form-guest-edit-permission'){
- $("#form-guest-edit-permission-save").removeClass('hidden');
}
}
});
@@ -335,18 +333,6 @@ kimchi.guest_edit_main = function() {
filterNodes("", $("#permission-avail-users"));
filterNodes("", $("#permission-avail-groups"));
});
- $("#form-guest-edit-permission-save").on("click", function(){
- var content = { users: [], groups: [] };
- $("#permission-sel-users").children().each(function(){
- content.users.push($("label", this).text());
- });
- $("#permission-sel-groups").children().each(function(){
- content.groups.push($("label", this).text());
- });
- kimchi.updateVM(kimchi.selectedGuest, content, function(){
- kimchi.window.close();
- });
- });
};
var initContent = function(guest) {
@@ -393,7 +379,7 @@ kimchi.guest_edit_main = function() {
kimchi.retrieveVM(kimchi.selectedGuest, initContent);
- var submitForm = function(event) {
+ var generalSubmit = function(event) {
$(saveButton).prop('disabled', true);
var data=$('#form-guest-edit-general').serializeObject();
if(data['memory']!=undefined) {
@@ -410,7 +396,27 @@ kimchi.guest_edit_main = function() {
kimchi.message.error(err.responseJSON.reason);
$(saveButton).prop('disabled', false);
});
+ }
+ var permissionSubmit = function(event) {
+ var content = { users: [], groups: [] };
+ $("#permission-sel-users").children().each(function(){
+ content.users.push($("label", this).text());
+ });
+ $("#permission-sel-groups").children().each(function(){
+ content.groups.push($("label", this).text());
+ });
+ kimchi.updateVM(kimchi.selectedGuest, content, function(){
+ kimchi.window.close();
+ });
+ }
+
+ // tap map, "general": 0, "storage": 1, "interface": 2, "permission": 3, "password": 4
+ var submit_map = {0: generalSubmit, 3:permissionSubmit};
+ var submitForm = function(event) {
+ var current = $('#guest-edit-tabs').tabs( "option", "active" );
+ var submitFun = submit_map[current];
+ submitFun && submitFun(event);
event.preventDefault();
};
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index 1c1d7d4..f24f7de 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -150,9 +150,6 @@
<span class="text">$_("Save")</span>
</button>
</div>
- <button id="form-guest-edit-permission-save" class="btn-normal hidden">
- <span class="text">$_("Save")</span>
- </button>
</footer>
</div>
<script id="cdrom-row-tmpl" type="text/html">
--
1.9.3
6
10
Aline pointed out some test failures I'd assumed were the same as
failures on the mater branch, but were actually as result of the
changes to check for valid url paths. The fix was causing a false
negative to be returned for valid repository URLs.
The only change in this patchset versus the previous (v3) version
is 2/3: Fix verification of remote ISO. 1/3 and 3/3 are the same
as version 3.
Christy Perez (3):
Fix Key Error when editing CD ROM path
Fix verification of remote ISO
Add unit tests for remote-backed CD ROM updates.
src/kimchi/model/vmstorages.py | 2 +-
src/kimchi/utils.py | 26 ++++++++++++++++++++++----
tests/test_model.py | 23 ++++++++++++++++++++++-
tests/utils.py | 20 +++++++++++++++++++-
4 files changed, 64 insertions(+), 7 deletions(-)
--
1.9.3
2
6
lxml only accepts strings as parameters when looking
up xml elements. The port for remote ISO was being
passed in as an int, and a KeyError was thrown.
This patch just casts the ints to strings for the
lookups.
Signed-off-by: Christy Perez <christy(a)linux.vnet.ibm.com>
---
src/kimchi/model/vmstorages.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py
index b5311db..e5be17c 100644
--- a/src/kimchi/model/vmstorages.py
+++ b/src/kimchi/model/vmstorages.py
@@ -67,7 +67,7 @@ def _get_storage_xml(params, ignore_source=False):
# Working with url paths
if src_type == 'network':
output = urlparse.urlparse(params.get('path'))
- port = output.port or socket.getservbyname(output.scheme)
+ port = str(output.port or socket.getservbyname(output.scheme))
host = E.host(name=output.hostname, port=port)
source = E.source(protocol=output.scheme, name=output.path)
source.append(host)
--
1.9.3
2
3

[PATCH V3] UI enhancement: Request /config/capabilities as soon as possible
by Aline Manera 13 Aug '14
by Aline Manera 13 Aug '14
13 Aug '14
V2 - V3:
- Make host tab uses the cached capabilities values (Cristian)
V1 - V2:
- Make sure kimchi.capabilities is properly set before using its value (Cristian)
I prefered to do it this way because making a sync request will prevent the
full UI to load even for some seconds.
Aline Manera (1):
UI enhancement: Request /config/capabilities as soon as possible
ui/js/src/kimchi.host.js | 21 +++++++++++--------
ui/js/src/kimchi.main.js | 8 ++++++++
ui/js/src/kimchi.template_add_main.js | 37 ++++++++++++++++++++--------------
ui/js/src/kimchi.template_edit_main.js | 20 ++++++++++--------
4 files changed, 55 insertions(+), 31 deletions(-)
--
1.9.3
3
4

13 Aug '14
Changed the default environment configuration present in the final files
src/kimchi.conf and src/kimchi.config.py from 'development' to
'production' mode.
This modification will prevent that debugging traces be printed in the
front-end when something goes wrong.
Signed-off-by: Paulo Vital <pvital(a)linux.vnet.ibm.com>
---
src/kimchi.conf.in | 2 +-
src/kimchi/config.py.in | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/kimchi.conf.in b/src/kimchi.conf.in
index 369f2c3..ed0270d 100644
--- a/src/kimchi.conf.in
+++ b/src/kimchi.conf.in
@@ -24,7 +24,7 @@
#ssl_key =
# Running environment of the server
-#environment = development
+#environment = production
[logging]
# Log directory
diff --git a/src/kimchi/config.py.in b/src/kimchi/config.py.in
index 68c8a4d..fca32ee 100644
--- a/src/kimchi/config.py.in
+++ b/src/kimchi/config.py.in
@@ -253,7 +253,7 @@ def _get_config():
config.set("server", "cherrypy_port", "8010")
config.set("server", "ssl_cert", "")
config.set("server", "ssl_key", "")
- config.set("server", "environment", "development")
+ config.set("server", "environment", "production")
config.add_section("logging")
config.set("logging", "log_dir", paths.log_dir)
config.set("logging", "log_level", DEFAULT_LOG_LEVEL)
--
1.8.3.1
3
4

[PATCH] Reject cdrom update when protocol is not supported by libvirt
by lvroyce@linux.vnet.ibm.com 12 Aug '14
by lvroyce@linux.vnet.ibm.com 12 Aug '14
12 Aug '14
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Based on Christy's patchset:
[Kimchi-devel] [PATCH v3 1/3] Fix Key Error when editing CD ROM path
When remote iso protocol is not supported by libvirt,
even if we work around by qemu command,
it will mess up device list logic.
So reject this use case.
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
src/kimchi/i18n.py | 1 +
src/kimchi/model/vmstorages.py | 5 ++++-
tests/test_model.py | 13 +++++++++++++
3 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
index 2eae7e8..f5ef275 100644
--- a/src/kimchi/i18n.py
+++ b/src/kimchi/i18n.py
@@ -255,6 +255,7 @@ messages = {
"KCHVMSTOR0015E": _("Cannot lookup disk path information by given pool/volume: %(error)s"),
"KCHVMSTOR0016E": _("Volume already been used by other vm"),
"KCHVMSTOR0017E": _("Only one of path or pool/volume can be specified to add a new virtual machine disk"),
+ "KCHVMSTOR0018E": _("Remote ISO update/attach is not supported by your libvirt version"),
"KCHREPOS0001E": _("YUM Repository ID must be one word only string."),
"KCHREPOS0002E": _("Repository URL must be an http://, ftp:// or file:// URL."),
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py
index e5be17c..7dc8800 100644
--- a/src/kimchi/model/vmstorages.py
+++ b/src/kimchi/model/vmstorages.py
@@ -29,6 +29,7 @@ from lxml.builder import E
from kimchi.exception import InvalidOperation, InvalidParameter, NotFoundError
from kimchi.exception import OperationFailed
+from kimchi.model.config import CapabilitiesModel
from kimchi.model.vms import DOM_STATE_MAP, VMModel
from kimchi.model.storagevolumes import StorageVolumeModel
from kimchi.model.utils import get_vm_config_flag
@@ -38,7 +39,7 @@ from kimchi.vmdisks import get_device_xml, get_vm_disk, get_vm_disk_list
from kimchi.vmdisks import DEV_TYPE_SRC_ATTR_MAP
HOTPLUG_TYPE = ['scsi', 'virtio']
-
+caps = CapabilitiesModel()
def _get_device_bus(dev_type, dom):
try:
@@ -69,6 +70,8 @@ def _get_storage_xml(params, ignore_source=False):
output = urlparse.urlparse(params.get('path'))
port = str(output.port or socket.getservbyname(output.scheme))
host = E.host(name=output.hostname, port=port)
+ if output.scheme not in caps.libvirt_stream_protocols:
+ raise InvalidOperation("KCHVMSTOR0018E")
source = E.source(protocol=output.scheme, name=output.path)
source.append(host)
disk.append(source)
diff --git a/tests/test_model.py b/tests/test_model.py
index 7fd0f00..dc56676 100644
--- a/tests/test_model.py
+++ b/tests/test_model.py
@@ -29,6 +29,7 @@ import tempfile
import threading
import time
import unittest
+import urlparse
import uuid
@@ -38,6 +39,7 @@ import utils
from kimchi import netinfo
from kimchi.exception import InvalidOperation, InvalidParameter
from kimchi.exception import NotFoundError, OperationFailed
+from kimchi.featuretests import FeatureTests
from kimchi.iscsi import TargetClient
from kimchi.model import model
from kimchi.rollbackcontext import RollbackContext
@@ -397,11 +399,22 @@ class ModelTests(unittest.TestCase):
valid_remote_iso_path = utils.get_remote_iso_path()
cdrom_args = {"type": "cdrom",
"path": valid_remote_iso_path}
+ protocol = urlparse.urlparse(valid_remote_iso_path).scheme
+ if not FeatureTests.libvirt_supports_iso_stream(protocol):
+ self.assertRaises(InvalidOperation, inst.vmstorages_create,
+ vm_name, cdrom_args)
+ return
+
cdrom_dev = inst.vmstorages_create(vm_name, cdrom_args)
storage_list = inst.vmstorages_get_list(vm_name)
self.assertEquals(prev_count + 1, len(storage_list))
# Update remote-backed cdrom with the same ISO
+ protocol = urlparse.urlparse(valid_remote_iso_path).scheme
+ if not FeatureTests.libvirt_supports_iso_stream(protocol):
+ self.assertRaises(InvalidOperation, inst.vmstorage_update,
+ vm_name, cdrom_dev, {'path': valid_remote_iso_path})
+ return
inst.vmstorage_update(vm_name, cdrom_dev,
{'path': valid_remote_iso_path})
cdrom_info = inst.vmstorage_lookup(vm_name, cdrom_dev)
--
1.8.3.2
3
3
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
Move qemu namespace from xml top level to cdrom generation,
so that future cdrom update can just modify this part without
affect top level xml.
Signed-off-by: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
---
src/kimchi/vmtemplate.py | 8 +++-----
1 file changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/kimchi/vmtemplate.py b/src/kimchi/vmtemplate.py
index 761bac5..c2101db 100644
--- a/src/kimchi/vmtemplate.py
+++ b/src/kimchi/vmtemplate.py
@@ -150,7 +150,7 @@ class VMTemplate(object):
"""
qemu_stream_cmdline = """
- <qemu:commandline>
+ <qemu:commandline %(ns)s>
<qemu:arg value='-drive'/>
<qemu:arg value='file=%(url)s,if=none,id=drive-%(bus)s0-1-0,\
readonly=on,format=raw'/>
@@ -179,7 +179,7 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/>
url = protocol + "://" + hostname + ":" + str(port) + url_path
if protocol not in libvirt_stream_protocols:
- return qemu_stream_cmdline % {'url': url, 'bus': bus}
+ return qemu_stream_cmdline % {'url': url, 'bus': bus, 'ns': QEMU_NAMESPACE}
params = {'protocol': protocol, 'url_path': url_path,
'hostname': hostname, 'port': port, 'dev': dev, 'bus': bus}
@@ -365,7 +365,6 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/>
params['uuid'] = vm_uuid
params['networks'] = self._get_networks_xml()
params['input_output'] = self._get_input_output_xml()
- params['qemu-namespace'] = ''
params['cdroms'] = ''
params['qemu-stream-cmdline'] = ''
graphics = kwargs.get('graphics')
@@ -389,13 +388,12 @@ drive=drive-%(bus)s0-1-0,id=%(bus)s0-1-0'/>
if not urlparse.urlparse(self.info.get('cdrom', "")).scheme in \
libvirt_stream_protocols and \
params.get('iso_stream', False):
- params['qemu-namespace'] = QEMU_NAMESPACE
params['qemu-stream-cmdline'] = cdrom_xml
else:
params['cdroms'] = cdrom_xml
xml = """
- <domain type='%(domain)s' %(qemu-namespace)s>
+ <domain type='%(domain)s'>
%(qemu-stream-cmdline)s
<name>%(name)s</name>
<uuid>%(uuid)s</uuid>
--
1.8.3.2
2
1
From: Simon Jin <simonjin(a)linux.vnet.ibm.com>
The backend should fitler the user name who can not login the shell.
Signed-off-by: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>
Signed-off-by: Simon Jin <simonjin(a)linux.vnet.ibm.com>
---
src/kimchi/model/host.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/kimchi/model/host.py b/src/kimchi/model/host.py
index a1f8944..39f45d8 100644
--- a/src/kimchi/model/host.py
+++ b/src/kimchi/model/host.py
@@ -419,7 +419,8 @@ def __init__(self, **kargs):
pass
def get_list(self):
- return [user.pw_name for user in pwd.getpwall()]
+ return [user.pw_name for user in pwd.getpwall()
+ if user.pw_shell.rsplit("/")[-1] not in ["nologin", "false"]]
class GroupsModel(object):
--
1.9.3
3
3

12 Aug '14
While trying to create a logical pool in a Ubuntu 14.04 server I got the
following error:
[08/Aug/2014:15:52:38] HTTP Traceback (most recent call last):
File "/home/alinefm/kimchi/src/kimchi/control/base.py", line 110, in lookup
self.info = lookup(*self.model_args)
File "/home/alinefm/kimchi/src/kimchi/model/host.py", line 274, in lookup
return disks.get_partition_details(name)
File "/home/alinefm/kimchi/src/kimchi/disks.py", line 180, in get_partition_details
dev_path = _get_dev_node_path(majmin)
File "/home/alinefm/kimchi/src/kimchi/disks.py", line 41, in _get_dev_node_path
return _get_friendly_dm_path(maj_min)
File "/home/alinefm/kimchi/src/kimchi/disks.py", line 33, in _get_friendly_dm_path
with open(dm_name) as dm_f:
IOError: [Errno 2] No such file or directory: '/sys/dev/block/253:5/dm/name'
It is because, even the maj_min starts with '253:', the file does not
exist.
So check this file exists in order to use it, otherwise get the dev path
from uevent file.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
src/kimchi/disks.py | 17 +++++++----------
1 file changed, 7 insertions(+), 10 deletions(-)
diff --git a/src/kimchi/disks.py b/src/kimchi/disks.py
index 65d3a1d..a092c17 100644
--- a/src/kimchi/disks.py
+++ b/src/kimchi/disks.py
@@ -17,6 +17,7 @@
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+import os.path
import re
import subprocess
@@ -27,18 +28,14 @@ from kimchi.exception import OperationFailed
from kimchi.utils import kimchi_log
-def _get_friendly_dm_path(maj_min):
- """ Returns user friendly dm path given the device number 'major:min' """
- dm_name = "/sys/dev/block/%s/dm/name" % maj_min
- with open(dm_name) as dm_f:
- content = dm_f.read().rstrip('\n')
- return "/dev/mapper/" + content
-
-
def _get_dev_node_path(maj_min):
""" Returns device node path given the device number 'major:min' """
- if maj_min.startswith('253:'):
- return _get_friendly_dm_path(maj_min)
+
+ dm_name = "/sys/dev/block/%s/dm/name" % maj_min
+ if os.path.exists(dm_name):
+ with open(dm_name) as dm_f:
+ content = dm_f.read().rstrip('\n')
+ return "/dev/mapper/" + content
uevent = "/sys/dev/block/%s/uevent" % maj_min
with open(uevent) as ueventf:
--
1.9.3
2
2

12 Aug '14
It depends on:
- [Kimchi-devel] [PATCH v3 1/3] Fix Key Error when editing CD ROM path
- [Kimchi-devel] [PATCH v3 2/3] Fix verification of remote ISO
- [Kimchi-devel] [PATCH v3 3/3] Add unit tests for remote-backed CD ROM updates.
And I still need to update the test cases accordingly.
Aline Manera (1):
bug fix: Allow user updates the cdrom from local to remote file
src/kimchi/model/vmstorages.py | 15 +++++++++++++++
1 file changed, 15 insertions(+)
--
1.9.3
2
3
lxml only accepts strings as parameters when looking
up xml elements. The port for remote ISO was being
passed in as an int, and a KeyError was thrown.
This patch just casts the ints to strings for the
lookups.
Signed-off-by: Christy Perez <christy(a)linux.vnet.ibm.com>
---
src/kimchi/model/vmstorages.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kimchi/model/vmstorages.py b/src/kimchi/model/vmstorages.py
index b5311db..e5be17c 100644
--- a/src/kimchi/model/vmstorages.py
+++ b/src/kimchi/model/vmstorages.py
@@ -67,7 +67,7 @@ def _get_storage_xml(params, ignore_source=False):
# Working with url paths
if src_type == 'network':
output = urlparse.urlparse(params.get('path'))
- port = output.port or socket.getservbyname(output.scheme)
+ port = str(output.port or socket.getservbyname(output.scheme))
host = E.host(name=output.hostname, port=port)
source = E.source(protocol=output.scheme, name=output.path)
source.append(host)
--
1.9.3
1
2

11 Aug '14
/config/capabilities was requested twice for the templates tab: one
request to identify if qemu has spice support and other one to identify
if the qemu has ISO streaming support.
Those requests were made right before using the information, causing a
delay on UI until get the server response.
For example, for distant servers, while creating a new template the
"Remote ISO" option was disabled at the first time and after some time it was
enabled.
The same behavior was identified while editing a template, as the graphics
options blinked to add the spice on it.
To fix it, request /config/capabilities when loading the templates tab to have
this information prior to edit or create a new template.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
ui/js/src/kimchi.template_add_main.js | 27 ++++++++++++---------------
ui/js/src/kimchi.template_edit_main.js | 12 ++++--------
ui/js/src/kimchi.template_main.js | 6 ++++++
3 files changed, 22 insertions(+), 23 deletions(-)
diff --git a/ui/js/src/kimchi.template_add_main.js b/ui/js/src/kimchi.template_add_main.js
index a24a306..a7421d7 100644
--- a/ui/js/src/kimchi.template_add_main.js
+++ b/ui/js/src/kimchi.template_add_main.js
@@ -222,22 +222,19 @@ kimchi.template_add_main = function() {
//1-2 remote iso
$('#iso-remote').css('opacity', 0.3).css('cursor', 'not-allowed');
- kimchi.getCapabilities(function(result) {
- if (result.qemu_stream == true) {
- $('#iso-remote').css('opacity', 1).css('cursor', 'pointer');
-
- $('#iso-remote').click(function() {
- kimchi.switchPage('iso-type-box', 'iso-remote-box');
- initRemoteIsoField();
- initIsoUrlField();
- kimchi.listDistros(function(isos) {
- showRemoteIsoField(isos);
- }, function() {
- });
+ if (kimchi.capabilities.qemu_stream == true) {
+ $('#iso-remote').css('opacity', 1).css('cursor', 'pointer');
+
+ $('#iso-remote').click(function() {
+ kimchi.switchPage('iso-type-box', 'iso-remote-box');
+ initRemoteIsoField();
+ initIsoUrlField();
+ kimchi.listDistros(function(isos) {
+ showRemoteIsoField(isos);
+ }, function() {
});
- }
- }, function() {
- });
+ });
+ }
$('#iso-remote-box-back').click(function() {
kimchi.switchPage('iso-remote-box', 'iso-type-box', 'right');
diff --git a/ui/js/src/kimchi.template_edit_main.js b/ui/js/src/kimchi.template_edit_main.js
index d42108c..281154a 100644
--- a/ui/js/src/kimchi.template_edit_main.js
+++ b/ui/js/src/kimchi.template_edit_main.js
@@ -39,14 +39,10 @@ kimchi.template_edit_main = function() {
}
var options = [{label: 'VNC', value: 'vnc'}];
- kimchi.getCapabilities(function(result) {
- if (result.qemu_spice == true) {
- options.push({label: 'Spice', value: 'spice'})
- }
- }, function() {
- }, function() {
- kimchi.select('template-edit-graphics-list', options);
- });
+ if (kimchi.capabilities.qemu_spice == true) {
+ options.push({label: 'Spice', value: 'spice'})
+ }
+ kimchi.select('template-edit-graphics-list', options);
var scsipools = {};
kimchi.listStoragePools(function(result) {
diff --git a/ui/js/src/kimchi.template_main.js b/ui/js/src/kimchi.template_main.js
index 3c8421d..8a783c3 100644
--- a/ui/js/src/kimchi.template_main.js
+++ b/ui/js/src/kimchi.template_main.js
@@ -83,6 +83,12 @@ kimchi.hideTitle = function() {
};
kimchi.template_main = function() {
+ kimchi.getCapabilities(function(result) {
+ kimchi.capabilities = result;
+ }, function() {
+ kimchi.capabilities = {};
+ });
+
if(kimchi.tabMode['templates'] === 'admin') {
$('.tools').attr('style','display');
$("#template-add").on("click", function(event) {
--
1.9.3
2
8

[PATCH] Fix issue #340: Show error message when server fails to list host partitions
by Aline Manera 11 Aug '14
by Aline Manera 11 Aug '14
11 Aug '14
Add error handlers to kimchi.listHostPartitions() to properly display
error message when an error occurs while trying to list host partitions.
Also update .po files as a new message was added.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
po/en_US.po | 47 +++++++++++++++++++++---------
po/kimchi.pot | 33 ++++++++++++++-------
po/pt_BR.po | 49 +++++++++++++++++++++++---------
po/zh_CN.po | 45 +++++++++++++++++++++--------
ui/js/src/kimchi.storage_main.js | 4 +++
ui/js/src/kimchi.storagepool_add_main.js | 3 ++
ui/pages/i18n.json.tmpl | 1 +
7 files changed, 132 insertions(+), 50 deletions(-)
diff --git a/po/en_US.po b/po/en_US.po
index f85d982..a34da3a 100644
--- a/po/en_US.po
+++ b/po/en_US.po
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: kimchi 0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-07-29 15:52-0300\n"
+"POT-Creation-Date: 2014-08-08 15:27-0300\n"
"PO-Revision-Date: 2013-07-11 17:32-0400\n"
"Last-Translator: Crístian Viana <vianac(a)linux.vnet.ibm.com>\n"
"Language-Team: English\n"
@@ -136,6 +136,16 @@ msgstr ""
"'%(user)s' to the ISO path group, or (not recommended) 'chmod -R o+x "
"'path_to_iso'.Details: %(err)s"
+msgid "Error occurs when probing image os information."
+msgstr ""
+
+msgid "No OS information found in given image."
+msgstr ""
+
+#, python-format
+msgid "Unable to find/read image file %(filename)s"
+msgstr ""
+
#, python-format
msgid "Virtual machine %(name)s already exists"
msgstr "Virtual machine %(name)s already exists"
@@ -337,8 +347,8 @@ msgstr "Template CDROM must be a local or remote ISO file"
msgid "Invalid storage pool URI %(value)s specified for template"
msgstr "Invalid storage pool URI %(value)s specified for template"
-msgid "Specify an ISO image as CDROM to create a template"
-msgstr "Specify an ISO image as CDROM to create a template"
+msgid "Specify an ISO image as CDROM or a base image to create a template"
+msgstr ""
msgid "All networks for the template must be specified in a list."
msgstr "All networks for the template must be specified in a list."
@@ -362,6 +372,13 @@ msgstr "Unable to delete template due error: %(err)s"
msgid "Disk size must be greater than 1GB."
msgstr "Disk size must be greater than 1GB."
+msgid "Template base image must be a valid local image file"
+msgstr ""
+
+#, python-format
+msgid "Cannot identify base image %(path)s format"
+msgstr ""
+
#, python-format
msgid "Storage pool %(name)s already exists"
msgstr "Storage pool %(name)s already exists"
@@ -791,10 +808,6 @@ msgstr "The path '%(value)s' is not valid local/remote path for the device"
msgid "Device name %(dev_name)s already exists in vm %(vm_name)s"
msgstr "Device name %(dev_name)s already exists in vm %(vm_name)s"
-msgid "Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
-msgstr ""
-"Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
-
msgid "Just support cdrom path update"
msgstr "Just support cdrom path update"
@@ -1102,9 +1115,6 @@ msgstr "Device Type"
msgid "The device type. Currently, \"cdrom\" and \"disk\" are supported."
msgstr "The device type. Currently, \"cdrom\" and \"disk\" are supported."
-msgid "Device Bus"
-msgstr "Device Bus"
-
msgid "Storage Pool"
msgstr "Storage Pool"
@@ -1276,9 +1286,6 @@ msgstr "Add"
msgid "Remove"
msgstr "Remove"
-msgid "Failed."
-msgstr "Failed."
-
msgid "Enable"
msgstr "Enable"
@@ -1489,6 +1496,9 @@ msgstr ""
"This storage pool is not persistent. Instead of deactivate, this action will "
"permanently delete it. Would you like to continue?"
+msgid "Unable to retrieve partitions information."
+msgstr ""
+
msgid "CDROM path need to be a valid local path and cannot be blank."
msgstr "CDROM path need to be a valid local path and cannot be blank."
@@ -1843,3 +1853,14 @@ msgstr "No templates found."
msgid "Clone"
msgstr "Clone"
+
+#~ msgid ""
+#~ "Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
+#~ msgstr ""
+#~ "Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
+
+#~ msgid "Device Bus"
+#~ msgstr "Device Bus"
+
+#~ msgid "Failed."
+#~ msgstr "Failed."
diff --git a/po/kimchi.pot b/po/kimchi.pot
index e869e3a..d8354fa 100755
--- a/po/kimchi.pot
+++ b/po/kimchi.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-07-29 15:52-0300\n"
+"POT-Creation-Date: 2014-08-08 15:27-0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL(a)li.org>\n"
@@ -130,6 +130,16 @@ msgid ""
"'path_to_iso'.Details: %(err)s"
msgstr ""
+msgid "Error occurs when probing image os information."
+msgstr ""
+
+msgid "No OS information found in given image."
+msgstr ""
+
+#, python-format
+msgid "Unable to find/read image file %(filename)s"
+msgstr ""
+
#, python-format
msgid "Virtual machine %(name)s already exists"
msgstr ""
@@ -324,7 +334,7 @@ msgstr ""
msgid "Invalid storage pool URI %(value)s specified for template"
msgstr ""
-msgid "Specify an ISO image as CDROM to create a template"
+msgid "Specify an ISO image as CDROM or a base image to create a template"
msgstr ""
msgid "All networks for the template must be specified in a list."
@@ -348,6 +358,13 @@ msgstr ""
msgid "Disk size must be greater than 1GB."
msgstr ""
+msgid "Template base image must be a valid local image file"
+msgstr ""
+
+#, python-format
+msgid "Cannot identify base image %(path)s format"
+msgstr ""
+
#, python-format
msgid "Storage pool %(name)s already exists"
msgstr ""
@@ -744,9 +761,6 @@ msgstr ""
msgid "Device name %(dev_name)s already exists in vm %(vm_name)s"
msgstr ""
-msgid "Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
-msgstr ""
-
msgid "Just support cdrom path update"
msgstr ""
@@ -1041,9 +1055,6 @@ msgstr ""
msgid "The device type. Currently, \"cdrom\" and \"disk\" are supported."
msgstr ""
-msgid "Device Bus"
-msgstr ""
-
msgid "Storage Pool"
msgstr ""
@@ -1209,9 +1220,6 @@ msgstr ""
msgid "Remove"
msgstr ""
-msgid "Failed."
-msgstr ""
-
msgid "Enable"
msgstr ""
@@ -1401,6 +1409,9 @@ msgid ""
"permanently delete it. Would you like to continue?"
msgstr ""
+msgid "Unable to retrieve partitions information."
+msgstr ""
+
msgid "CDROM path need to be a valid local path and cannot be blank."
msgstr ""
diff --git a/po/pt_BR.po b/po/pt_BR.po
index b38ff3f..452e778 100644
--- a/po/pt_BR.po
+++ b/po/pt_BR.po
@@ -20,7 +20,7 @@ msgid ""
msgstr ""
"Project-Id-Version: kimchi 1.0\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-07-29 15:52-0300\n"
+"POT-Creation-Date: 2014-08-08 15:27-0300\n"
"PO-Revision-Date: 2013-06-27 10:48+0000\n"
"Last-Translator: Crístian Viana <vianac(a)linux.vnet.ibm.com>\n"
"Language-Team: Aline Manera <alinefm(a)br.ibm.com>\n"
@@ -156,6 +156,16 @@ msgstr ""
"do caminho da ISO, ou (não recomendado) 'chmod -R o+x 'caminho_para_iso'. "
"Detalhes: %(err)s"
+msgid "Error occurs when probing image os information."
+msgstr ""
+
+msgid "No OS information found in given image."
+msgstr ""
+
+#, python-format
+msgid "Unable to find/read image file %(filename)s"
+msgstr ""
+
#, python-format
msgid "Virtual machine %(name)s already exists"
msgstr "Máquina virtual %(name)s já existe"
@@ -367,8 +377,8 @@ msgstr "Modelo do CDROM deve ser um arquivo ISO local ou remoto"
msgid "Invalid storage pool URI %(value)s specified for template"
msgstr "Storage pool URI inválido %(value)s especificado para modelo"
-msgid "Specify an ISO image as CDROM to create a template"
-msgstr "Especifique uma imagem ISO como CDROM para criar o modelo"
+msgid "Specify an ISO image as CDROM or a base image to create a template"
+msgstr ""
msgid "All networks for the template must be specified in a list."
msgstr "Todas redes para o modelo devem ser especificadas em lista"
@@ -393,6 +403,13 @@ msgstr "Não foi possível remover o modelo devido a um erro: %(err)s"
msgid "Disk size must be greater than 1GB."
msgstr "Tamanho do disco deve ser maior do que 1GB"
+msgid "Template base image must be a valid local image file"
+msgstr ""
+
+#, python-format
+msgid "Cannot identify base image %(path)s format"
+msgstr ""
+
#, python-format
msgid "Storage pool %(name)s already exists"
msgstr "Storage pool %(name)s já existe"
@@ -842,11 +859,6 @@ msgid "Device name %(dev_name)s already exists in vm %(vm_name)s"
msgstr ""
"Nome do dispositivo %(dev_name)s já existe na máquina virtual %(vm_name)s"
-msgid "Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
-msgstr ""
-"Tipo de barramento do dispositivo de destino inválido, tipos suportados: "
-"'ide', 'scsi', 'virtio'"
-
msgid "Just support cdrom path update"
msgstr "Apenas a atualização do caminho do CDROM é suportada"
@@ -1164,9 +1176,6 @@ msgid "The device type. Currently, \"cdrom\" and \"disk\" are supported."
msgstr ""
"O tipo do dispositivo. Atualmente, \"cdrom\" e \"disco\" são suportados."
-msgid "Device Bus"
-msgstr "Barramento do dispositivo"
-
msgid "Storage Pool"
msgstr "Nome do Storage Pool"
@@ -1340,9 +1349,6 @@ msgstr "Adicionar"
msgid "Remove"
msgstr "Remover"
-msgid "Failed."
-msgstr "Falhou."
-
msgid "Enable"
msgstr "Ativar"
@@ -1552,6 +1558,9 @@ msgstr ""
"O storage pool não é persistente. Ao invés de desativar, essa ação vai "
"removê-lo permanentemente. Deseja continuar?"
+msgid "Unable to retrieve partitions information."
+msgstr ""
+
msgid "CDROM path need to be a valid local path and cannot be blank."
msgstr ""
"Caminho do CDROM precisa ser um caminho local válido e não pode ser vazio."
@@ -1907,3 +1916,15 @@ msgstr "Nenhum modelo encontrado."
msgid "Clone"
msgstr "Clonar"
+
+#~ msgid ""
+#~ "Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
+#~ msgstr ""
+#~ "Tipo de barramento do dispositivo de destino inválido, tipos suportados: "
+#~ "'ide', 'scsi', 'virtio'"
+
+#~ msgid "Device Bus"
+#~ msgstr "Barramento do dispositivo"
+
+#~ msgid "Failed."
+#~ msgstr "Falhou."
diff --git a/po/zh_CN.po b/po/zh_CN.po
index aa3032a..83c7018 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -20,7 +20,7 @@ msgid ""
msgstr ""
"Project-Id-Version: kimchi 0.1\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2014-07-29 15:52-0300\n"
+"POT-Creation-Date: 2014-08-08 15:27-0300\n"
"PO-Revision-Date: 2013-06-27 10:48+0000\n"
"Last-Translator: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>\n"
"Language-Team: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>\n"
@@ -149,6 +149,16 @@ msgstr ""
"录下;或为'%(user)s'用户设置访问权限;或将'%(user)s'用户增加到ISO路径的属组;"
"或者为所有的用户增加访问权限 'chmod -R o+x '(不推荐)。详情:%(err)s"
+msgid "Error occurs when probing image os information."
+msgstr ""
+
+msgid "No OS information found in given image."
+msgstr ""
+
+#, python-format
+msgid "Unable to find/read image file %(filename)s"
+msgstr ""
+
#, python-format
msgid "Virtual machine %(name)s already exists"
msgstr "虚拟机%(name)s已经存在"
@@ -344,8 +354,8 @@ msgstr "模板的CDROM必须是一个本地或者远程的ISO文件"
msgid "Invalid storage pool URI %(value)s specified for template"
msgstr "给模板指定了无效的存储池URI %(value)s"
-msgid "Specify an ISO image as CDROM to create a template"
-msgstr "为新模版的CDROM指定一个ISO的镜像"
+msgid "Specify an ISO image as CDROM or a base image to create a template"
+msgstr ""
msgid "All networks for the template must be specified in a list."
msgstr "为模板指定的网络必须在一个列表中"
@@ -368,6 +378,13 @@ msgstr "由于错误:%(err)s,未能删除模板"
msgid "Disk size must be greater than 1GB."
msgstr "磁盘大小必须大于1GB。"
+msgid "Template base image must be a valid local image file"
+msgstr ""
+
+#, python-format
+msgid "Cannot identify base image %(path)s format"
+msgstr ""
+
#, python-format
msgid "Storage pool %(name)s already exists"
msgstr "存储池%(name)s已经存在"
@@ -764,9 +781,6 @@ msgstr "设备路径'%(value)s'不是一个有效的本地/远程路径"
msgid "Device name %(dev_name)s already exists in vm %(vm_name)s"
msgstr "设备%(dev_name)s在vm%(vm_name)s上已存在"
-msgid "Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
-msgstr "无效的目标设备总线类型。支持类型为:'ide','scsi','virtio'"
-
msgid "Just support cdrom path update"
msgstr "仅支持cdrom路径更新"
@@ -1061,9 +1075,6 @@ msgstr "设备类型"
msgid "The device type. Currently, \"cdrom\" and \"disk\" are supported."
msgstr "设备类型。目前支持设备类型:\"cdrom\"和\"disk\"。 "
-msgid "Device Bus"
-msgstr "设备总线"
-
msgid "Storage Pool"
msgstr "存储池"
@@ -1230,9 +1241,6 @@ msgstr "增加"
msgid "Remove"
msgstr "删除"
-msgid "Failed."
-msgstr "失败"
-
msgid "Enable"
msgstr "使能"
@@ -1424,6 +1432,9 @@ msgid ""
"permanently delete it. Would you like to continue?"
msgstr "对于非持久存储池,这个操作将会永久删除存储池而不是停用。是否继续?"
+msgid "Unable to retrieve partitions information."
+msgstr ""
+
msgid "CDROM path need to be a valid local path and cannot be blank."
msgstr "CDROM的路径必须是有效本地路径且不能为空"
@@ -1773,3 +1784,13 @@ msgstr "没有发现模板"
msgid "Clone"
msgstr "制作副本"
+
+#~ msgid ""
+#~ "Invalid target device bus type, type supported: 'ide', 'scsi', 'virtio'"
+#~ msgstr "无效的目标设备总线类型。支持类型为:'ide','scsi','virtio'"
+
+#~ msgid "Device Bus"
+#~ msgstr "设备总线"
+
+#~ msgid "Failed."
+#~ msgstr "失败"
diff --git a/ui/js/src/kimchi.storage_main.js b/ui/js/src/kimchi.storage_main.js
index 0ebdd59..ae3f963 100644
--- a/ui/js/src/kimchi.storage_main.js
+++ b/ui/js/src/kimchi.storage_main.js
@@ -223,6 +223,10 @@ kimchi.initLogicalPoolExtend = function() {
$('.host-partition').html(i18n['KCHPOOL6011M']);
$('.host-partition').addClass('text-help');
}
+ }, function(err) {
+ $('#loading-info', '#logicalPoolExtend').addClass('hidden');
+ $('.host-partition').html(i18n['KCHPOOL6013M'] + '<br/>(' + err.responseJSON.reason + ')');
+ $('.host-partition').addClass('text-help');
});
},
beforeClose : function() { $('.host-partition', '#logicalPoolExtend').empty(); },
diff --git a/ui/js/src/kimchi.storagepool_add_main.js b/ui/js/src/kimchi.storagepool_add_main.js
index 629a37b..ecbc682 100644
--- a/ui/js/src/kimchi.storagepool_add_main.js
+++ b/ui/js/src/kimchi.storagepool_add_main.js
@@ -50,6 +50,9 @@ kimchi.initStorageAddPage = function() {
$('.host-partition').html(i18n['KCHPOOL6011M']);
$('.host-partition').addClass('text-help');
}
+ }, function(err) {
+ $('.host-partition').html(i18n['KCHPOOL6013M'] + '<br/>(' + err.responseJSON.reason + ')');
+ $('.host-partition').addClass('text-help');
});
kimchi.getHostPCIDevices(function(data){
diff --git a/ui/pages/i18n.json.tmpl b/ui/pages/i18n.json.tmpl
index f1478a7..697f946 100644
--- a/ui/pages/i18n.json.tmpl
+++ b/ui/pages/i18n.json.tmpl
@@ -162,6 +162,7 @@
"KCHPOOL6010M": "$_("Looking for available partitions ...")",
"KCHPOOL6011M": "$_("No available partitions found.")",
"KCHPOOL6012M": "$_("This storage pool is not persistent. Instead of deactivate, this action will permanently delete it. Would you like to continue?")",
+ "KCHPOOL6013M": "$_("Unable to retrieve partitions information.")",
"KCHVMSTOR0001E": "$_("CDROM path need to be a valid local path and cannot be blank.")",
"KCHVMSTOR0002E": "$_("Disk pool or volume cannot be blank.")"
--
1.9.3
2
2

11 Aug '14
Changelog from v2:
- Rename one last occurrence of "_validate_repo_url" to the correct name.
Crístian Viana (3):
Issue #377: Validate repository URLs
typo: Fix "repositorie"
repository: Remove error message prefix
src/kimchi/mockmodel.py | 25 ++++++++++++++++---
src/kimchi/repositories.py | 11 +++++++++
src/kimchi/utils.py | 15 ++++++++++++
tests/test_model.py | 42 ++++++++++++++++++++++++++++++--
tests/test_rest.py | 41 +++++++++++++++++++++++++++++--
ui/js/src/kimchi.repository_add_main.js | 3 +--
ui/js/src/kimchi.repository_edit_main.js | 3 +--
ui/pages/i18n.json.tmpl | 1 -
8 files changed, 129 insertions(+), 12 deletions(-)
--
1.9.3
2
5

[PATCH V2] UI enhancement: Request /config/capabilities as soon as possible
by Aline Manera 09 Aug '14
by Aline Manera 09 Aug '14
09 Aug '14
V1 - V2:
- Make sure kimchi.capabilities is properly set before using its value (Cristian)
I prefered to do it this way because making a sync request will prevent the
full UI to load even for some seconds.
Aline Manera (1):
UI enhancement: Request /config/capabilities as soon as possible
ui/js/src/kimchi.main.js | 8 ++++++++
ui/js/src/kimchi.template_add_main.js | 37 ++++++++++++++++++++--------------
ui/js/src/kimchi.template_edit_main.js | 20 ++++++++++--------
3 files changed, 42 insertions(+), 23 deletions(-)
--
1.9.3
1
1
This patchset adds changes to allow a user to update a CD ROM created
with a remote ISO. It fixes a KeyError issue, improves the way a remote
ISO file is verified, and adds unit tests.
Note: Currently updating a CD ROM created with a local iso using a remote
ISO is not supported. This patchset does not address that issue,
but does add a test for it. This patchset is primarily to correct the
KeyError and prevent a regression when trying to update a remote ISO's
file.
Christy Perez (3):
Fix Key Error when editing CD ROM path
Fix verification of remote ISO
Add unit tests for remote-backed CD ROM updates.
src/kimchi/model/vmstorages.py | 2 +-
src/kimchi/utils.py | 21 +++++++++++-----
tests/test_model.py | 55 +++++++++++++++++++++++++++++++++++++++++-
tests/utils.py | 21 +++++++++++++++-
4 files changed, 90 insertions(+), 9 deletions(-)
--
1.9.3
1
3

08 Aug '14
v1->v2:
- changed the timeout of cherrypy to 10 minutes
Daniel Henrique Barboza (1):
Increasing nginx proxy timeout
src/kimchi/server.py | 5 +++++
src/nginx.conf.in | 7 +++++++
2 files changed, 12 insertions(+)
--
1.8.3.1
3
3

07 Aug '14
Changelog from v3:
- Fix code style errors
Crístian Viana (3):
Issue #377: Validate repository URLs
typo: Fix "repositorie"
repository: Remove error message prefix
src/kimchi/mockmodel.py | 25 +++++++++++++++++---
src/kimchi/repositories.py | 11 +++++++++
src/kimchi/utils.py | 16 +++++++++++++
tests/test_model.py | 39 ++++++++++++++++++++++++++++++--
tests/test_rest.py | 39 ++++++++++++++++++++++++++++++--
ui/js/src/kimchi.repository_add_main.js | 3 +--
ui/js/src/kimchi.repository_edit_main.js | 3 +--
ui/pages/i18n.json.tmpl | 1 -
8 files changed, 125 insertions(+), 12 deletions(-)
--
1.9.3
2
4
Do not hard coded x86_64 while running feature tests.
It prevents kimchi to run in different platforms.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
src/kimchi/featuretests.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/kimchi/featuretests.py b/src/kimchi/featuretests.py
index e401c23..8964098 100644
--- a/src/kimchi/featuretests.py
+++ b/src/kimchi/featuretests.py
@@ -59,7 +59,7 @@ SIMPLE_VM_XML = """
<name>A_SIMPLE_VM</name>
<memory unit='KiB'>10240</memory>
<os>
- <type arch='x86_64' machine='pc'>hvm</type>
+ <type>hvm</type>
<boot dev='hd'/>
</os>
</domain>"""
--
1.9.3
2
2
spice.css was added to source code but the build process was not updated
accordingly.
Fix it.
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
configure.ac | 1 +
contrib/kimchi.spec.fedora.in | 1 +
contrib/kimchi.spec.suse.in | 1 +
ui/css/Makefile.am | 2 +-
ui/css/spice/Makefile.am | 20 ++++++++++++++++++++
5 files changed, 24 insertions(+), 1 deletion(-)
create mode 100644 ui/css/spice/Makefile.am
diff --git a/configure.ac b/configure.ac
index a260f7d..d9b991a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -98,6 +98,7 @@ AC_CONFIG_FILES([
ui/Makefile
ui/css/Makefile
ui/css/novnc/Makefile
+ ui/css/spice/Makefile
ui/css/fonts/Makefile
ui/css/fonts/novnc/Makefile
ui/images/Makefile
diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in
index acc9bc0..0c6f934 100644
--- a/contrib/kimchi.spec.fedora.in
+++ b/contrib/kimchi.spec.fedora.in
@@ -166,6 +166,7 @@ rm -rf $RPM_BUILD_ROOT
%{_datadir}/kimchi/ui/css/fonts/novnc/Orbitron700.*
%endif
%{_datadir}/kimchi/ui/css/novnc/base.css
+%{_datadir}/kimchi/ui/css/spice/spice.css
%{_datadir}/kimchi/ui/css/theme-default.min.css
%{_datadir}/kimchi/ui/images/*.png
%{_datadir}/kimchi/ui/images/*.ico
diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in
index 7e082dc..2d3d271 100644
--- a/contrib/kimchi.spec.suse.in
+++ b/contrib/kimchi.spec.suse.in
@@ -87,6 +87,7 @@ rm -rf $RPM_BUILD_ROOT
%{_datadir}/kimchi/ui/css/fonts/novnc/Orbitron700.*
%endif
%{_datadir}/kimchi/ui/css/novnc/base.css
+%{_datadir}/kimchi/ui/css/spice/spice.css
%{_datadir}/kimchi/ui/css/theme-default.min.css
%{_datadir}/kimchi/ui/images/*.png
%{_datadir}/kimchi/ui/images/*.ico
diff --git a/ui/css/Makefile.am b/ui/css/Makefile.am
index 9a2a29f..a4f575d 100644
--- a/ui/css/Makefile.am
+++ b/ui/css/Makefile.am
@@ -15,7 +15,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-SUBDIRS = novnc
+SUBDIRS = novnc spice
if FONTS
SUBDIRS += fonts
diff --git a/ui/css/spice/Makefile.am b/ui/css/spice/Makefile.am
new file mode 100644
index 0000000..1271663
--- /dev/null
+++ b/ui/css/spice/Makefile.am
@@ -0,0 +1,20 @@
+#
+# Kimchi
+#
+# Copyright IBM, Corp. 2014
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+cssdir = $(datadir)/kimchi/ui/css/spice
+
+dist_css_DATA = spice.css
--
1.9.3
2
2
From: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>
V3 -> V4:
test case not pass. fix it.
V2 -> V3:
do not make ticket as sub-resource of a VM
V1 -> V2:
make ticket as sub-resource of a VM
A ticket is the credential to access VM.
Only who get the ticket can access a VM.
test this patch set:
set the ticket
$ sudo curl -k -u <user>:<password> -H "Content-Type: application/json" -H \
"Accept: application/json" https://localhost:8001/vms/test-vm-8/ \
-X PUT -d '{"ticket": {"passwd": "abcd"}}'
get the ticket
$ sudo curl -k -u <user>:<password> -H "Content-Type: application/json" -H \
"Accept: application/json" https://localhost:8001/vms/test-vm-8/
ShaoHe Feng (5):
vm ticket in backend: update API.md
vm ticket in backend: update controller and API.json
vm ticket in backend: update model
vm ticket in backend: update mockmodel
vm ticket in backend: update test case
docs/API.md | 7 +++++
src/kimchi/API.json | 16 +++++++++++
src/kimchi/control/vms.py | 3 +-
src/kimchi/i18n.py | 2 ++
src/kimchi/mockmodel.py | 14 +++++++++
src/kimchi/model/vms.py | 73 +++++++++++++++++++++++++++++++++++++++++++++++
tests/test_mockmodel.py | 2 +-
tests/test_model.py | 17 ++++++++++-
tests/test_rest.py | 12 ++++++++
9 files changed, 143 insertions(+), 3 deletions(-)
--
1.9.3
4
11
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
v5>v6,
Delete wrong volume type parameter in to_volume_list to avoid breaking volume filtering
v4>v5,
Because '/tmp' used tmpfs, but kimchi used 'cache=none' when create vm, so when create image
on '/tmp' result in create vm fails, fix this error.
Also clear some files created in test. (Aline)
v3>v4,
Aggreated image scanning and name generate logic to vmtemplate.py
to avoid duplicate code.
Updated testcases accordingly.
v2>v3,
Clear unused iso link,
Adding mockmodel and tests
How to test:
create a image using:
POST /templates {'name':'mytemp', 'disks':[{'base':'a_base_img_path'}]}
create a vm using:
POST /vms {'template': '/templates/mytemp', 'pool'....}
Royce Lv (7):
Add image probe function
Change doc and api specification
Change 'cdrom' to a optional param
Fix: Prevent iso links filling in osinfo.py
Create volume based on backing store image
Update mockmodel of base img vm
Add tests for image based template
Makefile.am | 1 +
contrib/DEBIAN/control.in | 4 +-
contrib/kimchi.spec.fedora.in | 2 +
contrib/kimchi.spec.suse.in | 2 +
docs/API.md | 3 +-
docs/README.md | 9 ++-
src/kimchi/API.json | 8 ++-
src/kimchi/control/storagevolumes.py | 2 +-
src/kimchi/control/templates.py | 2 +-
src/kimchi/exception.py | 4 ++
src/kimchi/i18n.py | 8 ++-
src/kimchi/imageinfo.py | 66 ++++++++++++++++++++++
src/kimchi/mockmodel.py | 23 ++++----
src/kimchi/model/templates.py | 12 +---
src/kimchi/model/vms.py | 1 +
src/kimchi/osinfo.py | 24 +-------
src/kimchi/vmtemplate.py | 105 +++++++++++++++++++++++++----------
tests/test_mockmodel.py | 12 ++--
tests/test_model.py | 31 +++++++++++
tests/test_osinfo.py | 8 ---
tests/test_rest.py | 44 +++++++++++++--
tests/test_vmtemplate.py | 25 ++++++---
22 files changed, 288 insertions(+), 108 deletions(-)
create mode 100644 src/kimchi/imageinfo.py
--
1.8.3.2
2
8

06 Aug '14
Changelog from v1:
- Put function "validate_repo_url" in "src/kimchi/utils.py".
Crístian Viana (3):
Issue #377: Validate repository URLs
typo: Fix "repositorie"
repository: Remove error message prefix
src/kimchi/mockmodel.py | 25 ++++++++++++++++---
src/kimchi/repositories.py | 11 +++++++++
src/kimchi/utils.py | 15 ++++++++++++
tests/test_model.py | 42 ++++++++++++++++++++++++++++++--
tests/test_rest.py | 41 +++++++++++++++++++++++++++++--
ui/js/src/kimchi.repository_add_main.js | 3 +--
ui/js/src/kimchi.repository_edit_main.js | 3 +--
ui/pages/i18n.json.tmpl | 1 -
8 files changed, 129 insertions(+), 12 deletions(-)
--
1.9.3
2
5

[PATCH 0/3] Fix: Do not pass 'bus' param when attaching disk
by lvroyce@linux.vnet.ibm.com 06 Aug '14
by lvroyce@linux.vnet.ibm.com 06 Aug '14
06 Aug '14
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
We passed 'IDE' for default value when attaching cdrom,
but 'IDE' bus is not supported on some platform(e.g. power),
In fact, kimchi has a embeded scheme to decide which bus is proper,
so let backend to decide which bus to use when attaching disk.
Royce Lv (3):
Delete 'bus' param from backend
Delete 'bus' selection from UI
Update testcases for bus type decision making
src/kimchi/API.json | 6 ------
src/kimchi/model/vmstorages.py | 3 +--
tests/test_model.py | 34 ++++++++++++++++++------------
ui/js/src/kimchi.guest_storage_add.main.js | 28 ------------------------
ui/pages/guest-storage-add.html.tmpl | 13 ------------
5 files changed, 22 insertions(+), 62 deletions(-)
--
1.8.3.2
3
6

06 Aug '14
Hi all,
I can take this bug whereas there is another issue that blocking this
progress. I cannot connect to vnc server using iPad with neither Safari
nor Chrome. The Chrome on PC works just fine. Please take a look at the
connection problem before we go through this issue.
Wang Wen
2
5
The 60 seconds nginx timeout is creating some issues with actions
that takes some time to complete, like creating a VM with a NFS
template. After 60 seconds without a backend answer, the user is
presented with a 504 Gateway Timeout error.
This patch increases the timeout to 600 seconds, 10 minutes. This is
enough time to process all the backend requests and ensure that a
504 error will only occur in a legitimate error in the backend.
Signed-off-by: Daniel Henrique Barboza <danielhb(a)linux.vnet.ibm.com>
---
src/nginx.conf.in | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/nginx.conf.in b/src/nginx.conf.in
index 3a8dfc5..1d1a398 100644
--- a/src/nginx.conf.in
+++ b/src/nginx.conf.in
@@ -38,6 +38,13 @@ http {
access_log /var/log/nginx/access.log main;
sendfile on;
+ # Timeout set to 10 minutes to avoid the 504 Gateway Timeout
+ # when Kimchi is processing a request.
+ proxy_connect_timeout 600;
+ proxy_send_timeout 600;
+ proxy_read_timeout 600;
+ send_timeout 600;
+
server {
listen $proxy_ssl_port ssl;
--
1.8.3.1
2
1
From: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
Signed-off-by: Yu Xin Huo <huoyuxin(a)linux.vnet.ibm.com>
---
ui/css/theme-default/guest-edit.css | 86 ++++++++++++++++++++++++++++++++++-
ui/js/src/kimchi.api.js | 55 ++++++++++++++++++++++
ui/js/src/kimchi.guest_edit_main.js | 66 +++++++++++++++++++++++++++
ui/pages/guest-edit.html.tmpl | 28 +++++++++++
4 files changed, 233 insertions(+), 2 deletions(-)
diff --git a/ui/css/theme-default/guest-edit.css b/ui/css/theme-default/guest-edit.css
index 1092cc9..c9b43bd 100644
--- a/ui/css/theme-default/guest-edit.css
+++ b/ui/css/theme-default/guest-edit.css
@@ -17,8 +17,8 @@
*/
#guest-edit-window {
font-size: 13px;
- height: 400px;
- width: 610px;
+ height: 420px;
+ width: 820px;
}
#guest-edit-tabs {
@@ -266,3 +266,85 @@
float: right;
margin-right: 10px;
}
+
+.guest-edit-pci {
+ height: 79%;
+ overflow: auto;
+ font-size: 12px;
+}
+
+.guest-edit-pci .filter {
+ height: 35px;
+ margin-right: 5px;
+ overflow: hidden;
+}
+
+.guest-edit-pci .group {
+ float: right;
+}
+
+.guest-edit-pci .filter .control {
+ border: 1px solid #AAAAAA;
+ font-size: 12px;
+ background-color: white;
+}
+
+.guest-edit-pci .filter select {
+ border-right: 0px!important;
+ border-radius: 7px 0px 0px 7px;
+ padding: 2px 2px 2px 7px;
+ width: 100px;
+}
+
+.guest-edit-pci .filter select option {
+ padding-left: 7px;
+}
+
+.guest-edit-pci .filter input {
+ border-radius: 0px 7px 7px 0px;
+ padding: 3px 3px 3px 10px;
+ width: 200px;
+ font-style: italic;
+}
+
+.guest-edit-pci .header {
+ margin-bottom: 8px;
+ padding-bottom: 2px;
+ font-weight: bold;
+ border-bottom: 1px solid #999999;
+}
+
+.guest-edit-pci .item {
+ margin-bottom: 4px;
+ overflow: hidden;
+}
+
+.guest-edit-pci .cell {
+ display: inline-block;
+ vertical-align: middle;
+ margin-right: 10px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.guest-edit-pci .item button {
+ width: 20px;
+ height: 20px;
+ float: right;
+}
+
+.guest-edit-pci .name {
+ width: 18%;
+ max-width: 18%;
+}
+
+.guest-edit-pci .product {
+ width: 45%;
+ max-width: 45%;
+}
+
+.guest-edit-pci .vendor {
+ width: 25%;
+ max-width: 25%;
+}
diff --git a/ui/js/src/kimchi.api.js b/ui/js/src/kimchi.api.js
index 4562992..ad91ad3 100644
--- a/ui/js/src/kimchi.api.js
+++ b/ui/js/src/kimchi.api.js
@@ -1098,5 +1098,60 @@ var kimchi = {
kimchi.message.error(data.responseJSON.reason);
}
});
+ },
+
+ getHostPCIDevices : function(suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'host/devices?_passthrough=true&_cap=pci',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ getVMPCIDevices : function(id, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'vms/'+encodeURIComponent(id)+'/hostdevs',
+ type : 'GET',
+ contentType : 'application/json',
+ dataType : 'json',
+ resend : true,
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ addVMPCIDevice : function(vm, device, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'vms/'+ encodeURIComponent(vm) +'/hostdevs',
+ type : 'POST',
+ contentType : 'application/json',
+ dataType : 'json',
+ data : JSON.stringify(device),
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
+ },
+
+ removeVMPCIDevice : function(vm, device, suc, err) {
+ kimchi.requestJSON({
+ url : kimchi.url + 'vms/'+ encodeURIComponent(vm) +'/hostdevs/' + encodeURIComponent(device),
+ type : 'DELETE',
+ contentType : 'application/json',
+ dataType : 'json',
+ success : suc,
+ error : err ? err : function(data) {
+ kimchi.message.error(data.responseJSON.reason);
+ }
+ });
}
};
diff --git a/ui/js/src/kimchi.guest_edit_main.js b/ui/js/src/kimchi.guest_edit_main.js
index 38a2bc0..d002add 100644
--- a/ui/js/src/kimchi.guest_edit_main.js
+++ b/ui/js/src/kimchi.guest_edit_main.js
@@ -349,6 +349,71 @@ kimchi.guest_edit_main = function() {
});
};
+ var setupPCIDevice = function(){
+ kimchi.getHostPCIDevices(function(hostPCIs){
+ kimchi.getVMPCIDevices(kimchi.selectedGuest, function(vmPCIs){
+ for(var i=0; i<hostPCIs.length; i++){
+ var itemNode = $.parseHTML(kimchi.substitute($('#pci-tmpl').html(),{
+ name: hostPCIs[i].name,
+ product: hostPCIs[i].product.description,
+ vendor: hostPCIs[i].vendor.description
+ }));
+ $(".body", "#form-guest-edit-pci").append(itemNode);
+ var iconClass = "ui-icon-plus";
+ for(var j=0; j<vmPCIs.length; j++){
+ if(hostPCIs[i].name==vmPCIs[j].name){
+ iconClass = "ui-icon-minus";
+ break;
+ }
+ }
+ $("button", itemNode).button({
+ icons: { primary: iconClass },
+ text: false
+ }).click(function(){
+ var obj = $(this);
+ if(obj.button("option", "icons").primary == "ui-icon-minus"){
+ kimchi.removeVMPCIDevice(kimchi.selectedGuest, obj.parent().prop("id"), function(){
+ obj.button("option", "icons", { primary: "ui-icon-plus" });
+ filterNodes($("select", "#form-guest-edit-pci").val(), $("input", "#form-guest-edit-pci").val());
+ });
+ }else{
+ kimchi.addVMPCIDevice(kimchi.selectedGuest, { name: obj.parent().prop("id") }, function(){
+ obj.button("option", "icons", { primary: "ui-icon-minus" });
+ filterNodes($("select", "#form-guest-edit-pci").val(), $("input", "#form-guest-edit-pci").val());
+ });
+ }
+ });
+ }
+ });
+ });
+ var filterNodes = function(group, text){
+ text = text.toLowerCase();
+ $(".body", "#form-guest-edit-pci").children().each(function(){
+ var textFilter = $(".name", this).text().toLowerCase().indexOf(text)!=-1;
+ textFilter = textFilter || $(".product", this).text().toLowerCase().indexOf(text)!=-1;
+ textFilter = textFilter || $(".vendor", this).text().toLowerCase().indexOf(text)!=-1;
+ var display = "none";
+ var itemGroup = $("button", this).button("option", "icons").primary;
+ if(textFilter){
+ if(group == "all"){
+ display = "";
+ }else if(group=="toAdd" && itemGroup=="ui-icon-plus"){
+ display = ""
+ }else if(group == "added" && itemGroup=="ui-icon-minus"){
+ display = ""
+ }
+ }
+ $(this).css("display", display);
+ });
+ };
+ $("select", "#form-guest-edit-pci").change(function(){
+ filterNodes($(this).val(), $("input", "#form-guest-edit-pci").val());
+ });
+ $("input", "#form-guest-edit-pci").on("keyup", function() {
+ filterNodes($("select", "#form-guest-edit-pci").val(), $(this).val());
+ });
+ };
+
var initContent = function(guest) {
guest['icon'] = guest['icon'] || 'images/icon-vm.png';
$('#form-guest-edit-general').fillWithObject(guest);
@@ -379,6 +444,7 @@ kimchi.guest_edit_main = function() {
setupInterface();
setupPermission();
+ setupPCIDevice();
kimchi.topic('kimchi/vmCDROMAttached').subscribe(onAttached);
kimchi.topic('kimchi/vmCDROMReplaced').subscribe(onReplaced);
diff --git a/ui/pages/guest-edit.html.tmpl b/ui/pages/guest-edit.html.tmpl
index 1c1d7d4..7ae3bfd 100644
--- a/ui/pages/guest-edit.html.tmpl
+++ b/ui/pages/guest-edit.html.tmpl
@@ -41,6 +41,9 @@
<li>
<a href="#form-guest-edit-permission">$_("Permission")</a>
</li>
+ <li>
+ <a href="#form-guest-edit-pci">$_("PCI Device")</a>
+ </li>
</ul>
<form id="form-guest-edit-general">
<fieldset class="guest-edit-fieldset">
@@ -142,6 +145,23 @@
</div>
</div>
</form>
+ <form id="form-guest-edit-pci" class="guest-edit-pci">
+ <div class="filter">
+ <span class="group">
+ <select class="control">
+ <option value="all">$_("All")</option>
+ <option value="toAdd">$_("To Add")</option>
+ <option value="added">$_("Added")</option>
+ </select><input type="text" class="control" placeholder="$_("filter")">
+ </span>
+ </div>
+ <div class="header">
+ <span class="cell name">$_("Name")</span>
+ <span class="cell product">$_("Product")</span>
+ <span class="cell vendor">$_("Vendor")</span>
+ </div>
+ <div class="body"></div>
+ </form>
</div>
</div>
<footer>
@@ -228,6 +248,14 @@
<label>{val}</label>
</div>
</script>
+<script id="pci-tmpl" type="text/html">
+<div class="item" id="{name}">
+ <span class="cell name" title="{name}">{name}</span>
+ <span class="cell product" title="{product}">{product}</span>
+ <span class="cell vendor" title="{vendor}">{vendor}</span>
+ <button></button>
+</div>
+</script>
<script type="text/javascript">
kimchi.guest_edit_main();
</script>
--
1.7.1
1
0
From: Royce Lv <lvroyce(a)linux.vnet.ibm.com>
v4>v5,
Because '/tmp' used tmpfs, but kimchi used 'cache=none' when create vm, so when create image
on '/tmp' result in create vm fails, fix this error.
Also clear some files created in test. (Aline)
v3>v4,
Aggreated image scanning and name generate logic to vmtemplate.py
to avoid duplicate code.
Updated testcases accordingly.
v2>v3,
Clear unused iso link,
Adding mockmodel and tests
How to test:
create a image using:
POST /templates {'name':'mytemp', 'disks':[{'base':'a_base_img_path'}]}
create a vm using:
POST /vms {'template': '/templates/mytemp', 'pool'....}
Royce Lv (7):
Add image probe function
Change doc and api specification
Change 'cdrom' to a optional param
Fix: Prevent iso links filling in osinfo.py
Create volume based on backing store image
Update mockmodel of base img vm
Add tests for image based template
Makefile.am | 1 +
contrib/DEBIAN/control.in | 4 +-
contrib/kimchi.spec.fedora.in | 2 +
contrib/kimchi.spec.suse.in | 2 +
docs/API.md | 3 +-
docs/README.md | 9 ++-
src/kimchi/API.json | 8 ++-
src/kimchi/control/storagevolumes.py | 2 +-
src/kimchi/control/templates.py | 2 +-
src/kimchi/exception.py | 4 ++
src/kimchi/i18n.py | 8 ++-
src/kimchi/imageinfo.py | 66 ++++++++++++++++++++++
src/kimchi/mockmodel.py | 21 +++----
src/kimchi/model/templates.py | 12 +---
src/kimchi/model/vms.py | 1 +
src/kimchi/osinfo.py | 24 +-------
src/kimchi/vmtemplate.py | 104 +++++++++++++++++++++++++----------
tests/test_mockmodel.py | 12 ++--
tests/test_model.py | 31 +++++++++++
tests/test_osinfo.py | 8 ---
tests/test_rest.py | 44 +++++++++++++--
tests/test_vmtemplate.py | 25 ++++++---
22 files changed, 287 insertions(+), 106 deletions(-)
create mode 100644 src/kimchi/imageinfo.py
--
1.8.3.2
3
9
Crístian Viana (3):
Issue #377: Validate repository URLs
typo: Fix "repositorie"
repository: Remove error message prefix
src/kimchi/mockmodel.py | 41 ++++++++++++++++++++++++++++---
src/kimchi/repositories.py | 29 +++++++++++++++++++++-
tests/test_model.py | 42 ++++++++++++++++++++++++++++++--
tests/test_rest.py | 41 +++++++++++++++++++++++++++++--
ui/js/src/kimchi.repository_add_main.js | 3 +--
ui/js/src/kimchi.repository_edit_main.js | 3 +--
ui/pages/i18n.json.tmpl | 1 -
7 files changed, 147 insertions(+), 13 deletions(-)
--
1.9.3
2
7

04 Aug '14
From: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>
set a cookie to store the ticket before connect a VM.
get the password from cookie and pass it to spice and VNC server.
Signed-off-by: ShaoHe Feng <shaohef(a)linux.vnet.ibm.com>
---
ui/js/novnc/main.js | 10 +++++++++-
ui/js/src/kimchi.guest_main.js | 22 ++++++++++++++++------
ui/pages/spice.html.tmpl | 10 +++++++++-
3 files changed, 34 insertions(+), 8 deletions(-)
diff --git a/ui/js/novnc/main.js b/ui/js/novnc/main.js
index 22037ec..84a813b 100644
--- a/ui/js/novnc/main.js
+++ b/ui/js/novnc/main.js
@@ -79,7 +79,15 @@ if (token) {
WebUtil.createCookie('token', token, 1)
}
-password = WebUtil.getQueryVar('password', '');
+// password = WebUtil.getQueryVar('password', '');
+var cookieRe = new RegExp(';?\\\s*(ticketVM)=(\s*[^;]*);?', 'g');
+var match = cookieRe.exec(document.cookie);
+var password = match ? decodeURIComponent(match[2]) : '';
+if (match) {
+ var utcString = new Date().toUTCString();
+ document.cookie = 'ticketVM' + '=; expires=' + utcString;
+}
+
path = WebUtil.getQueryVar('path', 'websockify');
if ((!host) || (!port)) {
diff --git a/ui/js/src/kimchi.guest_main.js b/ui/js/src/kimchi.guest_main.js
index ff6f2e1..1c1428f 100644
--- a/ui/js/src/kimchi.guest_main.js
+++ b/ui/js/src/kimchi.guest_main.js
@@ -150,12 +150,22 @@ kimchi.vmedit = function(event) {
kimchi.openVmConsole = function(event) {
var vm=$(this).closest('li[name=guest]');
var vmObject=vm.data();
- if (vmObject.graphics['type'] == 'vnc') {
- kimchi.vncToVM(vm.attr('id'));
- }
- else if (vmObject.graphics['type'] == 'spice') {
- kimchi.spiceToVM(vm.attr('id'));
- }
+ kimchi.retrieveVM(vm.attr('id'), function(result) {
+ if (result.ticket.expire && result.ticket.expire < 0) {
+ kimchi.message.error('please reset your password!');
+ return;
+ }
+ result.ticket.passwd != null && kimchi.cookie.set("ticketVM", result.ticket.passwd, 100);
+ if (vmObject.graphics['type'] == 'vnc') {
+ kimchi.vncToVM(vm.attr('id'));
+ }
+ else if (vmObject.graphics['type'] == 'spice') {
+ kimchi.spiceToVM(vm.attr('id'));
+ }
+ }, function(err) {
+ kimchi.message.error(err.responseJSON.reason);
+ }
+ );
};
diff --git a/ui/pages/spice.html.tmpl b/ui/pages/spice.html.tmpl
index 213d216..cb7c331 100644
--- a/ui/pages/spice.html.tmpl
+++ b/ui/pages/spice.html.tmpl
@@ -64,6 +64,14 @@
host = getParameter("listen");
port = getParameter("port");
token = getParameter("token");
+ var cookieRe = new RegExp(';?\\\s*(ticketVM)=(\s*[^;]*);?', 'g');
+ var match = cookieRe.exec(document.cookie);
+ password = match ? decodeURIComponent(match[2]) : '';
+ if (match) {
+ var utcString = new Date().toUTCString();
+ document.cookie = 'ticketVM' + '=; expires=' + utcString;
+ }
+
document.getElementById("host").value = host;
document.getElementById("port").value = port;
if ((!host) || (!port)) {
@@ -82,7 +90,7 @@
screen_id : "spice-screen",
dump_id : "debug-div",
message_id : "message-div",
- password : "",
+ password : password,
onerror : spice_error
});
} catch (e) {
--
1.9.3
4
4
As Kimchi depends on some imported code (src/kimchi/websocket.py and
src/kimchi/websockify.py) under LGPLv3, its license also needs to be upgraded
to LGPLv3.
COPYING.LGPL was updated according to http://www.gnu.org/licenses/lgpl-3.0.txt
As all Kimchi files have license header indicating LGPL 2.1 or later,
they don't need to be updated.
For reference: http://gplv3.fsf.org/dd3-faq
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
COPYING | 2 +-
COPYING.LGPL | 657 +++++++++++++++--------------------------------------------
2 files changed, 161 insertions(+), 498 deletions(-)
diff --git a/COPYING b/COPYING
index 6129b48..1701a50 100644
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
Kimchi is distributed pursuant to the terms of two different licenses.
The user interface (located in ui/ in this distribution) is governed by
the Apache License version 2.0. The rest of this distribution is
-governed by the GNU Lesser General Public License version 2.1.
+governed by the GNU Lesser General Public License version 3.
See COPYING.LGPL and COPYING.ASL2.
diff --git a/COPYING.LGPL b/COPYING.LGPL
index 4362b49..65c5ca8 100644
--- a/COPYING.LGPL
+++ b/COPYING.LGPL
@@ -1,502 +1,165 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 2.1, February 1999
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
- Copyright (C) 1991, 1999 Free Software Foundation, Inc.
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
-[This is the first released version of the Lesser GPL. It also counts
- as the successor of the GNU Library Public License, version 2, hence
- the version number 2.1.]
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-Licenses are intended to guarantee your freedom to share and change
-free software--to make sure the software is free for all its users.
-
- This license, the Lesser General Public License, applies to some
-specially designated software packages--typically libraries--of the
-Free Software Foundation and other authors who decide to use it. You
-can use it too, but we suggest you first think carefully about whether
-this license or the ordinary General Public License is the better
-strategy to use in any particular case, based on the explanations below.
-
- When we speak of free software, we are referring to freedom of use,
-not price. Our General Public Licenses are designed to make sure that
-you have the freedom to distribute copies of free software (and charge
-for this service if you wish); that you receive source code or can get
-it if you want it; that you can change the software and use pieces of
-it in new free programs; and that you are informed that you can do
-these things.
-
- To protect your rights, we need to make restrictions that forbid
-distributors to deny you these rights or to ask you to surrender these
-rights. These restrictions translate to certain responsibilities for
-you if you distribute copies of the library or if you modify it.
-
- For example, if you distribute copies of the library, whether gratis
-or for a fee, you must give the recipients all the rights that we gave
-you. You must make sure that they, too, receive or can get the source
-code. If you link other code with the library, you must provide
-complete object files to the recipients, so that they can relink them
-with the library after making changes to the library and recompiling
-it. And you must show them these terms so they know their rights.
-
- We protect your rights with a two-step method: (1) we copyright the
-library, and (2) we offer you this license, which gives you legal
-permission to copy, distribute and/or modify the library.
-
- To protect each distributor, we want to make it very clear that
-there is no warranty for the free library. Also, if the library is
-modified by someone else and passed on, the recipients should know
-that what they have is not the original version, so that the original
-author's reputation will not be affected by problems that might be
-introduced by others.
-
- Finally, software patents pose a constant threat to the existence of
-any free program. We wish to make sure that a company cannot
-effectively restrict the users of a free program by obtaining a
-restrictive license from a patent holder. Therefore, we insist that
-any patent license obtained for a version of the library must be
-consistent with the full freedom of use specified in this license.
-
- Most GNU software, including some libraries, is covered by the
-ordinary GNU General Public License. This license, the GNU Lesser
-General Public License, applies to certain designated libraries, and
-is quite different from the ordinary General Public License. We use
-this license for certain libraries in order to permit linking those
-libraries into non-free programs.
-
- When a program is linked with a library, whether statically or using
-a shared library, the combination of the two is legally speaking a
-combined work, a derivative of the original library. The ordinary
-General Public License therefore permits such linking only if the
-entire combination fits its criteria of freedom. The Lesser General
-Public License permits more lax criteria for linking other code with
-the library.
-
- We call this license the "Lesser" General Public License because it
-does Less to protect the user's freedom than the ordinary General
-Public License. It also provides other free software developers Less
-of an advantage over competing non-free programs. These disadvantages
-are the reason we use the ordinary General Public License for many
-libraries. However, the Lesser license provides advantages in certain
-special circumstances.
-
- For example, on rare occasions, there may be a special need to
-encourage the widest possible use of a certain library, so that it becomes
-a de-facto standard. To achieve this, non-free programs must be
-allowed to use the library. A more frequent case is that a free
-library does the same job as widely used non-free libraries. In this
-case, there is little to gain by limiting the free library to free
-software only, so we use the Lesser General Public License.
-
- In other cases, permission to use a particular library in non-free
-programs enables a greater number of people to use a large body of
-free software. For example, permission to use the GNU C Library in
-non-free programs enables many more people to use the whole GNU
-operating system, as well as its variant, the GNU/Linux operating
-system.
-
- Although the Lesser General Public License is Less protective of the
-users' freedom, it does ensure that the user of a program that is
-linked with the Library has the freedom and the wherewithal to run
-that program using a modified version of the Library.
-
- The precise terms and conditions for copying, distribution and
-modification follow. Pay close attention to the difference between a
-"work based on the library" and a "work that uses the library". The
-former contains code derived from the library, whereas the latter must
-be combined with the library in order to run.
-
- GNU LESSER GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License Agreement applies to any software library or other
-program which contains a notice placed by the copyright holder or
-other authorized party saying it may be distributed under the terms of
-this Lesser General Public License (also called "this License").
-Each licensee is addressed as "you".
-
- A "library" means a collection of software functions and/or data
-prepared so as to be conveniently linked with application programs
-(which use some of those functions and data) to form executables.
-
- The "Library", below, refers to any such software library or work
-which has been distributed under these terms. A "work based on the
-Library" means either the Library or any derivative work under
-copyright law: that is to say, a work containing the Library or a
-portion of it, either verbatim or with modifications and/or translated
-straightforwardly into another language. (Hereinafter, translation is
-included without limitation in the term "modification".)
-
- "Source code" for a work means the preferred form of the work for
-making modifications to it. For a library, complete source code means
-all the source code for all modules it contains, plus any associated
-interface definition files, plus the scripts used to control compilation
-and installation of the library.
-
- Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running a program using the Library is not restricted, and output from
-such a program is covered only if its contents constitute a work based
-on the Library (independent of the use of the Library in a tool for
-writing it). Whether that is true depends on what the Library does
-and what the program that uses the Library does.
-
- 1. You may copy and distribute verbatim copies of the Library's
-complete source code as you receive it, in any medium, provided that
-you conspicuously and appropriately publish on each copy an
-appropriate copyright notice and disclaimer of warranty; keep intact
-all the notices that refer to this License and to the absence of any
-warranty; and distribute a copy of this License along with the
-Library.
- You may charge a fee for the physical act of transferring a copy,
-and you may at your option offer warranty protection in exchange for a
-fee.
-
- 2. You may modify your copy or copies of the Library or any portion
-of it, thus forming a work based on the Library, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) The modified work must itself be a software library.
-
- b) You must cause the files modified to carry prominent notices
- stating that you changed the files and the date of any change.
-
- c) You must cause the whole of the work to be licensed at no
- charge to all third parties under the terms of this License.
-
- d) If a facility in the modified Library refers to a function or a
- table of data to be supplied by an application program that uses
- the facility, other than as an argument passed when the facility
- is invoked, then you must make a good faith effort to ensure that,
- in the event an application does not supply such function or
- table, the facility still operates, and performs whatever part of
- its purpose remains meaningful.
-
- (For example, a function in a library to compute square roots has
- a purpose that is entirely well-defined independent of the
- application. Therefore, Subsection 2d requires that any
- application-supplied function or table used by this function must
- be optional: if the application does not supply it, the square
- root function must still compute square roots.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Library,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Library, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote
-it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Library.
-
-In addition, mere aggregation of another work not based on the Library
-with the Library (or with a work based on the Library) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may opt to apply the terms of the ordinary GNU General Public
-License instead of this License to a given copy of the Library. To do
-this, you must alter all the notices that refer to this License, so
-that they refer to the ordinary GNU General Public License, version 2,
-instead of to this License. (If a newer version than version 2 of the
-ordinary GNU General Public License has appeared, then you can specify
-that version instead if you wish.) Do not make any other change in
-these notices.
-
- Once this change is made in a given copy, it is irreversible for
-that copy, so the ordinary GNU General Public License applies to all
-subsequent copies and derivative works made from that copy.
-
- This option is useful when you wish to copy part of the code of
-the Library into a program that is not a library.
-
- 4. You may copy and distribute the Library (or a portion or
-derivative of it, under Section 2) in object code or executable form
-under the terms of Sections 1 and 2 above provided that you accompany
-it with the complete corresponding machine-readable source code, which
-must be distributed under the terms of Sections 1 and 2 above on a
-medium customarily used for software interchange.
-
- If distribution of object code is made by offering access to copy
-from a designated place, then offering equivalent access to copy the
-source code from the same place satisfies the requirement to
-distribute the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 5. A program that contains no derivative of any portion of the
-Library, but is designed to work with the Library by being compiled or
-linked with it, is called a "work that uses the Library". Such a
-work, in isolation, is not a derivative work of the Library, and
-therefore falls outside the scope of this License.
-
- However, linking a "work that uses the Library" with the Library
-creates an executable that is a derivative of the Library (because it
-contains portions of the Library), rather than a "work that uses the
-library". The executable is therefore covered by this License.
-Section 6 states terms for distribution of such executables.
-
- When a "work that uses the Library" uses material from a header file
-that is part of the Library, the object code for the work may be a
-derivative work of the Library even though the source code is not.
-Whether this is true is especially significant if the work can be
-linked without the Library, or if the work is itself a library. The
-threshold for this to be true is not precisely defined by law.
-
- If such an object file uses only numerical parameters, data
-structure layouts and accessors, and small macros and small inline
-functions (ten lines or less in length), then the use of the object
-file is unrestricted, regardless of whether it is legally a derivative
-work. (Executables containing this object code plus portions of the
-Library will still fall under Section 6.)
-
- Otherwise, if the work is a derivative of the Library, you may
-distribute the object code for the work under the terms of Section 6.
-Any executables containing that work also fall under Section 6,
-whether or not they are linked directly with the Library itself.
-
- 6. As an exception to the Sections above, you may also combine or
-link a "work that uses the Library" with the Library to produce a
-work containing portions of the Library, and distribute that work
-under terms of your choice, provided that the terms permit
-modification of the work for the customer's own use and reverse
-engineering for debugging such modifications.
-
- You must give prominent notice with each copy of the work that the
-Library is used in it and that the Library and its use are covered by
-this License. You must supply a copy of this License. If the work
-during execution displays copyright notices, you must include the
-copyright notice for the Library among them, as well as a reference
-directing the user to the copy of this License. Also, you must do one
-of these things:
-
- a) Accompany the work with the complete corresponding
- machine-readable source code for the Library including whatever
- changes were used in the work (which must be distributed under
- Sections 1 and 2 above); and, if the work is an executable linked
- with the Library, with the complete machine-readable "work that
- uses the Library", as object code and/or source code, so that the
- user can modify the Library and then relink to produce a modified
- executable containing the modified Library. (It is understood
- that the user who changes the contents of definitions files in the
- Library will not necessarily be able to recompile the application
- to use the modified definitions.)
-
- b) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (1) uses at run time a
- copy of the library already present on the user's computer system,
- rather than copying library functions into the executable, and (2)
- will operate properly with a modified version of the library, if
- the user installs one, as long as the modified version is
- interface-compatible with the version that the work was made with.
-
- c) Accompany the work with a written offer, valid for at
- least three years, to give the same user the materials
- specified in Subsection 6a, above, for a charge no more
- than the cost of performing this distribution.
-
- d) If distribution of the work is made by offering access to copy
- from a designated place, offer equivalent access to copy the above
- specified materials from the same place.
-
- e) Verify that the user has already received a copy of these
- materials or that you have already sent this user a copy.
-
- For an executable, the required form of the "work that uses the
-Library" must include any data and utility programs needed for
-reproducing the executable from it. However, as a special exception,
-the materials to be distributed need not include anything that is
-normally distributed (in either source or binary form) with the major
-components (compiler, kernel, and so on) of the operating system on
-which the executable runs, unless that component itself accompanies
-the executable.
-
- It may happen that this requirement contradicts the license
-restrictions of other proprietary libraries that do not normally
-accompany the operating system. Such a contradiction means you cannot
-use both them and the Library together in an executable that you
-distribute.
-
- 7. You may place library facilities that are a work based on the
-Library side-by-side in a single library together with other library
-facilities not covered by this License, and distribute such a combined
-library, provided that the separate distribution of the work based on
-the Library and of the other library facilities is otherwise
-permitted, and provided that you do these two things:
-
- a) Accompany the combined library with a copy of the same work
- based on the Library, uncombined with any other library
- facilities. This must be distributed under the terms of the
- Sections above.
-
- b) Give prominent notice with the combined library of the fact
- that part of it is a work based on the Library, and explaining
- where to find the accompanying uncombined form of the same work.
-
- 8. You may not copy, modify, sublicense, link with, or distribute
-the Library except as expressly provided under this License. Any
-attempt otherwise to copy, modify, sublicense, link with, or
-distribute the Library is void, and will automatically terminate your
-rights under this License. However, parties who have received copies,
-or rights, from you under this License will not have their licenses
-terminated so long as such parties remain in full compliance.
-
- 9. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Library or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Library (or any work based on the
-Library), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Library or works based on it.
-
- 10. Each time you redistribute the Library (or any work based on the
-Library), the recipient automatically receives a license from the
-original licensor to copy, distribute, link with or modify the Library
-subject to these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties with
-this License.
-
- 11. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Library at all. For example, if a patent
-license would not permit royalty-free redistribution of the Library by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Library.
-
-If any portion of this section is held invalid or unenforceable under any
-particular circumstance, the balance of the section is intended to apply,
-and the section as a whole is intended to apply in other circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 12. If the distribution and/or use of the Library is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Library under this License may add
-an explicit geographical distribution limitation excluding those countries,
-so that distribution is permitted only in or among countries not thus
-excluded. In such case, this License incorporates the limitation as if
-written in the body of this License.
-
- 13. The Free Software Foundation may publish revised and/or new
-versions of the Lesser General Public License from time to time.
-Such new versions will be similar in spirit to the present version,
-but may differ in detail to address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Library
-specifies a version number of this License which applies to it and
-"any later version", you have the option of following the terms and
-conditions either of that version or of any later version published by
-the Free Software Foundation. If the Library does not specify a
-license version number, you may choose any version ever published by
-the Free Software Foundation.
-
- 14. If you wish to incorporate parts of the Library into other free
-programs whose distribution conditions are incompatible with these,
-write to the author to ask for permission. For software which is
-copyrighted by the Free Software Foundation, write to the Free
-Software Foundation; we sometimes make exceptions for this. Our
-decision will be guided by the two goals of preserving the free status
-of all derivatives of our free software and of promoting the sharing
-and reuse of software generally.
-
- NO WARRANTY
-
- 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
-WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
-EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
-OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
-KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
-LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
-THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
- 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
-WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
-AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
-FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
-CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
-LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
-RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
-FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
-SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
-DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Libraries
-
- If you develop a new library, and you want it to be of the greatest
-possible use to the public, we recommend making it free software that
-everyone can redistribute and change. You can do so by permitting
-redistribution under these terms (or, alternatively, under the terms of the
-ordinary General Public License).
-
- To apply these terms, attach the following notices to the library. It is
-safest to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least the
-"copyright" line and a pointer to where the full notice is found.
-
- <one line to give the library's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This library is free software; you can redistribute it and/or
- modify it under the terms of the GNU Lesser General Public
- License as published by the Free Software Foundation; either
- version 2.1 of the License, or (at your option) any later version.
-
- This library is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- Lesser General Public License for more details.
-
- You should have received a copy of the GNU Lesser General Public
- License along with this library; if not, write to the Free Software
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
-
-Also add information on how to contact you by electronic and paper mail.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the library, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the
- library `Frob' (a library for tweaking knobs) written by James Random Hacker.
-
- <signature of Ty Coon>, 1 April 1990
- Ty Coon, President of Vice
-
-That's all there is to it!
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
--
1.9.3
2
2
Aline Manera (2):
Remove useless image files
Remove useless jquery files
contrib/kimchi.spec.fedora.in | 2 -
contrib/kimchi.spec.suse.in | 2 -
ui/images/progressing.gif | Bin 1152 -> 0 bytes
ui/images/theme-default/bg-black-linen.png | Bin 84780 -> 0 bytes
ui/images/theme-default/bg-navbar.png | Bin 3043 -> 0 bytes
ui/js/jquery-ui.js | 14936 ---------------------------
ui/js/jquery.min.js | 5 -
7 files changed, 14945 deletions(-)
delete mode 100644 ui/images/progressing.gif
delete mode 100644 ui/images/theme-default/bg-black-linen.png
delete mode 100644 ui/images/theme-default/bg-navbar.png
delete mode 100644 ui/js/jquery-ui.js
delete mode 100644 ui/js/jquery.min.js
--
1.9.3
2
6
Aline Manera (2):
Remove useless image files
Remove useless jquery files
contrib/kimchi.spec.fedora.in | 2 -
contrib/kimchi.spec.suse.in | 2 -
ui/images/progressing.gif | Bin 1152 -> 0 bytes
ui/images/theme-default/bg-black-linen.png | Bin 84780 -> 0 bytes
ui/images/theme-default/bg-navbar.png | Bin 3043 -> 0 bytes
ui/js/jquery-ui.js | 14936 ---------------------------
ui/js/jquery.min.js | 5 -
7 files changed, 14945 deletions(-)
delete mode 100644 ui/images/progressing.gif
delete mode 100644 ui/images/theme-default/bg-black-linen.png
delete mode 100644 ui/images/theme-default/bg-navbar.png
delete mode 100644 ui/js/jquery-ui.js
delete mode 100644 ui/js/jquery.min.js
--
1.9.3
2
3
Hi all,
Due to issue 372:Account for network boot/install leveraging
DHCP/TFTP/(NFS/HTTP/FTP) network installation servers, we need to add a
way to boot the VM without cdrom. here is the ui for that we added a new
choice in the template dialogue and inside it are the available booting
materiel like the local ISO image.
I am working on it and will display more when I finished.
Any comments for this is welcomed
Best Regards
Wang Wen
2
2
By default, all translation files (.mo files) are installed in
%{_prefix}/share/locale
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
contrib/kimchi.spec.fedora.in | 2 +-
contrib/kimchi.spec.suse.in | 2 +-
plugins/sample/po/Makefile.in.in | 2 +-
po/Makefile.in.in | 2 +-
src/kimchi/config.py.in | 6 +++++-
tests/Makefile.am | 1 +
tests/test_config.py.in | 2 +-
7 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in
index ca3f7e1..c153086 100644
--- a/contrib/kimchi.spec.fedora.in
+++ b/contrib/kimchi.spec.fedora.in
@@ -160,7 +160,7 @@ rm -rf $RPM_BUILD_ROOT
%{_datadir}/kimchi/doc/README.md
%{_datadir}/kimchi/doc/kimchi-guest.png
%{_datadir}/kimchi/doc/kimchi-templates.png
-%{_datadir}/kimchi/mo/*/LC_MESSAGES/kimchi.mo
+%{_prefix}/share/locale/*/LC_MESSAGES/kimchi.mo
%{_datadir}/kimchi/config/ui/*.xml
%if %{with_fonts} == 1
%{_datadir}/kimchi/ui/css/fonts/novnc/Orbitron700.*
diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in
index 73304c2..1c81770 100644
--- a/contrib/kimchi.spec.suse.in
+++ b/contrib/kimchi.spec.suse.in
@@ -81,7 +81,7 @@ rm -rf $RPM_BUILD_ROOT
%{_datadir}/kimchi/doc/README.md
%{_datadir}/kimchi/doc/kimchi-guest.png
%{_datadir}/kimchi/doc/kimchi-templates.png
-%{_datadir}/kimchi/mo/*/LC_MESSAGES/kimchi.mo
+%{_prefix}/share/locale/*/LC_MESSAGES/kimchi.mo
%{_datadir}/kimchi/config/ui/*.xml
%if %{with_fonts} == 1
%{_datadir}/kimchi/ui/css/fonts/novnc/Orbitron700.*
diff --git a/plugins/sample/po/Makefile.in.in b/plugins/sample/po/Makefile.in.in
index 0eb3dda..52ab81c 100644
--- a/plugins/sample/po/Makefile.in.in
+++ b/plugins/sample/po/Makefile.in.in
@@ -26,7 +26,7 @@ prefix = @prefix@
exec_prefix = @exec_prefix@
datarootdir = @datarootdir@
datadir = @datadir@
-localedir = @datadir@/kimchi/mo
+localedir = @prefix@/share/locale
gettextsrcdir = $(datadir)/gettext/po
INSTALL = @INSTALL@
diff --git a/po/Makefile.in.in b/po/Makefile.in.in
index 239e4e5..d01fb31 100644
--- a/po/Makefile.in.in
+++ b/po/Makefile.in.in
@@ -26,7 +26,7 @@ prefix = @prefix@
exec_prefix = @exec_prefix@
datarootdir = @datarootdir@
datadir = @datadir@
-localedir = @datadir@/kimchi/mo
+localedir = @prefix@/share/locale
gettextsrcdir = $(datadir)/gettext/po
INSTALL = @INSTALL@
diff --git a/src/kimchi/config.py.in b/src/kimchi/config.py.in
index 3aef8cf..e18159c 100644
--- a/src/kimchi/config.py.in
+++ b/src/kimchi/config.py.in
@@ -102,7 +102,11 @@ class Paths(object):
self.plugins_dir = self.add_prefix('plugins')
self.ui_dir = self.add_prefix('ui')
- self.mo_dir = self.add_prefix('mo')
+
+ if self.installed:
+ self.mo_dir = '@prefix@/share/locale'
+ else:
+ self.mo_dir = self.add_prefix('mo')
def get_prefix(self):
if __file__.startswith("/"):
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e89a572..7f5294e 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -28,6 +28,7 @@ noinst_SCRIPTS = run_tests.sh
do_substitution = \
sed -e 's,[@]HAVE_PYMOD_UNITTEST[@],$(HAVE_PYMOD_UNITTEST),g' \
+ -e 's,[@]prefix[@],$(prefix),g' \
-e 's,[@]PYTHON_VERSION[@],$(PYTHON_VERSION),g' \
-e 's,[@]kimchidir[@],$(pythondir)/kimchi,g' \
-e 's,[@]pkgdatadir[@],$(pkgdatadir),g'
diff --git a/tests/test_config.py.in b/tests/test_config.py.in
index f349419..75ffdd7 100644
--- a/tests/test_config.py.in
+++ b/tests/test_config.py.in
@@ -51,7 +51,7 @@ class ConfigTests(unittest.TestCase):
self.assertInstalledPath(paths.src_dir, '@kimchidir@')
self.assertInstalledPath(paths.plugins_dir, '@kimchidir@/plugins')
self.assertInstalledPath(paths.ui_dir, '@pkgdatadir@/ui')
- self.assertInstalledPath(paths.mo_dir, '@pkgdatadir@/mo')
+ self.assertInstalledPath(paths.mo_dir, '@prefix@/share/locale')
def test_uninstalled_paths(self):
Paths.get_prefix = lambda self: '/home/user/kimchi'
--
1.9.3
2
2
Aline Manera (2):
Update copyright date
Add missing license headers
plugins/__init__.py | 2 +-
plugins/sample/__init__.py | 2 +-
plugins/sample/i18n.py | 2 +-
plugins/sample/model.py | 2 +-
plugins/sample/ui/pages/i18n.json.tmpl | 17 +++++++++++++++++
src/kimchi/__init__.py | 2 +-
src/kimchi/asynctask.py | 2 +-
src/kimchi/auth.py | 2 +-
src/kimchi/cachebust.py | 2 +-
src/kimchi/config.py.in | 2 +-
src/kimchi/control/__init__.py | 2 +-
src/kimchi/control/base.py | 2 +-
src/kimchi/control/config.py | 2 +-
src/kimchi/control/debugreports.py | 2 +-
src/kimchi/control/host.py | 2 +-
src/kimchi/control/interfaces.py | 2 +-
src/kimchi/control/networks.py | 2 +-
src/kimchi/control/plugins.py | 2 +-
src/kimchi/control/storagepools.py | 2 +-
src/kimchi/control/storagevolumes.py | 2 +-
src/kimchi/control/tasks.py | 2 +-
src/kimchi/control/templates.py | 2 +-
src/kimchi/control/utils.py | 2 +-
src/kimchi/control/vm/__init__.py | 2 +-
src/kimchi/control/vm/ifaces.py | 2 +-
src/kimchi/control/vms.py | 2 +-
src/kimchi/disks.py | 2 +-
src/kimchi/distroloader.py | 2 +-
src/kimchi/exception.py | 2 +-
src/kimchi/featuretests.py | 2 +-
src/kimchi/i18n.py | 2 +-
src/kimchi/iscsi.py | 2 +-
src/kimchi/isoinfo.py | 2 +-
src/kimchi/kvmusertests.py | 2 +-
src/kimchi/mockmodel.py | 2 +-
src/kimchi/model/__init__.py | 2 +-
src/kimchi/model/config.py | 2 +-
src/kimchi/model/debugreports.py | 2 +-
src/kimchi/model/host.py | 2 +-
src/kimchi/model/interfaces.py | 2 +-
src/kimchi/model/libvirtconnection.py | 2 +-
src/kimchi/model/libvirtstoragepool.py | 2 +-
src/kimchi/model/model.py | 2 +-
src/kimchi/model/networks.py | 2 +-
src/kimchi/model/plugins.py | 2 +-
src/kimchi/model/storagepools.py | 2 +-
src/kimchi/model/storageservers.py | 2 +-
src/kimchi/model/storagetargets.py | 2 +-
src/kimchi/model/storagevolumes.py | 2 +-
src/kimchi/model/tasks.py | 2 +-
src/kimchi/model/templates.py | 2 +-
src/kimchi/model/utils.py | 2 +-
src/kimchi/model/vmifaces.py | 2 +-
src/kimchi/model/vms.py | 2 +-
src/kimchi/netinfo.py | 2 +-
src/kimchi/network.py | 2 +-
src/kimchi/networkxml.py | 2 +-
src/kimchi/objectstore.py | 2 +-
src/kimchi/osinfo.py | 2 +-
src/kimchi/rollbackcontext.py | 2 +-
src/kimchi/root.py | 2 +-
src/kimchi/scan.py | 2 +-
src/kimchi/screenshot.py | 2 +-
src/kimchi/server.py | 2 +-
src/kimchi/sslcert.py | 2 +-
src/kimchi/template.py | 2 +-
src/kimchi/utils.py | 2 +-
src/kimchi/vmtemplate.py | 2 +-
src/kimchi/vnc.py | 2 +-
src/kimchi/xmlutils.py | 2 +-
tests/iso_gen.py | 2 +-
tests/test_authorization.py | 2 +-
tests/test_config.py.in | 2 +-
tests/test_exception.py | 2 +-
tests/test_mockmodel.py | 2 +-
tests/test_model.py | 2 +-
tests/test_networkxml.py | 2 +-
tests/test_osinfo.py | 2 +-
tests/test_plugin.py | 2 +-
tests/test_rest.py | 2 +-
tests/test_server.py | 2 +-
tests/test_storagepool.py | 2 +-
tests/test_vmtemplate.py | 2 +-
tests/utils.py | 2 +-
ui/css/theme-default/button.css | 2 +-
ui/css/theme-default/circleGauge.css | 2 +-
ui/css/theme-default/error.css | 2 +-
ui/css/theme-default/form.css | 2 +-
ui/css/theme-default/framework.css | 2 +-
ui/css/theme-default/grid.css | 2 +-
ui/css/theme-default/guest-edit.css | 2 +-
ui/css/theme-default/guest-media.css | 2 +-
ui/css/theme-default/guest-storage-add.css | 2 +-
ui/css/theme-default/host.css | 2 +-
ui/css/theme-default/line-chart.css | 2 +-
ui/css/theme-default/list.css | 2 +-
ui/css/theme-default/login-window.css | 2 +-
ui/css/theme-default/message.css | 2 +-
ui/css/theme-default/nav-tree.css | 2 +-
ui/css/theme-default/navbar.css | 2 +-
ui/css/theme-default/network.css | 2 +-
ui/css/theme-default/popover.css | 2 +-
ui/css/theme-default/report-add.css | 19 ++++++++++++++++++-
ui/css/theme-default/repository-add.css | 2 +-
ui/css/theme-default/repository-edit.css | 2 +-
ui/css/theme-default/reset.css | 2 +-
ui/css/theme-default/storage.css | 2 +-
ui/css/theme-default/template-edit.css | 2 +-
ui/css/theme-default/template.css | 2 +-
ui/css/theme-default/template_add.css | 2 +-
ui/css/theme-default/template_list.css | 2 +-
ui/css/theme-default/tile-check.css | 2 +-
ui/css/theme-default/toolbar.css | 2 +-
ui/css/theme-default/topbar.css | 2 +-
ui/css/theme-default/window.css | 2 +-
ui/js/src/kimchi.api.js | 2 +-
ui/js/src/kimchi.cookie.js | 2 +-
ui/js/src/kimchi.form.js | 2 +-
ui/js/src/kimchi.grid.js | 2 +-
ui/js/src/kimchi.guest_add_main.js | 2 +-
ui/js/src/kimchi.guest_edit_main.js | 2 +-
ui/js/src/kimchi.guest_main.js | 2 +-
ui/js/src/kimchi.guest_media_main.js | 2 +-
ui/js/src/kimchi.guest_storage_add.main.js | 2 +-
ui/js/src/kimchi.host.js | 2 +-
ui/js/src/kimchi.lang.js | 2 +-
ui/js/src/kimchi.line-chart.js | 2 +-
ui/js/src/kimchi.main.js | 2 +-
ui/js/src/kimchi.message.js | 2 +-
ui/js/src/kimchi.network.js | 2 +-
ui/js/src/kimchi.popable.js | 2 +-
ui/js/src/kimchi.report_add_main.js | 2 +-
ui/js/src/kimchi.repository_add_main.js | 2 +-
ui/js/src/kimchi.repository_edit_main.js | 2 +-
ui/js/src/kimchi.select.js | 2 +-
ui/js/src/kimchi.storage_main.js | 2 +-
ui/js/src/kimchi.storagepool_add_main.js | 2 +-
ui/js/src/kimchi.substitute.js | 2 +-
ui/js/src/kimchi.template_add_main.js | 2 +-
ui/js/src/kimchi.template_edit_main.js | 2 +-
ui/js/src/kimchi.template_main.js | 2 +-
ui/js/src/kimchi.topic.js | 2 +-
ui/js/src/kimchi.user.js | 2 +-
ui/js/src/kimchi.utils.js | 2 +-
ui/js/src/kimchi.window.js | 2 +-
ui/js/widgets/button-dropDown.js | 2 +-
ui/js/widgets/circleGauge.js | 2 +-
ui/js/widgets/combobox.js | 2 +-
ui/js/widgets/filter-select.js | 2 +-
ui/js/widgets/select-menu.js | 2 +-
ui/pages/error.html.tmpl | 2 +-
ui/pages/guest-add.html.tmpl | 2 +-
ui/pages/guest-edit.html.tmpl | 2 +-
ui/pages/guest-media.html.tmpl | 2 +-
ui/pages/guest-storage-add.html.tmpl | 2 +-
ui/pages/guest.html.tmpl | 2 +-
ui/pages/help/kimchi.css | 17 +++++++++++++++++
ui/pages/i18n.json.tmpl | 2 +-
ui/pages/kimchi-ui.html.tmpl | 2 +-
ui/pages/report-add.html.tmpl | 2 +-
ui/pages/repository-add.html.tmpl | 2 +-
ui/pages/repository-edit.html.tmpl | 2 +-
ui/pages/storagepool-add.html.tmpl | 2 +-
ui/pages/tabs/guests.html.tmpl | 2 +-
ui/pages/tabs/host.html.tmpl | 2 +-
ui/pages/tabs/network.html.tmpl | 2 +-
ui/pages/tabs/storage.html.tmpl | 2 +-
ui/pages/tabs/templates.html.tmpl | 2 +-
ui/pages/template-add.html.tmpl | 2 +-
ui/pages/template-edit.html.tmpl | 2 +-
170 files changed, 219 insertions(+), 168 deletions(-)
--
1.9.3
2
4
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
ui/images/progressing.gif | Bin 1152 -> 0 bytes
ui/images/theme-default/bg-black-linen.png | Bin 84780 -> 0 bytes
ui/images/theme-default/bg-navbar.png | Bin 3043 -> 0 bytes
3 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 ui/images/progressing.gif
delete mode 100644 ui/images/theme-default/bg-black-linen.png
delete mode 100644 ui/images/theme-default/bg-navbar.png
diff --git a/ui/images/progressing.gif b/ui/images/progressing.gif
deleted file mode 100644
index 6552d41d9d4c874091bb51931c6adf64a95e8bd0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1152
zcmZ?wbhEHb6k!ly*v!E2|NsBHckiA#b7s?~O&uK_IXO9@p`oUxrgCy}4A_9;e{Mh5
zkYH!W09PYD17=2`8pWS1oFWVy3_2k7AY&O=+5}E^`WY^hIhD@I;2`qkhUamCHs#2?
zD{Sl5ddqAu;o;E8n07H&VTOd$9w+z3Od8dz)1OOlg@hfQ?6P6gg)Nhut~h8s_iHy|
zTx?f<DfNuF4qs+in1T70o7P)P87f1<g5(0~t-`bz%`;<c7;R*Hby&0+Cd*D$nyx)7
zxH>hoEs9-2GEYbl<TO?^r*$AZO&~F#<dR{_!xK!0-EtLPZ*p|yWoF`%>SmZA?aQ<9
zUT3kA$h=8X2{%vLaEeZ2FfFvxHCy+@)2xTXXx&nW19L8~I4mhFCLSR*N5YUN_g8RC
zxvG$d3rC2Yoni>Hr8Z+mk+UtMt&Fyriq3S|nYyz*lh|s_)v7fmBnyPWjzbSOeSwpn
ztM^*GK3gr@WX{W=;H4@s$Eix`o#&Z+UC|vq;?Mm$*qz>q{MpOLKD$V>lW^GM@gRp<
zTfqAxmTMW#NQtz1Ow(GUENaLTnG@~OSSl%)a-~@^*=_L^Wu6LazRZG$dI^iu%U-z%
zg<00g*_8=1ySk){v9Pk(*~<9qvQD2qQDdsojG1{NY_a__TQemj7eK-u-Fqm>f>utm
zWMS=L&jJU059@Suul#8<RCK0juuU^pO9X~|9`=wYHdzoCYCehQmh?GGSt;IO&EY61
z^|cU^WKC}pQa3GC)^bS)J5HFrADAqr$@=Tg_KIeUMR6Q@nr+9QW_y9zNsLj!T)@YO
zmFW>EN`0IXPrZ>S5M3ptD%hrTMQhc8T>+_g4tF#}E)IMpc<k8J3C>bEce!tTI4V-^
zEEL|{;B8wW%<SUK!k*>CY&#hksVX{CWv466RGb|aXRcN%49rjY*aHrnWY;d4a(eT<
sn9_<U58lHODbH9KGQC;#6@s@G?AG7t-SQycLr1Fr#FC=U9tH+$0KoWQ9smFU
diff --git a/ui/images/theme-default/bg-black-linen.png b/ui/images/theme-default/bg-black-linen.png
deleted file mode 100644
index 3c069709ac86b6a812590c93c300829bbaa005f0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 84780
zcmaI7bC4%Nw=LS9Hm7adwr$(SuWj3!wr$(C-P5*h>&<u0J@>so?tK+eQJEQQue~yJ
zXCx{zLP1U(4jKy@2nYyHQbI%t2ncxp-+K-Q`mcm4N#X0?2h&+p!&%wR%-PM*(G*C~
z#Lmc+KoVeRZmMKzXyV~8Zps4$1WIJ7qT#F|E5l`M2cR?j4~EViVE@k=2#AN@-QLjH
z+SHlA$kg1@mY3+JvzLg#(u9{tomG}W)?V1u!cxN1(Nx(}PQ}>M+L+UXh@X#u$DQjR
z0l?JRkiZ>aW9!7_&P()Pyj=g%|0$*?BKR+evo$Z#e~Z$PRUi<yb2KGjrDLEqW?*9=
zU}L0XWM*M!XQv@xVqj#TXJDddWTItc=VE5!VqhTnUk}kgZ;mErTuLHh|I63E950cD
zv$H)HJ-wTo8=V_7ot>jOJtHS4=YKeum}vhYXq`N4oekY-ZJmh!he5>D$=K1--r3U5
zmf$~(hDLTS&b&nbl>T=W0QUcz*4F8NHPgQaqjxv7r)Q*N_)nGo2PiB1{|^NK{x{mm
zS;_SO)%$-Wc2e=MH>FoHb+U7DH2&9dX2kz-WzQw-Xlm$e=cr<5XY)U;s9<5|Z0BTQ
zXHOuk%t|0@Xl!ZwpZtH}Wo5Y}ZJnGAZH-MOMR<w+G0<6BnsBiQvoW%8FtT%Sa56J8
ziZTc>h%yNZF>$bQiU_i>iwXaat%#km3&7OY`G0Ip{$E>;|EukPLIGg^&$EcBqou2<
ziI}4ufZ)HX%w_q1(8A8j%EH9L#_*3&<o~SYzimza?^@XYuUhE;iJ|{b$Nqmh`hSZ4
zMbCed|2u5|W&U^Io7(=1cE^8VZC>NC^lwNoD9EUYVq#)qVPRooW8>i9;Ns%q;o;%q
z<NpguLPA0!A|hg9ViFP(Qc_YfGBR><ataEH<3Yn7AYgG5NfAL63{0?oc{h?Z7XnwC
zPhcK%7Co=kEgTPWkn}P<fHzh;l4b(BtNDmmz`jih^L8iw+=5Vy`^$DJDL^IMO`oJJ
zrZXPtk7&)15ShZ#gbC>K;hn8WWCrsj=D1&v|L8aT_m`Y2A><_6NtTW$AL1_)v(BAu
zb<2$}E?4|F<1hUm52xj?UWARkJFhSG55UXy3jW};@6WX%hTro%E}v_!@8}ah-%wny
z-)HHK-`D#=SX}ShaM%uy@2BVI$<F6ZFMe*=jjzMU*Vh66xBiBo_v`E4;Gq5MbZ6MK
zpZE9EEwFFLSMLs7=ja~4O>b=&!WR8^@3h^H9}eGj?@!&^+2{$v(}CY7#mCoy9UecP
zZwse4PT1q?$IjQWYn#uDpG|A#%>w<mp|{-Z{oNTmKHj%joBn0)VrSb~`KENtUAOaD
z{sY^dV(i&?4_7-9OSc?X{MjD}U8Uq|eci?wC+BtDCW5kE0xn~zT~F&ZCS+iyuaw)Y
z;zuE_n9#A3o`=`V;CH$GK2FGG?fPq4Uo&gz`qUm#-b)Oaf=x$0gRV#IGjoGFHO}~J
zh@r^Xl1;nzmVV0@Ml2xWw>WS|gic6-VAqu}E$rxw5TYWo3hGE1qQ!8y-17oXKkI4E
zttWqCkC3!WUV<KuH`+!XrC$PS9Br<dRj4Q37x&x}y5QoN1~>$?9ov79Cr>4<%y~gJ
z*N};7C0#4%iT-ZhP~k{{6<ok|U}{XR=X}?CWKJgypjzq(BVksXCL#gH|C(y1ZL^#y
zqz00Jt(LW81s2-q#1YE{A3wJ<1fj<g2%7uI;sqBL5IdTE)x`w(DF9plwVJnHtMU-H
zaXtwwLsUAgkAaGds{G!`-i)s)GJuOWBvA>ro_k2vdyR-Cn8Y+jFt~B2OVYNGAEv6L
zl-tgTezf9yS}bO&dZcV#L_R350NZMA>XZAk&z?bZ+&K^*;=5~3t$yWS7FT@``%nly
zU9uFP$)*uzP_Eu$g-+{U7!4Oyp4_&?^y`<~;<$QyZS;a!Iz7z1E1XKQLif>klkh%B
zGTkx8i8COdhaD9SW~U?AAf^{0GZOpb{?L+p?)mFj71!i)q1$hZH9m-*-6sGU1VqA)
z+b}fPBrr{DV@zcAb*QWXxp5hOs9-pI;n4{Ne6HYkcj&l>Ai;5y=X{o*(QM=Zw-;NO
zMv8RutUPf$s~!-?mHgFDn_e2a`SZH5qH2m4c`cTO1U2lDR4&RI#Kf%kHC)GULiU1)
zjN$D2YTpaC3}eq*LxQ$Tgm!ByB%wYlj^9DjK2q|VZ#(xWpUY6~!tb5F`owrUa5wk^
zEh8e)U}3h0_FlveztO*E<tkVpuNZB^eSKWo(1~r6Ht)14;tNT=aB!dcClgU{JTcSx
zCW(W)7-^f|ESZ~EKqb`5%O)<$s~uH3T5=5!nspaHGbwY>gN5~9VUXaavI-QYu?gvL
z^P~?c{26#|xadqjEzL<fY?JOjkm6={_#>UQslEia4LPD|27#Yn_*@J#7YOlQ9UR;~
z9}=6n^vhLq=^Nj)ja3DgJ2JdGz&2#cmL5$VFuSQVlFf<QSxs3JPGZtJ>fXupsKbxc
zRgmHZDQ#5HC7^#+7g$N0tg6VU=xR<@a^)Y>zn*KERYbcY+W3?w$9cOn*6=A*<)dJj
z^#c{9ZN@5=F~g^aTok^$a!!K9BqeEE1u?g(F0*QZ=O;s96-#`soVZL!ZOWSfNY&-^
zzN;pSb{TI&+f`K!6}PI1$J;fP4~ceL9;A^;<ylU6#>KKg9*fdhWc$B<7G9u*M+-h|
z3G-hlk!>`%1G?n3J)b5!=95HR2MKLs5&obMp?<XP@ufR2OBysi*U!9VEednw6`l-Q
zSW3Uyj|21}JWQI#Z=Ms<KH?&VI`y}P2}P3nQB(Ef%!s)ZUr5T7oLN4(!22Evro8h2
z@-LQk<Bw43PgaCNHbZLdqN$>6LnMFinVM8sCLa=irq8TTPKTo<!of5cgx*Fv(_Ru1
zT9+(;7=%inSNF`!855i-#q7MumxOP;lcVr?VUfkipfU#ySBZHcmpvC_UY<L@vQK??
zpFeK+*bW}*H=YfX-oFPaegs@z`#|{(dIQwKOLM!NKufO(al+ExV~M}2Mt9v0gM04O
z<weTntB@9VFW!J`<G8P76J$qaPMv5XrcT(q7-U`OBpIPluD{p!FR&Tu<+RZ7<4=$?
z1Q&Jl8io&{VR@67z(9{|^9EztLN4emQI^>hdt{MR#G&%&5Ile@`c1-N0UvxDjHC(v
zU=Uh(7yxiLxy4}5fI(Oa%i8>_-9N)3=izky@c^enR5)BLO<+bSn6L~|ILQDDGaM2-
z#r<GU!!Xttkxy7%VSagCoI_Qq4aFgTU{VwkD-YrmdES_a0A%mQQr$%<pL;w2MF^{g
zR8MS*PcdLC&-!YH+cvt7_qr(Rxo_lQ07BCqgUIPH=}+)Y-v!ei+eDD_S7SnkSjiyc
zr7dZ^SFB^Bleh$e|5pzDDk_3yZPfgN8-gyRee+J@rEO+Q%M8GT19`}O+4V!ZJz3aO
zv$ykru`ggea?rjs{mwT2^ceu{EwX}w{7O<Au7@)0I2ufi(haX<!ls2b3w0S{|0}H_
zkv*MSujX`-XwgD8QPx4NYN;U&t(2<qtyiynch(Ot@)FkJTDMupJ*rqMmsWmjUGJjc
z%Ucy#<ID6GWv(b37#EI604BRWG_z!Go>JxTLvLNg(EtJRN>@6Vx};;yLqn9Q-En7}
z&N?;LH7){p-=O)x!a7X^NFlc?pd_Obvuu1}K=(O>$((-Pzj(ltpjfxD<8~5NXQP(P
ze8tQIqNmT|;cb(>(<xn7{e*n_Q=itQhWxGTKIG*>b#GQ?yKMH#0}i#TUZPB5nFHMU
zu;99FrZZ(#`IIu~(Lh_340m79zO|h2>8bC=EkgS}ztEG!POJ4}9XVcd30<fS6hY*K
zNG3QKfmKWWI?D(#Uz|&1S3jgakw8~TSi)XXfESd$jtx@9^RL=n{t}yS3OjOpd+ZoI
z`0kE8q#(WeifhMXqf`HKu8OE38M}4bd*fo~Lt^XKKp2M%3)g(cUhykP9wrc7++`DG
zL{8mJw!oK*e@*L_z)}o1509N*r-Y#acTqeZaZJmH@1@@lgLhIGz|G|}l%dPZ`xElz
z1|BNb@bc$v&kL)w-V4VzD0t_y6k!l_6aL^hApH7A<U6agv=_TGXT&XT!|mp@1^?G}
zSJeFztq)GiCaMqlfG+!H#|14u?|A<m!=_tcSdLd2=spfhw5xD@;tS`seI8!GKHM_V
z!?C>l_AffTLE$SCM7Q3-mD=5`AXw9%K|P@p(FD75(Dr41xJ>Vj4RyMbX*nD#pH(QE
zg;G8^?H-WGq!EBkF8vM&9>P?T*Gf#khw)7wa$ja_tlgIye)O-A)C7<s@3^2z=-e%z
z85oKKCxo6SkWt*7U=HXz0z+)*{6V;C`-3if@uK((ALuin+F$2EB%`&+%dtLY*Y9Bk
z!VdSbxOlWr8yi8Ft_YdtZXBOSl3wFThygN)WR#tASYE0m=&JJ36G}TutuQR9X^7Ph
zc+Gh$`-~md#^~##;M5f)nDLe<rs7GdCYlZYl<*V3LuihxhcBun{tTzAlbwvf1rIS7
z*r%wmXr_eJfLXVM5Jvl~5Ky?%N6hu)X=);!v@2li3$4@<44c3p9cg8+_M8=0CQ1TN
z=7hrlDB?Vm!?<N|a9M9-UVG5IJRDn9eFI>5H^`4c!w!jES}HfrZr@#6ix{GL@CG~8
zjZHR7V)>xm3P}nO@xeR$1DszPT0dSM_wb*YV9(mQy?4WwdW(AeQn<ZR-($AN1b&LS
z_n#xf;7Z*n0akL_ukW__QZ<(Dysm0W>tX$Pqr^M0KIW~laR>gur!8CDBXj{n4q%37
zboe=(IB1XY-+B=1{a0cw1LZgdtDd2X;{MWZ{?ZC~Aw0%6t_VNJhln7yWz-h}FK$GA
z<Di<bnJpY`eLZM|6*qOub*54vwQh)^2Ed|$!bXZKGH1M)?_U9(r<Cmh>srww4m$iH
z65PJ~9PX#wa(trKor7ld4yXF0fpjs1TP`Oz?04YHOht9fhoPRZXr^G*M$UpVB*>{=
z<gfuWoCS|f?PhI*MK`&Hy{)FZLn2Y%yUJ_LEQFQI_WlNR9(fdAYv6hKxlMD#=91j;
zWsCD%>Y832F-GJ<Ln~u6zjfm+zn&c@W;mDzx@U{P+5QHM(|c}M*Y1DGh_6r>OoSkX
z8{`0fnX8vXlNt%Siq{b!21u9)B7W%kRj(x!6o6OKqOlg#nh21{Yo)4gGY1lbF&E^r
z#nz#U0VPYXn<46VUmWnxVNtfD8r$-rTd|`XP>L&IqN5>UR<#Fcku02$^Pgbpm%^uo
z*FFnhCN3{EZ<+ntaT}_>2t7E)$Ez6`Hk3Fx{$u;g<8#fS%X7VR^ltTQnh$5>uI;0o
z=iA)lCGO`b7jHXe_TX;ofCG%}>-FbS{Pp8jH?I)qnqRD+iv-hnZSvN3XA9wZ2W}(Y
z{jjwDr*+{;9lutj1)t9rUyr}%`<3vv(+?-{;tc)ZCG7XR+d(|rfvX?&-rfOQ?9cE0
ze><hnZr*R(S9|@=zUQ=#+}-DzABUZFkMLqx;k=4i{I{I29v+X(KBf*GB+2q3*Cj3*
zpM&j+mq$<QzGvPTx4nLF)CGC3p6-Kh^BY&$_d5JU`^%3u=|YkpnN^>ir`fPdMmU7t
z27LHuRF~e<p_^BQMdnBR>kl{Ak2!=wPs(5FZXar!Iln+$IyYPvl81ttw&=i5pHq2Y
zwf8o5Vy;J%5O`V!KF?!yQp4Hr8OZo-#Ia+q+q$=SH;Z}iV_n|jNy;a4^u(-4rQyho
zO56c^<Cm8w<nuB}L}>o9M8FpVPgBL27CsJr87Y{^Dp!fcu<cFZAuUlrcYu;ARJ)|%
z&EHg3TF72NT^3#aSx|zzqK0%{>MASG#}BYD7lo4OXEP|gPgP7rb(W!#>aJ_(-=EVS
z`SAsoDKTPj>p}FgWC5@PnwG#<9!Eyjv&8F`s!AjT%qvyeO4gCF>Ucql^C;}5MVpP3
z;~Hj%+qzz>q6z>Gk2up<9LED)d;tQ*!XfqhCwgg;<Kpu>hy_A{Z&+4F&!Hh1<&ZK-
zFGuc}w8Uao)*Y!}YY)W386;6U9UaKMPwf5$E9saq1^2*=>T<=GaA2)bY*~oQtrg-^
zXNiT^(D0B|puoSh)?jJK2_PF*WH8u-EWbHN;FIT_^VS*Qq0$lg3PF2&94JjL8Ba>x
zMe8C0V-(h~-xd>lQDvj!gP@U>5NgM%6$<E8{{|hqD833i2Nmns)~=QaNp^Eesr&{b
zX^?iFCeizwYSH|9QAG}RRyFMn!QH}}j(zp8Z<`>a^!@9cYS%kkn_m>9hlh29rVq<I
z5&Z;{P$Bs*n<fRO5A1vK38S83D1k^ZW}Q#O!lNrd5wf{4!j*gf@6jazpzSMP%u_en
zj$+oL#zP_~W(Jfvg5=;l@#5j^Y&hgS=nX+lZSC2a?_=I1L04sM8ez#Q;p@fxVgGoF
z?cuFLkA7y?lPPzWs_tQNpsc)CvHfRnfueVyfU1ei(>QTZXDsNtM5|q4#Hct@lQ5(i
z$(>h@H((YK8f3_QM8dRQ$oT0Jr!P`zJL>~^9k6LcvqaG5Sx%;sruj#etHunxy1}TX
zs~O#sZM5xvqN-XGqwx4RO8zU3?lSen?D=eXcn#tBN+cI;`LIbNL%yP2zY@im|HYz)
z57e}q$^CAjN)0`oF?E^}t6qPG*0WOkf_$9%rY6-YUjnVF&QvUTRzd$JyrDrz&@(A~
zwm8X6+m(B~QdTOfnf<m!rCdTduKSLn98uPaF5_B6w_OE7`1Sij%#w6Vq9<SJlT^xo
z>5N0KB4!pLmPno&*;%hqnY}x_P&HF{AkqQ?VvIg%*&GAd{Ehv5iYAqQY>LUWNmq?M
zX}SU1-QoVBh$23zUukwo?WzLApt<6piF{j^2DmI`i*${|COwKvs3=)==PFtZA-E&Z
z-R?;gsaj`Z9R<;SK-6wFY5->SZJosvxyncS?@V>E20M&X;(@fnL*v52riyLE$+Cx;
z?HIbomC|CK*qt+9^{H4u*?iK7?_UX+$U<|9u4f>V7%|+i&x}*Yvb1b|;=J3Ni`d_6
zzETBJh@!M?lB8Cd>g2sH`uHPw=S;j$##on~8`yMjSF|W!uBSCT#r-b5A8nXuUu_YV
zqo=DOwx!AijszrS*p74@2x{f6s|6hLB!f!_zoB9OPHmg8pMdAAv0`SewxesHJw#E;
zM=kia4|lWa8+?e>{@B;V-`c1GLun+k)J+<bKdWl;MUafG_eB>fSd}d<NVCtWl7~55
zz92OD^<IgAq&zOB#lGM|cHCi%etnEimioRp;OyCZyWHng_XHcC0R=1ij}ectwsDkZ
zyf0D#r8R+n4Pcoj>Iyait&22jow=H#vy@Nqq_9wHRX%RG48wTE!sgu}Fo70q99cb2
zBg}rTy{DQqa#CH*(Re2p&S?APTYuZx%v1lgXUnM+8PwE1jQl1lQ&d0c<5*rRXGdK)
ztXwc=*@&1@>(ju>=j3jWa0XsUrG$}d7!RPS22_^!H^+vhK`KjZl6YF}%lh13ZC-jN
zGu1uDuKBmfdMxjVkki8xjHi~Pr+Oy5p)U6Uni2Ej#X>NVpJ3%@qzU#@asZ>ga{Ox&
z^S{p1Se*UX@@lK3#YloEh^Vbbf(rzLE`tpP9|^$+$;iM$iFfqhGmkNN>+9O2rLOTn
z0w$qwg6HTRr2`&WNex7yTI8Q+L}$x=`@m|lB?L`LX-&6ZQ)5gvAr@9!lEiOc!^pX*
zn4SwEQgQ|*SVt+k25f&BW1fdU-`d=y<2A$nvI^4$p*2L@iVl`fYTw?r7lICXIPCzk
zG?62khdG*CK<g^!(53Ra0}OW4h$08kkD)(BuXPg`RjPwk4{mA%FL?G^q(N<T^t$u~
z37o=BT<Dj;1_8mXG7PWe|5%j&x<0V&eF<8JuP3>k!#%qBEDpDA_oRM7d_ll1_7B>8
z*iy)~FIy&yVcoy0@4a&8zP1Iq!E5~)l7AV5@WQnIzBXQec<KGI^vgWGC&b<6<&pA+
z)CGf%i;k$5;r_}Ia<^Let{CNmZ1sj>Y=S~;o~~Cw3NukBf{U8%A{0u}4dug3<JwqH
z6F%Qge8xb5!-adAtB2Xc3v3ie_yChEI{BFAvzgradBlIl=xF-E`+hhGigw#;!EYTc
zYYqFxy^bTqadkUGpSASD(ff7g)_O3;+r{TmSmIP_z?HsMc6lz;eq_a}ugzedCdfNk
zEjUg4=ph`%J=D%SWnr!=MvKU-P$_^ISx4o@2(GFB?Dc0)*RF+Yh5bH<Qj9os)zMqL
ze*S?E_?yv~6!Z;}l{YE)G)z4&-%l9<_j;evab3dwnl9?F&RbTBN1HQ_(G$qr>4Xr)
zY+;4fjCMSgx6^`g_p^%8Olj&<wx^~c;?7B7IGL+buDs}Sy^Ev$1Tn-&4hzy$9r?=Y
z7@I5=%AqW4^W`KgM@pBxWJ8a!4G~%EoKQ2a_T!PwRT3m}?!p28t_}C_^~2}#JZE8f
z^v3Nzdyv|<gvI;RcLmnZBPj;z@YH=1_RjIcfnL|00U=eT^{W;SKXk986Z_vnsg2jQ
zLE&TD_M1FA!<nRc13AK2a8}(#rjs3LBj3;Ok{2$YJ5-&|7fbh)sz+K=9?4YPMK+a2
z0mohbI@QnjzSX6s_>a?S^SV%j%shqq5}n*{NM9E@k$95Zw-hUfb$<>y5lmm0;>`dc
ztN^gAet#Y?Yj%oF(Y$`Yf-_Y+Nun+AGee9{XS)kJ*oLSNunjk1hFkM5_ctw<w>m)}
zDM}?@@!Ilyg62y}r=e%$s4Y2>8DgZ11VpPNcrdsdg#y?1nBT$#5hi@3RM)-&fTq7?
zP@>c%U=w=Sgiq*q2=YD69->mY>Mi6(1>zPMUHJrSiNOYa{Q>;;TVJp2qjG^7=i$qB
z+@;h~J|}_kCow@adE7<BqRe`+pO5jI7JbO2u_XPi>I_vTZYGSNPZ(OrwdKZlYot83
zfp8=wGYLG8-K{L)Ug$Y!ZZ|p+AGEr*Dorf@CC(C;r(+eAUb%TuDf#$eGqAI|UpW*<
zCj^^sA%`9!OFjrK^U>$;sbHL&t3P{1Aya)KWGB?p>9S5k<C=Wb40b6jxq~(}75iCK
zDC%Vs^m92W$5SR&E?oLEjcdbiU0T(D*Dl4^;>`4QyYk+KL#C;kTlkc7v&zc@?<b?4
zq`7~HE9;1kdUZT*@-s47DL}<^6^du0m>-r?kDfjo#~WJE_34snBeSjae2&n~JM?oZ
z<oPNqY?#J-#$;U7s_FQq|E52=tI^PtO+QvE@dNqk)ii6-Hs&U3W#y=w-7nu8Ww3^{
zgwRR(5vPmdmyn~MH>y@#t{vK#MsZFpp_eA6XH$CJMMS#3)Bjdo-rR5E0d?sKH0q!t
z3lU1wQ4tLoCO`Rlocz0*LdRt1A4te|c+MLq;;l9mvnkTX`BI$UUj6<D)`=R8S=~7>
z$3%1-Y1)G37Q)ORqQuolIE2)?3fBto2gYKyj-aV{ez;5FX-x)I!tA-YLJl~&dWnm=
zuO?Y5`x~fY>T|w`(rl#>KReyZ5_Ny=(ixZBOmFS;5HQyw!dE*598mysh$^6JXLLsm
z(n^+u7iMUzI?L*+YXWq&fp#w-GT*%&aHWaVx&Vw1?totOk&O8D-cIx97ldVWb>%kB
zepxTGgpU;MLJXQZ3KiAxldbTL^$&=E5|NeL84uaQC0D?-M9Z;`1Yv;b;;H~L5+@^J
z_yMoG6voG03)In!yrEkkm6nTK^?VkC$EQ}8Zi7opaw_@Ho>_nEqrVRc)B^sXsW4_g
z+2fp>?d<~owQ%ds(si2N^M~8x!HtkUe9%xS1D@tKuC<L|q}$4u8>O!w^?e)W`4)ZX
zWR0u3_Xi>}@8lw?55Z}WH~NLGyM!Y7?CjQH+Ad?v)b)5Tf=CX6?vhJFJ;I0W=l5B=
zoOkBCzKu@mf_VAjGl$J+J;FYhCzkzXv(I6vL(&NeF2FYI)se&X1EXlsn51ar{sEX(
zif2yXu7|sQjvm^4mnRAX)$z~9T2~-Q9<MN*YX}QTJb4Mu*33?XD8gZtFD8#T+4`rN
zZIcGN>kfW9UYN1>zw?~IFhU%ZB`|p$$%$iDUIl)hPVS2VUzq@WWc*Rv*{Ar}s>|0$
z?FABV3~?#9Sx}A@T>Kimhz>?1<|W?tvWz9C@jno_BhCcDCY-wjUDuqSP=O$<{UfFA
z(5~lKo~Azibq7|uv`Al{OIg`nlu|ImCS1AGK5EUJgfk_c0JxlQ9<8rpIV3^`iT+dX
zSmwD#Jrr4DHG(p@WAH$FOSJCtJ@wE8lmZgNB++%!yHliuMkNL5IejM&L<otpvpLGY
zVEl#^t358XoZ)5FEbtfz5gKJPmU&zO@S*~Iql8@kbI2I^pLNI-G-=rS^lL$iGpkku
z+ifrm4(l^?C1}%e)tco-F4GS#vEQGE+@H*6U=$0}ea5-A<DiKrvdL-EN6emnyH{f_
zyw4lo&(#h<XRJ5=0{g|KaQ+HOlkw~M2!mh#mKYrev24UGLC#w(e}+bZK<gg281?#Z
zZ7(|!UY^=u+zQuhcb3OVBC~5z66MMVuLpHcf6VCFH*zn9$$wMA(?U`5gGfQM6s$`m
zv>p@QqdxLuI;_}<?-dT=_jd0DKdwggk-|ruF_r)VSC!g#uHIM3?WWC#y-mqe)3wTt
znmskyCYPO)wHu@J@-vcKvZMUU$^9Qx@yyrFsy@BV97dnzI?Gnjw3w%c)G%!zVSo^Y
zh`xEW(<_>usXvarLrZ-DQP!%z70ch2d|7|FXriXD$V4fp>!&iOxdRUF&2%;8!78E)
zkRbD?PP9gWs3v15C2RY0+HtavIxz<(xO1+&LBK~oPpcTJc_%BPlD~|;$Iqv`#-bI`
z8;fbD$fq}k$FX@;TZ%2&*sJVQy9Z2D{w?R<8iDEe_Cv&{z<y-;k>;q|IH~FQfh#Lu
zGX^xc)#G)k>yvvJd`<NaJg5DWEHqs?va!QFyz<FbPMEvupnbB?8(kHo+23->$&O3X
zEa1$KbY{0d?Th6a?atdY*s1!Z0tj!aD^`EE^Bycmdb;SQjbN!CT7kEe+eYhC&U6){
zW=tAf#^}m<vpE0B&!$tKEWZ<_RjX`#QjeK<T&I)!ekkh%$oQpDz;79bZ_ozPGlmqk
zxL@+v<4j(=WoqwVS$jqOc8{kn;CdDbq=!$TA#7@@fg`|_{zL3CpGtCK1P&w%D!4%=
zyhHw$(1~lPoj_OtY&p>60Ttt)E3W1`Rg>%|9ZYIBRw8$C3R#^4UeTQ;eFamu|EItl
zNxC~~Z1$@re~ZJpKVZdq#kZ<0&XxdSX>g@=&|W@R_*@ykm(OFihTso+Y9He;vRG_L
z$cPrVd{2le*huhx15IfN?)pF=5t-_UYjyjTq(#Iv<+=PJrXb|<2&rsDp6$cJf}!w8
zqFd_ln?GPv^%v48BxT>6kB$cZbKwEB+Z4M(6ns$s1Qr&qlZA4Awet8H%mA9ldf*5E
z6PqfUYyx$=3>IMRz)C`<pSURtnOMa=;DjBO+7Y9hyQ;Q26oAZ5FJH5~#FaOGSvf&y
z1Z>NiKAYN7_d_e};i9<um&#~2S^-mghW5)X#<-KoAD9R7{6e?#^P*R7JJ5e;I5ZCd
zEjB)gRFK}{PG2*F8etWkh}d%KEQXn19@2bk)lzMy<u6rU^mv7xr@$BrTfENoc#2%^
z-@lR55<e3uuxLs73{s4n>h^_NM8uGPHLBI57sAo$yK<`{UFr)4tVXSLs|#(r+5a8y
z+b~VacV}ejvS>E*(Kgc0Lc1`gUNBb@WXif+oKVwPE)bc({~JlPU@V|Qz0`Hl(Bopl
z?EWM3&F(Eou+-JpB?=VM4ldaq6jRVOiN!G1-7cu>Pv-L}h_jBE&DlYgbd}}HDf9a-
zvtULp;m&KPlqo<cO|IsA)<}k8Hp#rwZHv|xm7G(sr<iY9(8W`t%@b+li>i#e`c?EP
z%a-|MxwUfU*3DLCld8V=c>b7)!J!7|qNT#d#J~GqDqwUi-~gWZ_Mk`CtNrdy;|L^M
zorP<GFfW}g7s`Gs&5qIsPAj~R2EF*EAL<`V*ZASCH_scy5*>xp+mb)>b|^llzFr*L
zog{{@zD~`B(w<)(h4s98&xB9CKCh<<OCN<WaR;*_y`R@Ey=QOPk8$3s@U<V6-y>wn
z-s>)%2i67KAqIG(($0eye?XZ_27!p{N}SsJ!Jh`efXiP-Nu23Z$#>8askGPjZHhmK
zhv*GmJlST|IA4_;s(C*I_cB42gew2~)7#G2<f1;}rPc`p6+$-FVoc#?uUSw;j25*c
zy0SV!(LoKDgq#{xmE-POq}TaeOcsXq>@b;#WLNtQiRgRzOVGgcA{7XX|Ix4FSe-}_
z9iths1n<0P@A91t0d9C6(U~PUh6sgu6iB8mYO6y5Ej=OQet0;VdW%pSDU0guVSA_u
zzj_<b+~?^q;tP?M&hOuE{<jCN)w1WXyn^qU0P4nUN{)`8mTg>6sU-OBVjx{@2I1Fi
zojA2BuMC{)N>8?GP1EkGt4PzCs+?W&WVL=IKmK)b6w|?-?Pq1crG2)XUXGpVlOcQQ
z{4G8_eBMZ3YW`r_lTkxmX$C=;I+1kVm2^x*O>eJj(xMYUU-I`juafV;O18(|#E!=2
z!mAXOqv9C6iDfa1%q*Lh$_@yusll#L^dniF+)hH1;ZexpMp%48c6#$1tuObFP?~(V
zL1=d&RLCTB!FW|3=u}eDrn=Sj!$@8=o%sklkQ*EdZos?Ri=5ytMNh{fo`272M*oZS
zB+xk>dSMcYEJ}*jI8HWwB@Za9`^`!_LEWnk#2o4H`9jXsG4$2aD()eP84^}muWBjU
zvA{6ylQpf70+6bC+KYSlZP7KInNy7DH3PXetI3DavThFKAxTn`yO;adq#&aQ_*utZ
zHnhMfjHt+OH(cupE0Q{xCYgq2tN<;lZE;mr%V-k}ny1aNIz*x^NQE5Yty7EXi4)Rk
zEW~DQBY+=xn?HnqxVQ_YxIaCVVvw2vMlwL_Z}6pt#Dy<KR^tPlBACyC5v+*pzb%Vn
zb5zPOJn7b5%fW9O2F$y76<NWnIiTbhx_18-!1}tQabqGkRe51u9v|N8TtYnX+EB2#
z`+BhKBk0R$=?pmD2m{~q=hI)(zumhZqEaG)7;h;SO<D{w|IS=r+<a&|N#1I%7tj;}
zIO4(6Vp6FH?C*1d=)gD?#hA1P;6z|jcU>J;9GmpR+=~w{1{HjDV>>qTaKpg~@sA?4
z3U)0rDOex%9vHvBUJ=9M4stH2$-2$IdG)T4afdFX0d_$)Ad}M)BGobE?6-p>g!EF@
zg)!R=Z&b?H+Kt)jcw@R*8)H=M!QiHE>Ri5#<&9mddtFxQVq4R@o}OC;x@)9ERRkZ$
z)LL&!9<^g~$4&rx^@ouvbZVaF$txWnqw4AWjNkfe+(*z}CG1^k(@VooFm;s`4l1bD
zCNKq(tz~2|42|Bt8JNb)OVoaBtt~FYF`&WCPpGh(V1B@v0_X}_PN9Jt<+h*c%~quK
zKm%aVy=Oq>4L{F^K2*aC`Atg*<-NwSg2`uEI`}uN9AJNbVTRVS7mtA`+k6MJAi+4T
zBS-u#vkhOnhyM9X7m6#kx*R+n7`mwi*`cs9(W|r8=-y3bZ-h!#cD(RA&8iNa>m=lB
zLG4s6m)=`*x43p9CeT|r9F`R67)ozq9g&773Oo)Fq323OiwlszO7jta5_cY&e@61<
zaUebuA*e5%BzUJwb>)E+^>VqVcD%j#BAzqjz<UFJ@!=1RsebV}-5>N#<=PB6STshr
zb!%RtAygbC!G@j}-&-1NHtm~vmQN8VFiV*mO=;-@B7@wW7yZSARImkq=9sFdj1+T`
zJ{TYJN?>_}3Op4-60VSRCy?Dl6F|X>80zUc8lX|{6T6vcp1^PopOfQ^@!qy0hOqH1
z@I=61^NhdybQghumWZ=Qb?aLvFF#1Pt4t+-%@xYnmwVanBXyQ*T8TsfwvB^;?RgHd
z?;M72rwp@kKzi-g(&B_g$k`((T(T6@QxjfOPf;-csnJBM>L09ZZf0dGS*@0Ap!r7G
z@&cVI;^6EH4Zv%PP^oJMr=j&!pE=fOE31Ggo5NN&I-pDiR)?M$+ea+rOH2b@)ABCr
zOM!6WJjdX|39Y$ix4+p^nB?nvR)iBm<XiA{_r)lV(Oy@38C)U1>4mt7j89gBCty$I
zaR+rV4$NMV?b6helJy`Ckb<5Oae-hd<%iJGVk9$ycjyq)58!5YcdM3^!FNLfronwc
zr=F;4iMa=-2)ffV0*{r7)e>(lp3DvNkOY<)Y@zk3ZURJeHp4X^mP5#U)W|Bdzo+Ve
zY*+&OL>b_kkd<OnLc--mO1L>+uEOM}cfRnc!TW?-x%hHs5Fm!<u4l*oT{u3Z%`#@O
z1hxv?ZJvmkr6D+UC(DWI6|lx;e~#DVFq+*~s)2M4@@GTW7o?$<?>N*PM1yz?=q1L(
zQ6#Dut=Ygg>N-nLxG=3-@V~g>9`&hO92;6HcMDjOUyv&k+m1m9j=@6?$++tw&f}vX
zUt=>uFEK$$2}%Nj2QSa>T22rTwySGbvQRBP&f%dx+G?V$fUPDJcwgVuuOqc#<hGn}
zih-NKP4BRs#MEJH{5%JlF%3jBP{Wv_)fHqV>|lX{xqVP8eCZ4qLJD^g6uDgL)>wb$
zKD&4)<wFLnNx}}2=KEC&<G7N<wY-#HjK)svPCMA!g+2W4R8m~O0^!c9Z-nQkgBOVS
zSn(OtjrV$$Rsk9Eh9_!+XaHB;Wx(vw2d127)XppRkZ94^5`|_4;(Rb`X^4u<Gpuux
zmso4AmG{MCqNYsNdoZ|xU*go9S4~uE0x?jJoDg#RQye{0ty>7&Et3&VDk4AMPt#le
zZz^WhW`HZzOWTFNDJ#Z-#dEshb!Unve6)juPJtJago|guRq2RvXWe+#8}$YbtE>BB
zt+ya49E2Bu&`o--qIE0sJ<W!=80$Nx<&JR@Q5KbMXD8T|q2<mJm~E}p(vjQ@++ke6
zO<j5m&Qtt;EMigyk0)DGx|=w_xjeV2u;baA&<I}SR8q+S=KuA}y58w77Y*}m;CVZc
zN|(Ev!Qp$^sKv&N(?Hd^VnMYfD?fdT{&@J&k28~gM<x}0a@<N)=kqkNv4Dg7%lWx*
z9Q|SZb6;OB<-yOv^?hx4*o2M$tDt_e(I{u_NaSR=tDzCyU`gfcR{y}mv>_Fpt8g<-
zXi#d6hK~MAJrY{l5-@*&4Ulb)WlR8eMrrOy^Ur=!Sl!(0s;~HTraU2EVQ5wuf}%Lg
zBC6}wL>(tueZX_+jSa@-L4fCxuA_-e21;x(as$&yn8uRs*iWbQmnvK5(U(57j&`q!
z@&C3uLOp(d|ILetw!feJ!mOn@NxIIDK~p`UX=|Q7kJ)4b)4Q}pQ(zr^|9%wcJt;a*
zjY~6~+^XeSX*sPDUeHR0gY%Ys^TZ|+sv)CU)(I3rFI9QmG+BOPLaSF;Ut$%RXrm;<
zmpOlCqEu~Gxr~5zu{JE@$CO(mZ|AI1nrq_91`tF~d<Sr9vgDe%Hqg!aYH6AR6ql;I
z6%he#b!BO9bS-0Q*JYO-39$aetxj$}DQflj76O8ov4GU2Vp!~aOI+^JI#Ct{>}Z)_
zwNgH4abYATL?Nx|@uj>ctg8O9W6tV6DV@OS3HO!;hcy$RNtxQIsZbEL#kKQCPuCRP
zv_%_UZvAR={pm`Z=aBTACeMJ_&$ne=N76vJ#~4BO7dk57RTd&3rr5sq7<C>z*oFdb
zs{F$6Jbzq+OXe`?h?GPYGTaSCXUPcGDMBu$Az8ilD$C%ZM9^#Pyp}v%?FK=mJ+=B4
zQmo1YMk?b3$SZCr!wY{F(c9bz(SPg4C1BF*M5N<GsLLAtP5iQuo1SZ%UquZhOJ~yB
zv%_5AK0}<hAk!{H_tmF~tyRE(N?-&uH%U&s3mue^b}B`LI}28Naa8!hO5?bPt`$~T
zfJh0~4A0~BAl+%#-4H)$R|4B$zP|+f;)sFm5~A>`1?ZfV?A$CU(5-0d7{O{0&1(CQ
zYS^DsGbUgQ632pq_ZV)qQ-EBsh9NS)e!%*tQ%J^>F^)G+q1?-ynoJ9^3yaBqpu?yM
zi8mWFxlP*A#SKY|GIbwpYT^xF^rFDnmCszKiM^TxLo&U7n-!l$htpVz7Eo0RUW>qa
zzJe6x2*mZ@pqwhf<)nwR#1sg{(G_imASFf%AzRB_+C<DI$Y2kWin>!L{AIHjT9=Ye
z*}pf*(_%_ashg^fekAyN-hAYL%<=p+ut-<>)h0Qm?C&d%AZdF2i%W*uHn;xQ+wao;
z7B-)PZ{ysD_#C#`Za7MV1pD9=JrBqW^#>5{Z~D9TAID9=pp%|FeP=3^*Jyrg=8lv*
zY=ke53<0N1;URvBnrpkYzMcj8QL;tqrghszg78axxRWE})xRH)YhcNG!kH`l3SVuL
zPbMF>`X{k<JKSQEPI}d$OHlEAj9SKsmZW>u+1Y*u8<K5)5||VSU)+w1bG<kVOB3G#
zdoVVe-i`;k#rT>+_#-^e+5Q=2vuS!d+kA6|i}LhmPp{goe{x+DdBRpF&@ozl{OUdq
z^xZBBea=2|du-sL{@yY^a*Z*Lkwu>k#yzHm9W0tPMtvEs82>Cnl0N*B!tZ4JzIEta
z_fGp8@qY5jdCd$E`?PaIF1377*0C*U^c&fl|4}==3a8GKIivVmqzb&yw<O)k;VE4g
z-shU%V>XE|st&mgcd!r__f}?d&n`MqlGZ+w6XY%BW{ZO6jW-o|pd)iBJc;>Yjqe5w
ziN<#atm;g`;9l4|@Q>=#9S&#&moU2g8cAwU^0m%<g{i*h%o?mUKdR=V=6S1NK<k4)
zOiq=e1kA2w!Mo0*K2)={6==(HtOPs4P{Uvt1LHZJ^2WRN64)c0)6KhxVSnYk-^$4U
z?wljPMwH{CqH)oIG-k;NcPXxD0|{5HrBrtlDj&{K8Xw*Dv=VKCT=81vMh<V#Wuy69
z)o%{#mk>}I@!<e+Egm1nU|2w7%)q^Uk|lm<=`AIk=9q@}?&E0v(ExMIkLqXRKr$oG
zIcL>qyuCAyvGSeow?46P2q8*hH5?``2NEE}!sAk3d1x>S2J<_tGCKHVN(3hC87{M=
zzDS`9G$@Dq0=q&!nhrp1<J6B!Ru9}^1@FfKHH*u*vkJnMZL)>Fo4Q*F0#d;W&scyV
z!q#D&Pe#*`w|QDP9hNU9msErfo%t(PaX5H3l_)VfZQzv<vT>g$G>M?ia&nl3Ac#iP
zRb)L>q{;S5cSSfVb}nbSTtvD#j1C#Obp{Kl!hTr<VkdNT{Z`lff`OmjYa~RNs*4hG
z<*)WWmMQq*^v>CRQDH^`x>=g^IWxZp3j#u=D#yZ@%!_M0ZFD+=B`leQh))b_kN)HQ
zlEF1brI+-?rQPQ-+Kr7jS-7d}nyzuTf}C@s1>GD~dW6tXt=g4<r|)#s#Y{l!ini;@
zGrENub752z>S~2nnZIHaDs%-HdB<=_El~B{N|u$NYJx8G@QbLGy^3(&%*O3Gd8tc(
z#QPePQg%A6&DJ_G3h7PL;i$LUefjm-*qdAf(tgoRnv|v&ycg2TlJ;DLr6(x}me8Hg
z-zlwNpt3<8CGEne*rw8S{4Og%7!k3EmSQzMb|%!A^IDk*Oy8d)MGodQCHq239&Z&Q
z+BsPP=GJp;i*zowAut>u4Z#qk<V<zX2+bN=k9v_I65#8#G3c}1qFnFTFTpfO9h?K*
zd0)9$e+&eX2(lT*XUif>Z!XMDRwKM8sYi39Qdbtl>OX^r6h*yB^|I;kopULc=%HuE
zhucp$V=-Qymm!I}h$DPg&l~D%-Zr^6czn^F)B3(x{CK03K502Zo;x?fJieXyy|DTw
zR=qM|gBy3Fsb6iM34>%d@IQHM2c--U+g;x$ekrM`2=tlH2gfcCv-lvTxkS8GPd%xK
zoDMS=vcbMhd$mk!4#CqI{H0A2mdtQG5ax?XY#R(v-N+||M-1Z#yw}`#Ugt_)_jvS~
zERy52CYTA5Eb0@}YM7Ne&|hLO+KGF#seDbULMR3>QwC6q10sN#xJSq_7(5yz59ub;
z83|foiub9=@pbN$klmcH8uNZFjsRnv5S2diX}(~{a@Br2NXHd2$ORZ_&qPCKG-s4d
zH3vf~GGRzJ3mhnCl1S0v$K9hpph^WLmmy^iU~E=}3N&FUqzUYnO)rb=<iwpmo9*-Q
z-jbP$IR?(eZdJu=I5&I(9o(Pn+dkVdIN$t#z7+LJe6dD#dp+b9Q%CvYti#2Rarbl9
z`D2vd7kcgS#(m4IWo203YCL5?HHZH2X|)iqw(0OIC(TLEPmVlca4<D!DiPdQzDsP@
z#${<AexJ-=2XZ$=Qy!)IS~VW{y$CL~ZO1Go@&CJagJ67JlIq9N^-Ktb<M!$BG>E||
zJbjOo`?qsQS&kP5pzL*jqT*>#jxLlwI@LWUa3MaciNDCA3CrD=8fR$i_6BOpr;M}3
zDC-g5=9VavHE4k2LN8Hh<7zndb!_j>YxGU|QUm{GX2&m|O~W5&{IXa|AGau9EAOM%
zd-!yly7g^tJ1D+=H<|_NU90qMIB1#0hcoDmQ{sT$@VVo4zVV10+)20nmGzeP!I~%|
z*@orug~Wfu6nhY+yU&uB+?jOgcGHe#XLUyS$ccF5Mu6~x0q|KEBSm});eGk@T#kjS
zWe+FXl+@=}+=JjwH&ssJ;7kr<T*LVqZ9p5b(qwfbtE8&C=QWGN(^HhgoIv8pHUD78
z$ag%m+8<fBA)fl8%)i~<bs6V}F?w-h(^~>f*|TT8dq<f$^O}Q6Cb^VADxI8&sXDS-
zYnv^GQkN%@$6H-?n;zvJLyZ+bXHhf5Ayk_uA!>d?8|i30s<k!@4Su8RagSjo%?P@I
z%H`N-^0i4uo#1nPioQ$H(M>=vjgM1Q(G0IUgncougfhRIOsW%q&wjFdFu_0wq&-~U
zkbG~@MQKBM=Zx`L*fG6p8U^Dkj|Oz7!;TG5ceZT;j={69@dM3EtuZy2NAyw2!?uB2
zhjET}`k7f021ic(Ws^pg!+0MWC{92V5rHlJ9Ful#mjSl>m5}vBVshi;av!HQ%N}Pe
z<J)VDxh3|&{f<^E9H@I9gu58<dhqJ%md(ezyePP!A<xmhkUGfmMe@bc<TvvnB2Hf`
zBabk8O~3f~_`Y{CI7{fT@$>S-LwfdtzaY=k{rxbB&Sk?_>y|c}pNot`zW(&{jgbL&
z!XN#m`?G>D>X5{L#doP6Tg&qOXquXP|C)44j`#D<EIi%*^Y#&eQXFG~v(dFSB>zT0
zzlm>Z^)cTgXL-QEEH}XR^0<+cQk{r#*B6)B`rl*BL;}7G?+9wdvnW=&1Mc8na&qHS
ze(8iy@ipB7<ACK};()F?m$aKNGz;_dk9$~K>GMvU@_U2Fo&(Y6X-bAr1yoo?CInFu
zs1HdC@x~?}Z>b?{;;CMa5I(5%z&5?5>>+-t__43k;U>`wf1E4&<{wLvd|iI7`Rko4
zi5MP9{)yv*ExiW(QUnhfxiLJ?RaSY<27fcp*vvU*c_LmImvUarg<{fY@U~%Kk=eG}
zmp{qG+Ga^r*ul^vIg-j-wJEq@)n1WrSP3Vfe@IM8kcK10vGtvT(Kq=o<AhKw>kKcm
zCk!FaNFBdP_>ES2F9#rvuSX3ZGMq8__#Qv&Q9M0wD<2VRhthH^PZNU~*Xq&oS$Fd^
zvwZY%hv|TO%%emO&zPV6w0QXayP#i|Tb+09EFSZX`Z&?B6d9J=nvRjg1C565<it!a
z!|H`5yk>I$FeA!_vcHXtGhjj*mrUED4BS10S|DavovaHt<s!UR-HF<KI%wTd2O`P`
zR>tAPX@bVAK{YJZL<-n#K@R*%n30D6w8@PRW{W_5#Ro(MKuF0gvW45#x;t7!YnlY7
zkP`}JS|PtLA9RrCZant=eoolG_$$Z%d0>q}8uKL|P8cP<nA!|iIOk6}w^tzVAc@s_
zk;JyWmF<m(d*Wi7I6L}y=dV0<x{_v5`(ud{n0LA6$Mu;RQQG+{?b$MM%bC-3-P}RY
zB+(;@7*DIRl`t4kvKQvFDt!4a4pcH2>TQC+q8GzfIYIH>7++Zq{rkfwE&p$yOW&y!
zn+tkEiO5G2to+8)MJx$})GUKVj?HH2{F8%LSmCV*uw~J-*3}QoMC!qvV0^35{o-|i
zj-35%k`n)ozN`F2dDPp#`-xx2KRjz(=0s?zvY4tDMMeFy)bH8z{NGw?!L5;Vl>{?r
zZ1go)T@<pW(91gI8_LDU&~{N959gOnU+3PgTBmFK6H~sW+v-u&+SzAsN3tF+o?eDm
zMlMzrZ5mr;Oa*~3UCpf>HfE&{jSU&OXzIt8kNP<?w)LFd+0<FHRlaV7R=#|hJ38Oz
zm-^`s%w8_O$_gHTr{w5W>%*h=M>4=y*L>{khR@e6cGjGpo2*kTX6uy&2!31VE!*Fq
z?(^#Ac;CaHYvCknU<3<Gh!vXds1nDptTe?XHJD1DGMEVcWdth3r@7*WOHmgoCkBTK
zPrjkB3O9*J_zOH%zE<H`<<fBv;~92>@h9>cZd)KE72-H=y?}$=><Qqcxoc$xnAb3p
zW^<IWJsYYl&N&!x*2y%*zlcl;eiu}xjLwln5E<6%xw#2|I*uTuHK<&k14uqM8ESQA
z@cgp>k(f+$Q&r5;9~)L(uHv$rQFUSNTvBD6lJ^VV&Pj<nzLWJXQbRqog)~FusKnhd
zGNxviuG89FuVH$wD6i1n>ej|&z=AQwyIM&ukFF?CZ#^NUYk{%ZuXcbGEl%ogi@Bbk
zi3BgJ0I8fYd++iFGoA*3OE(XFP$ewv#?UUKuFYiB@=~fIY8=T*Lr1Ga-@-;<s~pyJ
z2|l<OYrnJ(7gXFH>(JW-Sxl@6ZJjIkwv|sL;@>S4Nlq+v&<GERgf!=C#h8pT-O(0-
zna%dXt=k3S67#juIuRJ<f3g%m8TJ+jkgV2`UsDS0;|p`;Ri#Tjoao(!KZdb*{+Z<&
z!o@Gb`=AWr!*>c4_)!RJ)4XZ-t0S}x9&O7ZD3XQu=-_Lkmx1N`NYT*7PLLNtK)Mmp
zNo5KKW8yXN-LYeMGCH>;=_e3S!j2Nc(MlQHE<(B~olK<F1!>BU#)TGPgrx@GQxmOV
z`c=+yrrMl*(M)Fe!LZ8KtC&Ir(I+wDskG&#a$Gti1R6b3sAwNGEpVM#Qc=e;ZM>)g
z?rTcTQkgzK+n-;&%o_M$ueZ^!w=KX-9oe8Hp~}jhv9u|)q%o~RJ+ILjzYyO6hHEc3
zqlvYiyzD1Jb@*U}VFO;sz*WLA$er)ee=IAaykAigF;u?^0rz}Uz>6v3*8M{tk;i=r
zpOAxN1j|QS7t!a|S75o>(j63V^-}ok1{5f4{{!Ck%cubGN^xz9=mCoYh5xaM$27N>
zZk>M@E7BA#xycfpiDbNb!cc@8IuioA0yrJrwl#5|BMjRj@#!n=IXOG{{$OlQh}9f;
z>f0Bq;$Cl|%5R<$8T(y$f#A6lg(~Uu2|6hyoe<MpUK)KG?&KaGAh~}`Ckg9Do~wr|
z|Mkkdao2ay2p?GI8Sa+w#Lo|hMF^+;?%}>(e0%>J{?CtNp3EDo@2avy`}Bsf41EOu
zoc-?BvE_umUR>rus^#b8pLgHH!5k;79|l~*VEGN&v#{CDq>a-52S`A-zin8*K479<
z|Mz!6cKI3m(0|&0IQ-@3ar0#ycMm^J`%SRZ>wix_EQcfI-Pk|Naa$ildncSioIX9h
z$ngdHW0LVOeaK^94<K%Opp<$Ngy2SUk~XcF_vvZM+w^eqdHUCzFNdi`9)IfhAt7hl
zXG|NRm;OV0?)oY9pXDgSm*aQgX`~>!gOQLUV(?Rn?>%VnFdt(>Yu?(<MjsP<3g@OH
zDWXSJky8H~`p(3g>N*Mx1$k@9c{sPFE#VCAeXT$_4-WkeqF8f>t<W`vF`Dm7!^#p~
z`(-5R$hn?SqWDPE3&~y{(=ks&+img9I8DFhc8okBg<_KFwSTH<96n6b%kwdu&|t)h
zI4=3Q8v%Pn0~0YtH_F}_?DzZrI4)a(r{(H+%zY{%@i6kCU9r@6nL62#;+Fb|;~~p&
zvvseW#+NT6kbdnoWFC8b#@)D+Bf5t%g+9EDy*vIv4)`H_PyOAK86a(#r@v$x_FrE5
zuKyu_?_W6d$IKg{&&1i+<VHyc#?Y5I2EsVm<LEw3-jDl!<1=9{L^9a5EH1<&!6<$2
z)(mObr=(WNo&1Qul!$x|4lR|V$$F(QUulE|Xoj!QA?iF@-6pOVjuqs>Tky7AA`t2n
zp#d^x^yCU80?#Zi=U4>`Kyq-1kEC?@zS`5s)E!{>KwC}B@+l?K3{IG9i{F1_6l6l`
zzyMx{JC<RoNyxTN5zNUK9sn!REo5|H%{wH+Qb>T`Gm-UH$j&m7RVY|N1<LwDLk`Yb
z&sOX`l_y@ksjYv1sR|zrVguS-&2Xjw3$XxSEzAnP=?Or=+TOIeRQ4hoXBT$WG)5`@
zyYrj*(a-eh)$Qp8FMfAwZ<^{yQ=N8;+3DH2uWn9%|JBXc7u9KVqkm&=RJk&$`KG!l
z8e2BzM*UBCG5;58eltINb8~iEnDf)wxjA1beKj*~t`@$zD$Y;M>8YESH|N$CUisy1
zQEnG@=AUnAIIjR!h#YRg_<H7H#%I^wS9bC1Hzq4|jw)9)dgbqLEu7r=|Mk*V_&3a+
za4Wznp4+Ceu$nEA;5Ps`*AhW1lkGy~%FzXM-&ckn4We-*a_csB!T-W|10(tdz*yvB
zmF&gLXq-37FDkI+bnmIcN;&5QFJ`wcdh~c%TTpP?8B~U6<@;Fq>fZKXGT;cLYFPD)
zx?0d&>FVqEg_uf_Em6Us>^t^AJVUac@iRA<g!Vk5?k%9ch>Zf16@Uf3ir!gV%!58z
zp;ghqC0W};E}Kx88~gUQXw4N;(pQ!u7GOsz8jV-tw59Vowo&OW8vWj&{bhAdMMYmV
zvzY~K?y+tb6)tA?Kc1ieLgU5!?0#<Mb39eE?d@vj%2Qjv{}$ABWlCRNsvEg%PK)N<
z#q8p%MP*HOz9?*E3`BKbpX<0vU(@L+7K>%2tV){D7Z>(7Wjt>eqRpP>UX0hzEBc+r
z=qp57E9dRSa5GyNF1=t2#;iy=V;D~M*}8Tb+gQVRe0hNlCzOq6{wMr7_aUZsmq#9c
z2rj<-Te|ybQQQvFDAPe)|C)HKf3%b1L<8>s@RB6|*S>%K{JLxZ)P_yk^-s0@wckJe
z^u-Os?zpGnzrTKb`_t1VJ$(M@?>~?0-TL9nQ9gV;T#i2+p1%D3#3C;rpN@yWZ1UKr
zUH>xH;~l-Q>j7eV9{U{IaM;I><(E~T()2{1KhoI$oR3ttk6P*v;lp+KtLVLWhM)a3
zjD0VMVNAP35>AlFQX$&8*y%J{D*d8qZ;jY1vZ!uMO<s8&q*%T${{^`@*m*SAoY7<B
zsz$?Ner1iHoAbx*l}6<&?XZk&ED_9$km_n?Yp*3<k&RfCR11Ru8KOwfSuabnG-vCz
z0%Iy?&=?t&A%msT3yp+@w#LrT-d-%O$(RU5Y0isIZLSp7A7s{nZ9I|wO=-`o3j<v^
z18WOX^U_w<BRY-dQMVKy`TB$&=Xg!xu9<Qi6Ed7z<o86n%E+?lY4i!G#+EsD^mr#!
z7QUkuA>ovWh*y~0Vy6%m?BvhGv`#ELiqoN8ArPk^n8WqKwB0cQ?)cO6TY2d6G>&N`
zmC`Om^^l_EB<>J%pfo0?&|(;;Z#C~C^v*M^c%?n{Nhb@A(35a8Cs~soJMvGx_qd*r
z3hFScNCaqd>4gGksu_{qSN$V>cXE}_XE%lVy7<NIs!@2W&TnqMD$Z_xd4IaPnJ>;R
zerawOu~9gmyYjxMP75{fs>1wQuYT>zrU9^~@W#kj=o{y{Mlm57ik(~5+Hkzan34xT
zgOIs<gz!%x<ep+s>AS4;7z4VB2}@?*D2{`TS%dyOs!KU4O+x427UybiNd4+wftknp
z;@m7MEY8ox{<<?~KjLg=jMBwLSv`Ke=+x%sLjC%0A1|(MZeVd?mAbf^{aV4STGaEC
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
diff --git a/ui/images/theme-default/bg-navbar.png b/ui/images/theme-default/bg-navbar.png
deleted file mode 100644
index d26a5045104bf1485bf240576f2a292e5c97785e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3043
zcmV<93mo)`P)<h;3K|Lk000e1NJLTq0012T001%w1^@s6plQ(T00009a7bBm000XU
z000XU0RWnu7ytkYPiaF#P*7-ZbZ>KLZ*U+<Lqi~Na&Km7Y-Iodc-oy)XH-+^7Crag
z^g>IBfRsybQWXdwQbLP>6p<z>Aqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uh<iVD~V
z<RPMtgQJLw%KPDaqifc@_vX$1wbwr9tn;0-&j-K=43<bUQ8j=JsX`tR;Dg7+#^K~H
zK!FM*Z~zbpvt%K2{UZSY_<lS*D<Z%Lz5oGu(+dayz)hRLFdT>f59&ghTmgWD0l;*T
zI7<kC6aYYajzXpYKt=(8otP$50H6c_V9R4-;{Z@C0AMG7=F<Rxo%or10RUT+Ar%3j
zkpLhQWr#!oXgdI`&sK^>09Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p
z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-<?i
z0%4j!F2Z@488U%158(66005wo6%pWr^Zj_v4zAA5HjcIqUoGmt2LB>rV&neh&#Q1i
z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_<lS*MWK+n+1cgf
z<k(8YLR(?VSAG6x!e78w{cQPuJpA|d;J)G{fihizM+Erb!p!tcr5w+a34~(Y=8s4G
zw+sLL9n&JjNn*KJDiq^U5^;`1nvC-@r6P$!k}1U{(*I=Q-z@tBKHoI}uxdU5dyy@u
zU1J0GOD7Ombim^G008p4Z^6_k2m^p<gW=D2|L;HjN1!DDfM!XOaR2~bL?kX$%CkSm
z2mk;?pn)o|K^yeJ7%adB9Ki+L!3+FgHiSYX#KJ-lLJDMn9CBbOtb#%)hRv`YDqt_v
zKpix|QD}yfa1JiQRk#j4a1Z)n2%f<xynzV>LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW
zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_Ifq<Ex{*7`05XF7hP+2Hl!3BQJ=6@fL%FCo
z8iYoo3(#bAF`ADSpqtQgv>H8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X
zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ<AYmRsNLWl*PS{AOARHt#5!wki2?K;t
z!Y3k=s7tgax)J%r7-BLphge7~Bi0g+6E6^Zh(p9TBoc{3GAFr^0!gu?RMHaCM$&Fl
zBk3%un>0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4
z<uv66WtcKSRim0x-Ke2d5jBrmLam{;Qm;{ms1r1GnmNsb7D-E`t)i9F8fX`2_i3-_
zbh;7Ul^#x)&{xvS=|||7=mYe33=M`AgU5(xC>fg=2N-7=cNnjjOr{yriy6mMFgG#l
znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U
zt5vF<Q0r40Q)j6=sE4X&sBct1q<&fbi3VB2Ov6t@q*0);U*o*SAPZv|vv@2aYYnT0
zb%8a+Cb7-ge0D0knEf5Qi#@8Tp*ce{N;6lpQuCB%KL_KOarm5cP6_8Ir<e17iry6O
zDdH&`rZh~sF=bq9s+O0QSgS~@QL9Jmy*94xr=6y~MY~!1fet~(N+(<=M`w@D1)b+p
z*;C!83a1uLJv#NSE~;y#8=<>IcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya?
z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y
zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB
zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt
z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a<fJbF^|4I#xQ~n$Dc=
zKYhjYmgz5NSkDm8*fZm{6U!;YX`NG>(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C
z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB
zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe
zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0
z?2xS?_ve_-k<Mujg;0Lz*3buG=3$G&ehepthlN*$KaOySSQ^nWmo<0M+(UEUMEXRQ
zMBbZcF;6+KElM>iKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$
z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4
z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu
zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu
z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E
ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw
zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX
z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i&
z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01
z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R
z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw
zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD
zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3|
zawq-H%e&ckC+@AhPrP6BK<z=<L*0kfKU@CX*zeqbYQT4(^U>T#_XdT7&;F71j}Joy
zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z
zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot<a{81DF0~rvGr5Xr~8u`lav1h
z1DNytV>2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F}
z0003FNkl<Zc-rh(%M!vM5KFNC|I6`&gUCM6Db*2942A9{WCJZF9a!87E@(UGsqFxu
zP45o6)w}84L6@r+je^<rF;F*+`54Tr>!13R#J#6G=udht8#ymUS{4FS4az|wdj>(r
zP~aHJgV~C-r&s~)G8l@ICIDIVGRT|;!pwMiXHS_-&Ky4g+y->2LuFAe=K!F!O%<tv
z3>65jIC^+VysEc2;%Ud(iAV#nlt6&2^nSfL@Kfu&4C$poGcLsBGk0!}bf#XTxz-wZ
zHaL1oBq#=kf!UIY&{?O7W`#71FjH3i&yxACpxKWjBuXU=nyEPkCZM3i)T;Na|GlOa
ldV6Z!>LLogTLS>_J^;HAY48e%5Sjo0002ovPDHLkV1jCHrgZ=S
--
1.9.3
1
0
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
ui/images/progressing.gif | Bin 1152 -> 0 bytes
ui/images/theme-default/bg-black-linen.png | Bin 84780 -> 0 bytes
ui/images/theme-default/bg-navbar.png | Bin 3043 -> 0 bytes
3 files changed, 0 insertions(+), 0 deletions(-)
delete mode 100644 ui/images/progressing.gif
delete mode 100644 ui/images/theme-default/bg-black-linen.png
delete mode 100644 ui/images/theme-default/bg-navbar.png
diff --git a/ui/images/progressing.gif b/ui/images/progressing.gif
deleted file mode 100644
index 6552d41d9d4c874091bb51931c6adf64a95e8bd0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 1152
zcmZ?wbhEHb6k!ly*v!E2|NsBHckiA#b7s?~O&uK_IXO9@p`oUxrgCy}4A_9;e{Mh5
zkYH!W09PYD17=2`8pWS1oFWVy3_2k7AY&O=+5}E^`WY^hIhD@I;2`qkhUamCHs#2?
zD{Sl5ddqAu;o;E8n07H&VTOd$9w+z3Od8dz)1OOlg@hfQ?6P6gg)Nhut~h8s_iHy|
zTx?f<DfNuF4qs+in1T70o7P)P87f1<g5(0~t-`bz%`;<c7;R*Hby&0+Cd*D$nyx)7
zxH>hoEs9-2GEYbl<TO?^r*$AZO&~F#<dR{_!xK!0-EtLPZ*p|yWoF`%>SmZA?aQ<9
zUT3kA$h=8X2{%vLaEeZ2FfFvxHCy+@)2xTXXx&nW19L8~I4mhFCLSR*N5YUN_g8RC
zxvG$d3rC2Yoni>Hr8Z+mk+UtMt&Fyriq3S|nYyz*lh|s_)v7fmBnyPWjzbSOeSwpn
ztM^*GK3gr@WX{W=;H4@s$Eix`o#&Z+UC|vq;?Mm$*qz>q{MpOLKD$V>lW^GM@gRp<
zTfqAxmTMW#NQtz1Ow(GUENaLTnG@~OSSl%)a-~@^*=_L^Wu6LazRZG$dI^iu%U-z%
zg<00g*_8=1ySk){v9Pk(*~<9qvQD2qQDdsojG1{NY_a__TQemj7eK-u-Fqm>f>utm
zWMS=L&jJU059@Suul#8<RCK0juuU^pO9X~|9`=wYHdzoCYCehQmh?GGSt;IO&EY61
z^|cU^WKC}pQa3GC)^bS)J5HFrADAqr$@=Tg_KIeUMR6Q@nr+9QW_y9zNsLj!T)@YO
zmFW>EN`0IXPrZ>S5M3ptD%hrTMQhc8T>+_g4tF#}E)IMpc<k8J3C>bEce!tTI4V-^
zEEL|{;B8wW%<SUK!k*>CY&#hksVX{CWv466RGb|aXRcN%49rjY*aHrnWY;d4a(eT<
sn9_<U58lHODbH9KGQC;#6@s@G?AG7t-SQycLr1Fr#FC=U9tH+$0KoWQ9smFU
diff --git a/ui/images/theme-default/bg-black-linen.png b/ui/images/theme-default/bg-black-linen.png
deleted file mode 100644
index 3c069709ac86b6a812590c93c300829bbaa005f0..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 84780
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%0A1|(MZeVd?mAbf^{aV4STGaEC
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
diff --git a/ui/images/theme-default/bg-navbar.png b/ui/images/theme-default/bg-navbar.png
deleted file mode 100644
index d26a5045104bf1485bf240576f2a292e5c97785e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 3043
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
--
1.9.3
1
0
Kimchi is using the jquery files under ui/libs
So ui/js/jquery-ui.js and ui/js/jquery.min.js can be removed
Signed-off-by: Aline Manera <alinefm(a)linux.vnet.ibm.com>
---
contrib/kimchi.spec.fedora.in | 2 -
contrib/kimchi.spec.suse.in | 2 -
ui/js/jquery-ui.js | 14936 ----------------------------------------
ui/js/jquery.min.js | 5 -
4 files changed, 14945 deletions(-)
delete mode 100644 ui/js/jquery-ui.js
delete mode 100644 ui/js/jquery.min.js
diff --git a/contrib/kimchi.spec.fedora.in b/contrib/kimchi.spec.fedora.in
index ca3f7e1..ee5d08b 100644
--- a/contrib/kimchi.spec.fedora.in
+++ b/contrib/kimchi.spec.fedora.in
@@ -172,8 +172,6 @@ rm -rf $RPM_BUILD_ROOT
%{_datadir}/kimchi/ui/images/theme-default/*.png
%{_datadir}/kimchi/ui/images/theme-default/*.gif
%{_datadir}/kimchi/ui/js/kimchi.min.js
-%{_datadir}/kimchi/ui/js/jquery-ui.js
-%{_datadir}/kimchi/ui/js/jquery.min.js
%{_datadir}/kimchi/ui/js/modernizr.custom.2.6.2.min.js
%{_datadir}/kimchi/ui/js/novnc/*.js
%{_datadir}/kimchi/ui/js/spice/*.js
diff --git a/contrib/kimchi.spec.suse.in b/contrib/kimchi.spec.suse.in
index 73304c2..00b5028 100644
--- a/contrib/kimchi.spec.suse.in
+++ b/contrib/kimchi.spec.suse.in
@@ -93,8 +93,6 @@ rm -rf $RPM_BUILD_ROOT
%{_datadir}/kimchi/ui/images/theme-default/*.png
%{_datadir}/kimchi/ui/images/theme-default/*.gif
%{_datadir}/kimchi/ui/js/kimchi.min.js
-%{_datadir}/kimchi/ui/js/jquery-ui.js
-%{_datadir}/kimchi/ui/js/jquery.min.js
%{_datadir}/kimchi/ui/js/modernizr.custom.2.6.2.min.js
%{_datadir}/kimchi/ui/js/novnc/*.js
%{_datadir}/kimchi/ui/js/spice/*.js
diff --git a/ui/js/jquery-ui.js b/ui/js/jquery-ui.js
deleted file mode 100644
index b5e8d2e..0000000
--- a/ui/js/jquery-ui.js
+++ /dev/null
@@ -1,14936 +0,0 @@
-/*! jQuery UI - v1.10.1 - 2013-02-15
-* http://jqueryui.com
-* Includes: jquery.ui.core.js, jquery.ui.widget.js, jquery.ui.mouse.js, jquery.ui.draggable.js, jquery.ui.droppable.js, jquery.ui.resizable.js, jquery.ui.selectable.js, jquery.ui.sortable.js, jquery.ui.effect.js, jquery.ui.accordion.js, jquery.ui.autocomplete.js, jquery.ui.button.js, jquery.ui.datepicker.js, jquery.ui.dialog.js, jquery.ui.effect-blind.js, jquery.ui.effect-bounce.js, jquery.ui.effect-clip.js, jquery.ui.effect-drop.js, jquery.ui.effect-explode.js, jquery.ui.effect-fade.js, jquery.ui.effect-fold.js, jquery.ui.effect-highlight.js, jquery.ui.effect-pulsate.js, jquery.ui.effect-scale.js, jquery.ui.effect-shake.js, jquery.ui.effect-slide.js, jquery.ui.effect-transfer.js, jquery.ui.menu.js, jquery.ui.position.js, jquery.ui.progressbar.js, jquery.ui.slider.js, jquery.ui.spinner.js, jquery.ui.tabs.js, jquery.ui.tooltip.js
-* Copyright 2013 jQuery Foundation and other contributors; Licensed MIT */
-
-(function( $, undefined ) {
-
-var uuid = 0,
- runiqueId = /^ui-id-\d+$/;
-
-// prevent duplicate loading
-// this is only a problem because we proxy existing functions
-// and we don't want to double proxy them
-$.ui = $.ui || {};
-if ( $.ui.version ) {
- return;
-}
-
-$.extend( $.ui, {
- version: "1.10.1",
-
- keyCode: {
- BACKSPACE: 8,
- COMMA: 188,
- DELETE: 46,
- DOWN: 40,
- END: 35,
- ENTER: 13,
- ESCAPE: 27,
- HOME: 36,
- LEFT: 37,
- NUMPAD_ADD: 107,
- NUMPAD_DECIMAL: 110,
- NUMPAD_DIVIDE: 111,
- NUMPAD_ENTER: 108,
- NUMPAD_MULTIPLY: 106,
- NUMPAD_SUBTRACT: 109,
- PAGE_DOWN: 34,
- PAGE_UP: 33,
- PERIOD: 190,
- RIGHT: 39,
- SPACE: 32,
- TAB: 9,
- UP: 38
- }
-});
-
-// plugins
-$.fn.extend({
- _focus: $.fn.focus,
- focus: function( delay, fn ) {
- return typeof delay === "number" ?
- this.each(function() {
- var elem = this;
- setTimeout(function() {
- $( elem ).focus();
- if ( fn ) {
- fn.call( elem );
- }
- }, delay );
- }) :
- this._focus.apply( this, arguments );
- },
-
- scrollParent: function() {
- var scrollParent;
- if (($.ui.ie && (/(static|relative)/).test(this.css("position"))) || (/absolute/).test(this.css("position"))) {
- scrollParent = this.parents().filter(function() {
- return (/(relative|absolute|fixed)/).test($.css(this,"position")) && (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
- }).eq(0);
- } else {
- scrollParent = this.parents().filter(function() {
- return (/(auto|scroll)/).test($.css(this,"overflow")+$.css(this,"overflow-y")+$.css(this,"overflow-x"));
- }).eq(0);
- }
-
- return (/fixed/).test(this.css("position")) || !scrollParent.length ? $(document) : scrollParent;
- },
-
- zIndex: function( zIndex ) {
- if ( zIndex !== undefined ) {
- return this.css( "zIndex", zIndex );
- }
-
- if ( this.length ) {
- var elem = $( this[ 0 ] ), position, value;
- while ( elem.length && elem[ 0 ] !== document ) {
- // Ignore z-index if position is set to a value where z-index is ignored by the browser
- // This makes behavior of this function consistent across browsers
- // WebKit always returns auto if the element is positioned
- position = elem.css( "position" );
- if ( position === "absolute" || position === "relative" || position === "fixed" ) {
- // IE returns 0 when zIndex is not specified
- // other browsers return a string
- // we ignore the case of nested elements with an explicit value of 0
- // <div style="z-index: -10;"><div style="z-index: 0;"></div></div>
- value = parseInt( elem.css( "zIndex" ), 10 );
- if ( !isNaN( value ) && value !== 0 ) {
- return value;
- }
- }
- elem = elem.parent();
- }
- }
-
- return 0;
- },
-
- uniqueId: function() {
- return this.each(function() {
- if ( !this.id ) {
- this.id = "ui-id-" + (++uuid);
- }
- });
- },
-
- removeUniqueId: function() {
- return this.each(function() {
- if ( runiqueId.test( this.id ) ) {
- $( this ).removeAttr( "id" );
- }
- });
- }
-});
-
-// selectors
-function focusable( element, isTabIndexNotNaN ) {
- var map, mapName, img,
- nodeName = element.nodeName.toLowerCase();
- if ( "area" === nodeName ) {
- map = element.parentNode;
- mapName = map.name;
- if ( !element.href || !mapName || map.nodeName.toLowerCase() !== "map" ) {
- return false;
- }
- img = $( "img[usemap=#" + mapName + "]" )[0];
- return !!img && visible( img );
- }
- return ( /input|select|textarea|button|object/.test( nodeName ) ?
- !element.disabled :
- "a" === nodeName ?
- element.href || isTabIndexNotNaN :
- isTabIndexNotNaN) &&
- // the element and all of its ancestors must be visible
- visible( element );
-}
-
-function visible( element ) {
- return $.expr.filters.visible( element ) &&
- !$( element ).parents().addBack().filter(function() {
- return $.css( this, "visibility" ) === "hidden";
- }).length;
-}
-
-$.extend( $.expr[ ":" ], {
- data: $.expr.createPseudo ?
- $.expr.createPseudo(function( dataName ) {
- return function( elem ) {
- return !!$.data( elem, dataName );
- };
- }) :
- // support: jQuery <1.8
- function( elem, i, match ) {
- return !!$.data( elem, match[ 3 ] );
- },
-
- focusable: function( element ) {
- return focusable( element, !isNaN( $.attr( element, "tabindex" ) ) );
- },
-
- tabbable: function( element ) {
- var tabIndex = $.attr( element, "tabindex" ),
- isTabIndexNaN = isNaN( tabIndex );
- return ( isTabIndexNaN || tabIndex >= 0 ) && focusable( element, !isTabIndexNaN );
- }
-});
-
-// support: jQuery <1.8
-if ( !$( "<a>" ).outerWidth( 1 ).jquery ) {
- $.each( [ "Width", "Height" ], function( i, name ) {
- var side = name === "Width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ],
- type = name.toLowerCase(),
- orig = {
- innerWidth: $.fn.innerWidth,
- innerHeight: $.fn.innerHeight,
- outerWidth: $.fn.outerWidth,
- outerHeight: $.fn.outerHeight
- };
-
- function reduce( elem, size, border, margin ) {
- $.each( side, function() {
- size -= parseFloat( $.css( elem, "padding" + this ) ) || 0;
- if ( border ) {
- size -= parseFloat( $.css( elem, "border" + this + "Width" ) ) || 0;
- }
- if ( margin ) {
- size -= parseFloat( $.css( elem, "margin" + this ) ) || 0;
- }
- });
- return size;
- }
-
- $.fn[ "inner" + name ] = function( size ) {
- if ( size === undefined ) {
- return orig[ "inner" + name ].call( this );
- }
-
- return this.each(function() {
- $( this ).css( type, reduce( this, size ) + "px" );
- });
- };
-
- $.fn[ "outer" + name] = function( size, margin ) {
- if ( typeof size !== "number" ) {
- return orig[ "outer" + name ].call( this, size );
- }
-
- return this.each(function() {
- $( this).css( type, reduce( this, size, true, margin ) + "px" );
- });
- };
- });
-}
-
-// support: jQuery <1.8
-if ( !$.fn.addBack ) {
- $.fn.addBack = function( selector ) {
- return this.add( selector == null ?
- this.prevObject : this.prevObject.filter( selector )
- );
- };
-}
-
-// support: jQuery 1.6.1, 1.6.2 (http://bugs.jquery.com/ticket/9413)
-if ( $( "<a>" ).data( "a-b", "a" ).removeData( "a-b" ).data( "a-b" ) ) {
- $.fn.removeData = (function( removeData ) {
- return function( key ) {
- if ( arguments.length ) {
- return removeData.call( this, $.camelCase( key ) );
- } else {
- return removeData.call( this );
- }
- };
- })( $.fn.removeData );
-}
-
-
-
-
-
-// deprecated
-$.ui.ie = !!/msie [\w.]+/.exec( navigator.userAgent.toLowerCase() );
-
-$.support.selectstart = "onselectstart" in document.createElement( "div" );
-$.fn.extend({
- disableSelection: function() {
- return this.bind( ( $.support.selectstart ? "selectstart" : "mousedown" ) +
- ".ui-disableSelection", function( event ) {
- event.preventDefault();
- });
- },
-
- enableSelection: function() {
- return this.unbind( ".ui-disableSelection" );
- }
-});
-
-$.extend( $.ui, {
- // $.ui.plugin is deprecated. Use the proxy pattern instead.
- plugin: {
- add: function( module, option, set ) {
- var i,
- proto = $.ui[ module ].prototype;
- for ( i in set ) {
- proto.plugins[ i ] = proto.plugins[ i ] || [];
- proto.plugins[ i ].push( [ option, set[ i ] ] );
- }
- },
- call: function( instance, name, args ) {
- var i,
- set = instance.plugins[ name ];
- if ( !set || !instance.element[ 0 ].parentNode || instance.element[ 0 ].parentNode.nodeType === 11 ) {
- return;
- }
-
- for ( i = 0; i < set.length; i++ ) {
- if ( instance.options[ set[ i ][ 0 ] ] ) {
- set[ i ][ 1 ].apply( instance.element, args );
- }
- }
- }
- },
-
- // only used by resizable
- hasScroll: function( el, a ) {
-
- //If overflow is hidden, the element might have extra content, but the user wants to hide it
- if ( $( el ).css( "overflow" ) === "hidden") {
- return false;
- }
-
- var scroll = ( a && a === "left" ) ? "scrollLeft" : "scrollTop",
- has = false;
-
- if ( el[ scroll ] > 0 ) {
- return true;
- }
-
- // TODO: determine which cases actually cause this to happen
- // if the element doesn't have the scroll set, see if it's possible to
- // set the scroll
- el[ scroll ] = 1;
- has = ( el[ scroll ] > 0 );
- el[ scroll ] = 0;
- return has;
- }
-});
-
-})( jQuery );
-
-(function( $, undefined ) {
-
-var uuid = 0,
- slice = Array.prototype.slice,
- _cleanData = $.cleanData;
-$.cleanData = function( elems ) {
- for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
- try {
- $( elem ).triggerHandler( "remove" );
- // http://bugs.jquery.com/ticket/8235
- } catch( e ) {}
- }
- _cleanData( elems );
-};
-
-$.widget = function( name, base, prototype ) {
- var fullName, existingConstructor, constructor, basePrototype,
- // proxiedPrototype allows the provided prototype to remain unmodified
- // so that it can be used as a mixin for multiple widgets (#8876)
- proxiedPrototype = {},
- namespace = name.split( "." )[ 0 ];
-
- name = name.split( "." )[ 1 ];
- fullName = namespace + "-" + name;
-
- if ( !prototype ) {
- prototype = base;
- base = $.Widget;
- }
-
- // create selector for plugin
- $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
- return !!$.data( elem, fullName );
- };
-
- $[ namespace ] = $[ namespace ] || {};
- existingConstructor = $[ namespace ][ name ];
- constructor = $[ namespace ][ name ] = function( options, element ) {
- // allow instantiation without "new" keyword
- if ( !this._createWidget ) {
- return new constructor( options, element );
- }
-
- // allow instantiation without initializing for simple inheritance
- // must use "new" keyword (the code above always passes args)
- if ( arguments.length ) {
- this._createWidget( options, element );
- }
- };
- // extend with the existing constructor to carry over any static properties
- $.extend( constructor, existingConstructor, {
- version: prototype.version,
- // copy the object used to create the prototype in case we need to
- // redefine the widget later
- _proto: $.extend( {}, prototype ),
- // track widgets that inherit from this widget in case this widget is
- // redefined after a widget inherits from it
- _childConstructors: []
- });
-
- basePrototype = new base();
- // we need to make the options hash a property directly on the new instance
- // otherwise we'll modify the options hash on the prototype that we're
- // inheriting from
- basePrototype.options = $.widget.extend( {}, basePrototype.options );
- $.each( prototype, function( prop, value ) {
- if ( !$.isFunction( value ) ) {
- proxiedPrototype[ prop ] = value;
- return;
- }
- proxiedPrototype[ prop ] = (function() {
- var _super = function() {
- return base.prototype[ prop ].apply( this, arguments );
- },
- _superApply = function( args ) {
- return base.prototype[ prop ].apply( this, args );
- };
- return function() {
- var __super = this._super,
- __superApply = this._superApply,
- returnValue;
-
- this._super = _super;
- this._superApply = _superApply;
-
- returnValue = value.apply( this, arguments );
-
- this._super = __super;
- this._superApply = __superApply;
-
- return returnValue;
- };
- })();
- });
- constructor.prototype = $.widget.extend( basePrototype, {
- // TODO: remove support for widgetEventPrefix
- // always use the name + a colon as the prefix, e.g., draggable:start
- // don't prefix for widgets that aren't DOM-based
- widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
- }, proxiedPrototype, {
- constructor: constructor,
- namespace: namespace,
- widgetName: name,
- widgetFullName: fullName
- });
-
- // If this widget is being redefined then we need to find all widgets that
- // are inheriting from it and redefine all of them so that they inherit from
- // the new version of this widget. We're essentially trying to replace one
- // level in the prototype chain.
- if ( existingConstructor ) {
- $.each( existingConstructor._childConstructors, function( i, child ) {
- var childPrototype = child.prototype;
-
- // redefine the child widget using the same prototype that was
- // originally used, but inherit from the new version of the base
- $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
- });
- // remove the list of existing child constructors from the old constructor
- // so the old child constructors can be garbage collected
- delete existingConstructor._childConstructors;
- } else {
- base._childConstructors.push( constructor );
- }
-
- $.widget.bridge( name, constructor );
-};
-
-$.widget.extend = function( target ) {
- var input = slice.call( arguments, 1 ),
- inputIndex = 0,
- inputLength = input.length,
- key,
- value;
- for ( ; inputIndex < inputLength; inputIndex++ ) {
- for ( key in input[ inputIndex ] ) {
- value = input[ inputIndex ][ key ];
- if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
- // Clone objects
- if ( $.isPlainObject( value ) ) {
- target[ key ] = $.isPlainObject( target[ key ] ) ?
- $.widget.extend( {}, target[ key ], value ) :
- // Don't extend strings, arrays, etc. with objects
- $.widget.extend( {}, value );
- // Copy everything else by reference
- } else {
- target[ key ] = value;
- }
- }
- }
- }
- return target;
-};
-
-$.widget.bridge = function( name, object ) {
- var fullName = object.prototype.widgetFullName || name;
- $.fn[ name ] = function( options ) {
- var isMethodCall = typeof options === "string",
- args = slice.call( arguments, 1 ),
- returnValue = this;
-
- // allow multiple hashes to be passed on init
- options = !isMethodCall && args.length ?
- $.widget.extend.apply( null, [ options ].concat(args) ) :
- options;
-
- if ( isMethodCall ) {
- this.each(function() {
- var methodValue,
- instance = $.data( this, fullName );
- if ( !instance ) {
- return $.error( "cannot call methods on " + name + " prior to initialization; " +
- "attempted to call method '" + options + "'" );
- }
- if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
- return $.error( "no such method '" + options + "' for " + name + " widget instance" );
- }
- methodValue = instance[ options ].apply( instance, args );
- if ( methodValue !== instance && methodValue !== undefined ) {
- returnValue = methodValue && methodValue.jquery ?
- returnValue.pushStack( methodValue.get() ) :
- methodValue;
- return false;
- }
- });
- } else {
- this.each(function() {
- var instance = $.data( this, fullName );
- if ( instance ) {
- instance.option( options || {} )._init();
- } else {
- $.data( this, fullName, new object( options, this ) );
- }
- });
- }
-
- return returnValue;
- };
-};
-
-$.Widget = function( /* options, element */ ) {};
-$.Widget._childConstructors = [];
-
-$.Widget.prototype = {
- widgetName: "widget",
- widgetEventPrefix: "",
- defaultElement: "<div>",
- options: {
- disabled: false,
-
- // callbacks
- create: null
- },
- _createWidget: function( options, element ) {
- element = $( element || this.defaultElement || this )[ 0 ];
- this.element = $( element );
- this.uuid = uuid++;
- this.eventNamespace = "." + this.widgetName + this.uuid;
- this.options = $.widget.extend( {},
- this.options,
- this._getCreateOptions(),
- options );
-
- this.bindings = $();
- this.hoverable = $();
- this.focusable = $();
-
- if ( element !== this ) {
- $.data( element, this.widgetFullName, this );
- this._on( true, this.element, {
- remove: function( event ) {
- if ( event.target === element ) {
- this.destroy();
- }
- }
- });
- this.document = $( element.style ?
- // element within the document
- element.ownerDocument :
- // element is window or document
- element.document || element );
- this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
- }
-
- this._create();
- this._trigger( "create", null, this._getCreateEventData() );
- this._init();
- },
- _getCreateOptions: $.noop,
- _getCreateEventData: $.noop,
- _create: $.noop,
- _init: $.noop,
-
- destroy: function() {
- this._destroy();
- // we can probably remove the unbind calls in 2.0
- // all event bindings should go through this._on()
- this.element
- .unbind( this.eventNamespace )
- // 1.9 BC for #7810
- // TODO remove dual storage
- .removeData( this.widgetName )
- .removeData( this.widgetFullName )
- // support: jquery <1.6.3
- // http://bugs.jquery.com/ticket/9413
- .removeData( $.camelCase( this.widgetFullName ) );
- this.widget()
- .unbind( this.eventNamespace )
- .removeAttr( "aria-disabled" )
- .removeClass(
- this.widgetFullName + "-disabled " +
- "ui-state-disabled" );
-
- // clean up events and states
- this.bindings.unbind( this.eventNamespace );
- this.hoverable.removeClass( "ui-state-hover" );
- this.focusable.removeClass( "ui-state-focus" );
- },
- _destroy: $.noop,
-
- widget: function() {
- return this.element;
- },
-
- option: function( key, value ) {
- var options = key,
- parts,
- curOption,
- i;
-
- if ( arguments.length === 0 ) {
- // don't return a reference to the internal hash
- return $.widget.extend( {}, this.options );
- }
-
- if ( typeof key === "string" ) {
- // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
- options = {};
- parts = key.split( "." );
- key = parts.shift();
- if ( parts.length ) {
- curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
- for ( i = 0; i < parts.length - 1; i++ ) {
- curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
- curOption = curOption[ parts[ i ] ];
- }
- key = parts.pop();
- if ( value === undefined ) {
- return curOption[ key ] === undefined ? null : curOption[ key ];
- }
- curOption[ key ] = value;
- } else {
- if ( value === undefined ) {
- return this.options[ key ] === undefined ? null : this.options[ key ];
- }
- options[ key ] = value;
- }
- }
-
- this._setOptions( options );
-
- return this;
- },
- _setOptions: function( options ) {
- var key;
-
- for ( key in options ) {
- this._setOption( key, options[ key ] );
- }
-
- return this;
- },
- _setOption: function( key, value ) {
- this.options[ key ] = value;
-
- if ( key === "disabled" ) {
- this.widget()
- .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
- .attr( "aria-disabled", value );
- this.hoverable.removeClass( "ui-state-hover" );
- this.focusable.removeClass( "ui-state-focus" );
- }
-
- return this;
- },
-
- enable: function() {
- return this._setOption( "disabled", false );
- },
- disable: function() {
- return this._setOption( "disabled", true );
- },
-
- _on: function( suppressDisabledCheck, element, handlers ) {
- var delegateElement,
- instance = this;
-
- // no suppressDisabledCheck flag, shuffle arguments
- if ( typeof suppressDisabledCheck !== "boolean" ) {
- handlers = element;
- element = suppressDisabledCheck;
- suppressDisabledCheck = false;
- }
-
- // no element argument, shuffle and use this.element
- if ( !handlers ) {
- handlers = element;
- element = this.element;
- delegateElement = this.widget();
- } else {
- // accept selectors, DOM elements
- element = delegateElement = $( element );
- this.bindings = this.bindings.add( element );
- }
-
- $.each( handlers, function( event, handler ) {
- function handlerProxy() {
- // allow widgets to customize the disabled handling
- // - disabled as an array instead of boolean
- // - disabled class as method for disabling individual parts
- if ( !suppressDisabledCheck &&
- ( instance.options.disabled === true ||
- $( this ).hasClass( "ui-state-disabled" ) ) ) {
- return;
- }
- return ( typeof handler === "string" ? instance[ handler ] : handler )
- .apply( instance, arguments );
- }
-
- // copy the guid so direct unbinding works
- if ( typeof handler !== "string" ) {
- handlerProxy.guid = handler.guid =
- handler.guid || handlerProxy.guid || $.guid++;
- }
-
- var match = event.match( /^(\w+)\s*(.*)$/ ),
- eventName = match[1] + instance.eventNamespace,
- selector = match[2];
- if ( selector ) {
- delegateElement.delegate( selector, eventName, handlerProxy );
- } else {
- element.bind( eventName, handlerProxy );
- }
- });
- },
-
- _off: function( element, eventName ) {
- eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
- element.unbind( eventName ).undelegate( eventName );
- },
-
- _delay: function( handler, delay ) {
- function handlerProxy() {
- return ( typeof handler === "string" ? instance[ handler ] : handler )
- .apply( instance, arguments );
- }
- var instance = this;
- return setTimeout( handlerProxy, delay || 0 );
- },
-
- _hoverable: function( element ) {
- this.hoverable = this.hoverable.add( element );
- this._on( element, {
- mouseenter: function( event ) {
- $( event.currentTarget ).addClass( "ui-state-hover" );
- },
- mouseleave: function( event ) {
- $( event.currentTarget ).removeClass( "ui-state-hover" );
- }
- });
- },
-
- _focusable: function( element ) {
- this.focusable = this.focusable.add( element );
- this._on( element, {
- focusin: function( event ) {
- $( event.currentTarget ).addClass( "ui-state-focus" );
- },
- focusout: function( event ) {
- $( event.currentTarget ).removeClass( "ui-state-focus" );
- }
- });
- },
-
- _trigger: function( type, event, data ) {
- var prop, orig,
- callback = this.options[ type ];
-
- data = data || {};
- event = $.Event( event );
- event.type = ( type === this.widgetEventPrefix ?
- type :
- this.widgetEventPrefix + type ).toLowerCase();
- // the original event may come from any element
- // so we need to reset the target on the new event
- event.target = this.element[ 0 ];
-
- // copy original event properties over to the new event
- orig = event.originalEvent;
- if ( orig ) {
- for ( prop in orig ) {
- if ( !( prop in event ) ) {
- event[ prop ] = orig[ prop ];
- }
- }
- }
-
- this.element.trigger( event, data );
- return !( $.isFunction( callback ) &&
- callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
- event.isDefaultPrevented() );
- }
-};
-
-$.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
- $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
- if ( typeof options === "string" ) {
- options = { effect: options };
- }
- var hasOptions,
- effectName = !options ?
- method :
- options === true || typeof options === "number" ?
- defaultEffect :
- options.effect || defaultEffect;
- options = options || {};
- if ( typeof options === "number" ) {
- options = { duration: options };
- }
- hasOptions = !$.isEmptyObject( options );
- options.complete = callback;
- if ( options.delay ) {
- element.delay( options.delay );
- }
- if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
- element[ method ]( options );
- } else if ( effectName !== method && element[ effectName ] ) {
- element[ effectName ]( options.duration, options.easing, callback );
- } else {
- element.queue(function( next ) {
- $( this )[ method ]();
- if ( callback ) {
- callback.call( element[ 0 ] );
- }
- next();
- });
- }
- };
-});
-
-})( jQuery );
-
-(function( $, undefined ) {
-
-var mouseHandled = false;
-$( document ).mouseup( function() {
- mouseHandled = false;
-});
-
-$.widget("ui.mouse", {
- version: "1.10.1",
- options: {
- cancel: "input,textarea,button,select,option",
- distance: 1,
- delay: 0
- },
- _mouseInit: function() {
- var that = this;
-
- this.element
- .bind("mousedown."+this.widgetName, function(event) {
- return that._mouseDown(event);
- })
- .bind("click."+this.widgetName, function(event) {
- if (true === $.data(event.target, that.widgetName + ".preventClickEvent")) {
- $.removeData(event.target, that.widgetName + ".preventClickEvent");
- event.stopImmediatePropagation();
- return false;
- }
- });
-
- this.started = false;
- },
-
- // TODO: make sure destroying one instance of mouse doesn't mess with
- // other instances of mouse
- _mouseDestroy: function() {
- this.element.unbind("."+this.widgetName);
- if ( this._mouseMoveDelegate ) {
- $(document)
- .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
- .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
- }
- },
-
- _mouseDown: function(event) {
- // don't let more than one widget handle mouseStart
- if( mouseHandled ) { return; }
-
- // we may have missed mouseup (out of window)
- (this._mouseStarted && this._mouseUp(event));
-
- this._mouseDownEvent = event;
-
- var that = this,
- btnIsLeft = (event.which === 1),
- // event.target.nodeName works around a bug in IE 8 with
- // disabled inputs (#7620)
- elIsCancel = (typeof this.options.cancel === "string" && event.target.nodeName ? $(event.target).closest(this.options.cancel).length : false);
- if (!btnIsLeft || elIsCancel || !this._mouseCapture(event)) {
- return true;
- }
-
- this.mouseDelayMet = !this.options.delay;
- if (!this.mouseDelayMet) {
- this._mouseDelayTimer = setTimeout(function() {
- that.mouseDelayMet = true;
- }, this.options.delay);
- }
-
- if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
- this._mouseStarted = (this._mouseStart(event) !== false);
- if (!this._mouseStarted) {
- event.preventDefault();
- return true;
- }
- }
-
- // Click event may never have fired (Gecko & Opera)
- if (true === $.data(event.target, this.widgetName + ".preventClickEvent")) {
- $.removeData(event.target, this.widgetName + ".preventClickEvent");
- }
-
- // these delegates are required to keep context
- this._mouseMoveDelegate = function(event) {
- return that._mouseMove(event);
- };
- this._mouseUpDelegate = function(event) {
- return that._mouseUp(event);
- };
- $(document)
- .bind("mousemove."+this.widgetName, this._mouseMoveDelegate)
- .bind("mouseup."+this.widgetName, this._mouseUpDelegate);
-
- event.preventDefault();
-
- mouseHandled = true;
- return true;
- },
-
- _mouseMove: function(event) {
- // IE mouseup check - mouseup happened when mouse was out of window
- if ($.ui.ie && ( !document.documentMode || document.documentMode < 9 ) && !event.button) {
- return this._mouseUp(event);
- }
-
- if (this._mouseStarted) {
- this._mouseDrag(event);
- return event.preventDefault();
- }
-
- if (this._mouseDistanceMet(event) && this._mouseDelayMet(event)) {
- this._mouseStarted =
- (this._mouseStart(this._mouseDownEvent, event) !== false);
- (this._mouseStarted ? this._mouseDrag(event) : this._mouseUp(event));
- }
-
- return !this._mouseStarted;
- },
-
- _mouseUp: function(event) {
- $(document)
- .unbind("mousemove."+this.widgetName, this._mouseMoveDelegate)
- .unbind("mouseup."+this.widgetName, this._mouseUpDelegate);
-
- if (this._mouseStarted) {
- this._mouseStarted = false;
-
- if (event.target === this._mouseDownEvent.target) {
- $.data(event.target, this.widgetName + ".preventClickEvent", true);
- }
-
- this._mouseStop(event);
- }
-
- return false;
- },
-
- _mouseDistanceMet: function(event) {
- return (Math.max(
- Math.abs(this._mouseDownEvent.pageX - event.pageX),
- Math.abs(this._mouseDownEvent.pageY - event.pageY)
- ) >= this.options.distance
- );
- },
-
- _mouseDelayMet: function(/* event */) {
- return this.mouseDelayMet;
- },
-
- // These are placeholder methods, to be overriden by extending plugin
- _mouseStart: function(/* event */) {},
- _mouseDrag: function(/* event */) {},
- _mouseStop: function(/* event */) {},
- _mouseCapture: function(/* event */) { return true; }
-});
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.widget("ui.draggable", $.ui.mouse, {
- version: "1.10.1",
- widgetEventPrefix: "drag",
- options: {
- addClasses: true,
- appendTo: "parent",
- axis: false,
- connectToSortable: false,
- containment: false,
- cursor: "auto",
- cursorAt: false,
- grid: false,
- handle: false,
- helper: "original",
- iframeFix: false,
- opacity: false,
- refreshPositions: false,
- revert: false,
- revertDuration: 500,
- scope: "default",
- scroll: true,
- scrollSensitivity: 20,
- scrollSpeed: 20,
- snap: false,
- snapMode: "both",
- snapTolerance: 20,
- stack: false,
- zIndex: false,
-
- // callbacks
- drag: null,
- start: null,
- stop: null
- },
- _create: function() {
-
- if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
- this.element[0].style.position = "relative";
- }
- if (this.options.addClasses){
- this.element.addClass("ui-draggable");
- }
- if (this.options.disabled){
- this.element.addClass("ui-draggable-disabled");
- }
-
- this._mouseInit();
-
- },
-
- _destroy: function() {
- this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
- this._mouseDestroy();
- },
-
- _mouseCapture: function(event) {
-
- var o = this.options;
-
- // among others, prevent a drag on a resizable-handle
- if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
- return false;
- }
-
- //Quit if we're not on a valid handle
- this.handle = this._getHandle(event);
- if (!this.handle) {
- return false;
- }
-
- $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
- $("<div class='ui-draggable-iframeFix' style='background: #fff;'></div>")
- .css({
- width: this.offsetWidth+"px", height: this.offsetHeight+"px",
- position: "absolute", opacity: "0.001", zIndex: 1000
- })
- .css($(this).offset())
- .appendTo("body");
- });
-
- return true;
-
- },
-
- _mouseStart: function(event) {
-
- var o = this.options;
-
- //Create and append the visible helper
- this.helper = this._createHelper(event);
-
- this.helper.addClass("ui-draggable-dragging");
-
- //Cache the helper size
- this._cacheHelperProportions();
-
- //If ddmanager is used for droppables, set the global draggable
- if($.ui.ddmanager) {
- $.ui.ddmanager.current = this;
- }
-
- /*
- * - Position generation -
- * This block generates everything position related - it's the core of draggables.
- */
-
- //Cache the margins of the original element
- this._cacheMargins();
-
- //Store the helper's css position
- this.cssPosition = this.helper.css("position");
- this.scrollParent = this.helper.scrollParent();
-
- //The element's absolute position on the page minus margins
- this.offset = this.positionAbs = this.element.offset();
- this.offset = {
- top: this.offset.top - this.margins.top,
- left: this.offset.left - this.margins.left
- };
-
- $.extend(this.offset, {
- click: { //Where the click happened, relative to the element
- left: event.pageX - this.offset.left,
- top: event.pageY - this.offset.top
- },
- parent: this._getParentOffset(),
- relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
- });
-
- //Generate the original position
- this.originalPosition = this.position = this._generatePosition(event);
- this.originalPageX = event.pageX;
- this.originalPageY = event.pageY;
-
- //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
- (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
-
- //Set a containment if given in the options
- if(o.containment) {
- this._setContainment();
- }
-
- //Trigger event + callbacks
- if(this._trigger("start", event) === false) {
- this._clear();
- return false;
- }
-
- //Recache the helper size
- this._cacheHelperProportions();
-
- //Prepare the droppable offsets
- if ($.ui.ddmanager && !o.dropBehaviour) {
- $.ui.ddmanager.prepareOffsets(this, event);
- }
-
-
- this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
-
- //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
- if ( $.ui.ddmanager ) {
- $.ui.ddmanager.dragStart(this, event);
- }
-
- return true;
- },
-
- _mouseDrag: function(event, noPropagation) {
-
- //Compute the helpers position
- this.position = this._generatePosition(event);
- this.positionAbs = this._convertPositionTo("absolute");
-
- //Call plugins and callbacks and use the resulting position if something is returned
- if (!noPropagation) {
- var ui = this._uiHash();
- if(this._trigger("drag", event, ui) === false) {
- this._mouseUp({});
- return false;
- }
- this.position = ui.position;
- }
-
- if(!this.options.axis || this.options.axis !== "y") {
- this.helper[0].style.left = this.position.left+"px";
- }
- if(!this.options.axis || this.options.axis !== "x") {
- this.helper[0].style.top = this.position.top+"px";
- }
- if($.ui.ddmanager) {
- $.ui.ddmanager.drag(this, event);
- }
-
- return false;
- },
-
- _mouseStop: function(event) {
-
- //If we are using droppables, inform the manager about the drop
- var element,
- that = this,
- elementInDom = false,
- dropped = false;
- if ($.ui.ddmanager && !this.options.dropBehaviour) {
- dropped = $.ui.ddmanager.drop(this, event);
- }
-
- //if a drop comes from outside (a sortable)
- if(this.dropped) {
- dropped = this.dropped;
- this.dropped = false;
- }
-
- //if the original element is no longer in the DOM don't bother to continue (see #8269)
- element = this.element[0];
- while ( element && (element = element.parentNode) ) {
- if (element === document ) {
- elementInDom = true;
- }
- }
- if ( !elementInDom && this.options.helper === "original" ) {
- return false;
- }
-
- if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
- $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
- if(that._trigger("stop", event) !== false) {
- that._clear();
- }
- });
- } else {
- if(this._trigger("stop", event) !== false) {
- this._clear();
- }
- }
-
- return false;
- },
-
- _mouseUp: function(event) {
- //Remove frame helpers
- $("div.ui-draggable-iframeFix").each(function() {
- this.parentNode.removeChild(this);
- });
-
- //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
- if( $.ui.ddmanager ) {
- $.ui.ddmanager.dragStop(this, event);
- }
-
- return $.ui.mouse.prototype._mouseUp.call(this, event);
- },
-
- cancel: function() {
-
- if(this.helper.is(".ui-draggable-dragging")) {
- this._mouseUp({});
- } else {
- this._clear();
- }
-
- return this;
-
- },
-
- _getHandle: function(event) {
-
- var handle = !this.options.handle || !$(this.options.handle, this.element).length ? true : false;
- $(this.options.handle, this.element)
- .find("*")
- .addBack()
- .each(function() {
- if(this === event.target) {
- handle = true;
- }
- });
-
- return handle;
-
- },
-
- _createHelper: function(event) {
-
- var o = this.options,
- helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
-
- if(!helper.parents("body").length) {
- helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
- }
-
- if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
- helper.css("position", "absolute");
- }
-
- return helper;
-
- },
-
- _adjustOffsetFromHelper: function(obj) {
- if (typeof obj === "string") {
- obj = obj.split(" ");
- }
- if ($.isArray(obj)) {
- obj = {left: +obj[0], top: +obj[1] || 0};
- }
- if ("left" in obj) {
- this.offset.click.left = obj.left + this.margins.left;
- }
- if ("right" in obj) {
- this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
- }
- if ("top" in obj) {
- this.offset.click.top = obj.top + this.margins.top;
- }
- if ("bottom" in obj) {
- this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
- }
- },
-
- _getParentOffset: function() {
-
- //Get the offsetParent and cache its position
- this.offsetParent = this.helper.offsetParent();
- var po = this.offsetParent.offset();
-
- // This is a special case where we need to modify a offset calculated on start, since the following happened:
- // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
- // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
- // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
- if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
- po.left += this.scrollParent.scrollLeft();
- po.top += this.scrollParent.scrollTop();
- }
-
- //This needs to be actually done for all browsers, since pageX/pageY includes this information
- //Ugly IE fix
- if((this.offsetParent[0] === document.body) ||
- (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
- po = { top: 0, left: 0 };
- }
-
- return {
- top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
- left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
- };
-
- },
-
- _getRelativeOffset: function() {
-
- if(this.cssPosition === "relative") {
- var p = this.element.position();
- return {
- top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
- left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
- };
- } else {
- return { top: 0, left: 0 };
- }
-
- },
-
- _cacheMargins: function() {
- this.margins = {
- left: (parseInt(this.element.css("marginLeft"),10) || 0),
- top: (parseInt(this.element.css("marginTop"),10) || 0),
- right: (parseInt(this.element.css("marginRight"),10) || 0),
- bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
- };
- },
-
- _cacheHelperProportions: function() {
- this.helperProportions = {
- width: this.helper.outerWidth(),
- height: this.helper.outerHeight()
- };
- },
-
- _setContainment: function() {
-
- var over, c, ce,
- o = this.options;
-
- if(o.containment === "parent") {
- o.containment = this.helper[0].parentNode;
- }
- if(o.containment === "document" || o.containment === "window") {
- this.containment = [
- o.containment === "document" ? 0 : $(window).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
- o.containment === "document" ? 0 : $(window).scrollTop() - this.offset.relative.top - this.offset.parent.top,
- (o.containment === "document" ? 0 : $(window).scrollLeft()) + $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
- (o.containment === "document" ? 0 : $(window).scrollTop()) + ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
- ];
- }
-
- if(!(/^(document|window|parent)$/).test(o.containment) && o.containment.constructor !== Array) {
- c = $(o.containment);
- ce = c[0];
-
- if(!ce) {
- return;
- }
-
- over = ($(ce).css("overflow") !== "hidden");
-
- this.containment = [
- (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0),
- (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0),
- (over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left - this.margins.right,
- (over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top - this.margins.bottom
- ];
- this.relative_container = c;
-
- } else if(o.containment.constructor === Array) {
- this.containment = o.containment;
- }
-
- },
-
- _convertPositionTo: function(d, pos) {
-
- if(!pos) {
- pos = this.position;
- }
-
- var mod = d === "absolute" ? 1 : -1,
- scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
-
- return {
- top: (
- pos.top + // The absolute mouse position
- this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
- ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
- ),
- left: (
- pos.left + // The absolute mouse position
- this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
- ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
- )
- };
-
- },
-
- _generatePosition: function(event) {
-
- var containment, co, top, left,
- o = this.options,
- scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
- scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName),
- pageX = event.pageX,
- pageY = event.pageY;
-
- /*
- * - Position constraining -
- * Constrain the position to a mix of grid, containment.
- */
-
- if(this.originalPosition) { //If we are not dragging yet, we won't check for options
- if(this.containment) {
- if (this.relative_container){
- co = this.relative_container.offset();
- containment = [ this.containment[0] + co.left,
- this.containment[1] + co.top,
- this.containment[2] + co.left,
- this.containment[3] + co.top ];
- }
- else {
- containment = this.containment;
- }
-
- if(event.pageX - this.offset.click.left < containment[0]) {
- pageX = containment[0] + this.offset.click.left;
- }
- if(event.pageY - this.offset.click.top < containment[1]) {
- pageY = containment[1] + this.offset.click.top;
- }
- if(event.pageX - this.offset.click.left > containment[2]) {
- pageX = containment[2] + this.offset.click.left;
- }
- if(event.pageY - this.offset.click.top > containment[3]) {
- pageY = containment[3] + this.offset.click.top;
- }
- }
-
- if(o.grid) {
- //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
- top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
- pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
-
- left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
- pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
- }
-
- }
-
- return {
- top: (
- pageY - // The absolute mouse position
- this.offset.click.top - // Click offset (relative to the element)
- this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
- ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
- ),
- left: (
- pageX - // The absolute mouse position
- this.offset.click.left - // Click offset (relative to the element)
- this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
- ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
- )
- };
-
- },
-
- _clear: function() {
- this.helper.removeClass("ui-draggable-dragging");
- if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
- this.helper.remove();
- }
- this.helper = null;
- this.cancelHelperRemoval = false;
- },
-
- // From now on bulk stuff - mainly helpers
-
- _trigger: function(type, event, ui) {
- ui = ui || this._uiHash();
- $.ui.plugin.call(this, type, [event, ui]);
- //The absolute position has to be recalculated after plugins
- if(type === "drag") {
- this.positionAbs = this._convertPositionTo("absolute");
- }
- return $.Widget.prototype._trigger.call(this, type, event, ui);
- },
-
- plugins: {},
-
- _uiHash: function() {
- return {
- helper: this.helper,
- position: this.position,
- originalPosition: this.originalPosition,
- offset: this.positionAbs
- };
- }
-
-});
-
-$.ui.plugin.add("draggable", "connectToSortable", {
- start: function(event, ui) {
-
- var inst = $(this).data("ui-draggable"), o = inst.options,
- uiSortable = $.extend({}, ui, { item: inst.element });
- inst.sortables = [];
- $(o.connectToSortable).each(function() {
- var sortable = $.data(this, "ui-sortable");
- if (sortable && !sortable.options.disabled) {
- inst.sortables.push({
- instance: sortable,
- shouldRevert: sortable.options.revert
- });
- sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
- sortable._trigger("activate", event, uiSortable);
- }
- });
-
- },
- stop: function(event, ui) {
-
- //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
- var inst = $(this).data("ui-draggable"),
- uiSortable = $.extend({}, ui, { item: inst.element });
-
- $.each(inst.sortables, function() {
- if(this.instance.isOver) {
-
- this.instance.isOver = 0;
-
- inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
- this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
-
- //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
- if(this.shouldRevert) {
- this.instance.options.revert = true;
- }
-
- //Trigger the stop of the sortable
- this.instance._mouseStop(event);
-
- this.instance.options.helper = this.instance.options._helper;
-
- //If the helper has been the original item, restore properties in the sortable
- if(inst.options.helper === "original") {
- this.instance.currentItem.css({ top: "auto", left: "auto" });
- }
-
- } else {
- this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
- this.instance._trigger("deactivate", event, uiSortable);
- }
-
- });
-
- },
- drag: function(event, ui) {
-
- var inst = $(this).data("ui-draggable"), that = this;
-
- $.each(inst.sortables, function() {
-
- var innermostIntersecting = false,
- thisSortable = this;
-
- //Copy over some variables to allow calling the sortable's native _intersectsWith
- this.instance.positionAbs = inst.positionAbs;
- this.instance.helperProportions = inst.helperProportions;
- this.instance.offset.click = inst.offset.click;
-
- if(this.instance._intersectsWith(this.instance.containerCache)) {
- innermostIntersecting = true;
- $.each(inst.sortables, function () {
- this.instance.positionAbs = inst.positionAbs;
- this.instance.helperProportions = inst.helperProportions;
- this.instance.offset.click = inst.offset.click;
- if (this !== thisSortable &&
- this.instance._intersectsWith(this.instance.containerCache) &&
- $.contains(thisSortable.instance.element[0], this.instance.element[0])
- ) {
- innermostIntersecting = false;
- }
- return innermostIntersecting;
- });
- }
-
-
- if(innermostIntersecting) {
- //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
- if(!this.instance.isOver) {
-
- this.instance.isOver = 1;
- //Now we fake the start of dragging for the sortable instance,
- //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
- //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
- this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
- this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
- this.instance.options.helper = function() { return ui.helper[0]; };
-
- event.target = this.instance.currentItem[0];
- this.instance._mouseCapture(event, true);
- this.instance._mouseStart(event, true, true);
-
- //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
- this.instance.offset.click.top = inst.offset.click.top;
- this.instance.offset.click.left = inst.offset.click.left;
- this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
- this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
-
- inst._trigger("toSortable", event);
- inst.dropped = this.instance.element; //draggable revert needs that
- //hack so receive/update callbacks work (mostly)
- inst.currentItem = inst.element;
- this.instance.fromOutside = inst;
-
- }
-
- //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
- if(this.instance.currentItem) {
- this.instance._mouseDrag(event);
- }
-
- } else {
-
- //If it doesn't intersect with the sortable, and it intersected before,
- //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
- if(this.instance.isOver) {
-
- this.instance.isOver = 0;
- this.instance.cancelHelperRemoval = true;
-
- //Prevent reverting on this forced stop
- this.instance.options.revert = false;
-
- // The out event needs to be triggered independently
- this.instance._trigger("out", event, this.instance._uiHash(this.instance));
-
- this.instance._mouseStop(event, true);
- this.instance.options.helper = this.instance.options._helper;
-
- //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
- this.instance.currentItem.remove();
- if(this.instance.placeholder) {
- this.instance.placeholder.remove();
- }
-
- inst._trigger("fromSortable", event);
- inst.dropped = false; //draggable revert needs that
- }
-
- }
-
- });
-
- }
-});
-
-$.ui.plugin.add("draggable", "cursor", {
- start: function() {
- var t = $("body"), o = $(this).data("ui-draggable").options;
- if (t.css("cursor")) {
- o._cursor = t.css("cursor");
- }
- t.css("cursor", o.cursor);
- },
- stop: function() {
- var o = $(this).data("ui-draggable").options;
- if (o._cursor) {
- $("body").css("cursor", o._cursor);
- }
- }
-});
-
-$.ui.plugin.add("draggable", "opacity", {
- start: function(event, ui) {
- var t = $(ui.helper), o = $(this).data("ui-draggable").options;
- if(t.css("opacity")) {
- o._opacity = t.css("opacity");
- }
- t.css("opacity", o.opacity);
- },
- stop: function(event, ui) {
- var o = $(this).data("ui-draggable").options;
- if(o._opacity) {
- $(ui.helper).css("opacity", o._opacity);
- }
- }
-});
-
-$.ui.plugin.add("draggable", "scroll", {
- start: function() {
- var i = $(this).data("ui-draggable");
- if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
- i.overflowOffset = i.scrollParent.offset();
- }
- },
- drag: function( event ) {
-
- var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
-
- if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
-
- if(!o.axis || o.axis !== "x") {
- if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
- i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
- } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
- i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
- }
- }
-
- if(!o.axis || o.axis !== "y") {
- if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
- i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
- } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
- i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
- }
- }
-
- } else {
-
- if(!o.axis || o.axis !== "x") {
- if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
- scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
- } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
- scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
- }
- }
-
- if(!o.axis || o.axis !== "y") {
- if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
- scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
- } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
- scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
- }
- }
-
- }
-
- if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
- $.ui.ddmanager.prepareOffsets(i, event);
- }
-
- }
-});
-
-$.ui.plugin.add("draggable", "snap", {
- start: function() {
-
- var i = $(this).data("ui-draggable"),
- o = i.options;
-
- i.snapElements = [];
-
- $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
- var $t = $(this),
- $o = $t.offset();
- if(this !== i.element[0]) {
- i.snapElements.push({
- item: this,
- width: $t.outerWidth(), height: $t.outerHeight(),
- top: $o.top, left: $o.left
- });
- }
- });
-
- },
- drag: function(event, ui) {
-
- var ts, bs, ls, rs, l, r, t, b, i, first,
- inst = $(this).data("ui-draggable"),
- o = inst.options,
- d = o.snapTolerance,
- x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
- y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
-
- for (i = inst.snapElements.length - 1; i >= 0; i--){
-
- l = inst.snapElements[i].left;
- r = l + inst.snapElements[i].width;
- t = inst.snapElements[i].top;
- b = t + inst.snapElements[i].height;
-
- //Yes, I know, this is insane ;)
- if(!((l-d < x1 && x1 < r+d && t-d < y1 && y1 < b+d) || (l-d < x1 && x1 < r+d && t-d < y2 && y2 < b+d) || (l-d < x2 && x2 < r+d && t-d < y1 && y1 < b+d) || (l-d < x2 && x2 < r+d && t-d < y2 && y2 < b+d))) {
- if(inst.snapElements[i].snapping) {
- (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
- }
- inst.snapElements[i].snapping = false;
- continue;
- }
-
- if(o.snapMode !== "inner") {
- ts = Math.abs(t - y2) <= d;
- bs = Math.abs(b - y1) <= d;
- ls = Math.abs(l - x2) <= d;
- rs = Math.abs(r - x1) <= d;
- if(ts) {
- ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
- }
- if(bs) {
- ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
- }
- if(ls) {
- ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
- }
- if(rs) {
- ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
- }
- }
-
- first = (ts || bs || ls || rs);
-
- if(o.snapMode !== "outer") {
- ts = Math.abs(t - y1) <= d;
- bs = Math.abs(b - y2) <= d;
- ls = Math.abs(l - x1) <= d;
- rs = Math.abs(r - x2) <= d;
- if(ts) {
- ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
- }
- if(bs) {
- ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
- }
- if(ls) {
- ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
- }
- if(rs) {
- ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
- }
- }
-
- if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
- (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
- }
- inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
-
- }
-
- }
-});
-
-$.ui.plugin.add("draggable", "stack", {
- start: function() {
- var min,
- o = this.data("ui-draggable").options,
- group = $.makeArray($(o.stack)).sort(function(a,b) {
- return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
- });
-
- if (!group.length) { return; }
-
- min = parseInt($(group[0]).css("zIndex"), 10) || 0;
- $(group).each(function(i) {
- $(this).css("zIndex", min + i);
- });
- this.css("zIndex", (min + group.length));
- }
-});
-
-$.ui.plugin.add("draggable", "zIndex", {
- start: function(event, ui) {
- var t = $(ui.helper), o = $(this).data("ui-draggable").options;
- if(t.css("zIndex")) {
- o._zIndex = t.css("zIndex");
- }
- t.css("zIndex", o.zIndex);
- },
- stop: function(event, ui) {
- var o = $(this).data("ui-draggable").options;
- if(o._zIndex) {
- $(ui.helper).css("zIndex", o._zIndex);
- }
- }
-});
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-function isOverAxis( x, reference, size ) {
- return ( x > reference ) && ( x < ( reference + size ) );
-}
-
-$.widget("ui.droppable", {
- version: "1.10.1",
- widgetEventPrefix: "drop",
- options: {
- accept: "*",
- activeClass: false,
- addClasses: true,
- greedy: false,
- hoverClass: false,
- scope: "default",
- tolerance: "intersect",
-
- // callbacks
- activate: null,
- deactivate: null,
- drop: null,
- out: null,
- over: null
- },
- _create: function() {
-
- var o = this.options,
- accept = o.accept;
-
- this.isover = false;
- this.isout = true;
-
- this.accept = $.isFunction(accept) ? accept : function(d) {
- return d.is(accept);
- };
-
- //Store the droppable's proportions
- this.proportions = { width: this.element[0].offsetWidth, height: this.element[0].offsetHeight };
-
- // Add the reference and positions to the manager
- $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
- $.ui.ddmanager.droppables[o.scope].push(this);
-
- (o.addClasses && this.element.addClass("ui-droppable"));
-
- },
-
- _destroy: function() {
- var i = 0,
- drop = $.ui.ddmanager.droppables[this.options.scope];
-
- for ( ; i < drop.length; i++ ) {
- if ( drop[i] === this ) {
- drop.splice(i, 1);
- }
- }
-
- this.element.removeClass("ui-droppable ui-droppable-disabled");
- },
-
- _setOption: function(key, value) {
-
- if(key === "accept") {
- this.accept = $.isFunction(value) ? value : function(d) {
- return d.is(value);
- };
- }
- $.Widget.prototype._setOption.apply(this, arguments);
- },
-
- _activate: function(event) {
- var draggable = $.ui.ddmanager.current;
- if(this.options.activeClass) {
- this.element.addClass(this.options.activeClass);
- }
- if(draggable){
- this._trigger("activate", event, this.ui(draggable));
- }
- },
-
- _deactivate: function(event) {
- var draggable = $.ui.ddmanager.current;
- if(this.options.activeClass) {
- this.element.removeClass(this.options.activeClass);
- }
- if(draggable){
- this._trigger("deactivate", event, this.ui(draggable));
- }
- },
-
- _over: function(event) {
-
- var draggable = $.ui.ddmanager.current;
-
- // Bail if draggable and droppable are same element
- if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
- return;
- }
-
- if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
- if(this.options.hoverClass) {
- this.element.addClass(this.options.hoverClass);
- }
- this._trigger("over", event, this.ui(draggable));
- }
-
- },
-
- _out: function(event) {
-
- var draggable = $.ui.ddmanager.current;
-
- // Bail if draggable and droppable are same element
- if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
- return;
- }
-
- if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
- if(this.options.hoverClass) {
- this.element.removeClass(this.options.hoverClass);
- }
- this._trigger("out", event, this.ui(draggable));
- }
-
- },
-
- _drop: function(event,custom) {
-
- var draggable = custom || $.ui.ddmanager.current,
- childrenIntersection = false;
-
- // Bail if draggable and droppable are same element
- if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
- return false;
- }
-
- this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
- var inst = $.data(this, "ui-droppable");
- if(
- inst.options.greedy &&
- !inst.options.disabled &&
- inst.options.scope === draggable.options.scope &&
- inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
- $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
- ) { childrenIntersection = true; return false; }
- });
- if(childrenIntersection) {
- return false;
- }
-
- if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
- if(this.options.activeClass) {
- this.element.removeClass(this.options.activeClass);
- }
- if(this.options.hoverClass) {
- this.element.removeClass(this.options.hoverClass);
- }
- this._trigger("drop", event, this.ui(draggable));
- return this.element;
- }
-
- return false;
-
- },
-
- ui: function(c) {
- return {
- draggable: (c.currentItem || c.element),
- helper: c.helper,
- position: c.position,
- offset: c.positionAbs
- };
- }
-
-});
-
-$.ui.intersect = function(draggable, droppable, toleranceMode) {
-
- if (!droppable.offset) {
- return false;
- }
-
- var draggableLeft, draggableTop,
- x1 = (draggable.positionAbs || draggable.position.absolute).left, x2 = x1 + draggable.helperProportions.width,
- y1 = (draggable.positionAbs || draggable.position.absolute).top, y2 = y1 + draggable.helperProportions.height,
- l = droppable.offset.left, r = l + droppable.proportions.width,
- t = droppable.offset.top, b = t + droppable.proportions.height;
-
- switch (toleranceMode) {
- case "fit":
- return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
- case "intersect":
- return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
- x2 - (draggable.helperProportions.width / 2) < r && // Left Half
- t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
- y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
- case "pointer":
- draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
- draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
- return isOverAxis( draggableTop, t, droppable.proportions.height ) && isOverAxis( draggableLeft, l, droppable.proportions.width );
- case "touch":
- return (
- (y1 >= t && y1 <= b) || // Top edge touching
- (y2 >= t && y2 <= b) || // Bottom edge touching
- (y1 < t && y2 > b) // Surrounded vertically
- ) && (
- (x1 >= l && x1 <= r) || // Left edge touching
- (x2 >= l && x2 <= r) || // Right edge touching
- (x1 < l && x2 > r) // Surrounded horizontally
- );
- default:
- return false;
- }
-
-};
-
-/*
- This manager tracks offsets of draggables and droppables
-*/
-$.ui.ddmanager = {
- current: null,
- droppables: { "default": [] },
- prepareOffsets: function(t, event) {
-
- var i, j,
- m = $.ui.ddmanager.droppables[t.options.scope] || [],
- type = event ? event.type : null, // workaround for #2317
- list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
-
- droppablesLoop: for (i = 0; i < m.length; i++) {
-
- //No disabled and non-accepted
- if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
- continue;
- }
-
- // Filter out elements in the current dragged item
- for (j=0; j < list.length; j++) {
- if(list[j] === m[i].element[0]) {
- m[i].proportions.height = 0;
- continue droppablesLoop;
- }
- }
-
- m[i].visible = m[i].element.css("display") !== "none";
- if(!m[i].visible) {
- continue;
- }
-
- //Activate the droppable if used directly from draggables
- if(type === "mousedown") {
- m[i]._activate.call(m[i], event);
- }
-
- m[i].offset = m[i].element.offset();
- m[i].proportions = { width: m[i].element[0].offsetWidth, height: m[i].element[0].offsetHeight };
-
- }
-
- },
- drop: function(draggable, event) {
-
- var dropped = false;
- $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
-
- if(!this.options) {
- return;
- }
- if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
- dropped = this._drop.call(this, event) || dropped;
- }
-
- if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
- this.isout = true;
- this.isover = false;
- this._deactivate.call(this, event);
- }
-
- });
- return dropped;
-
- },
- dragStart: function( draggable, event ) {
- //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
- draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
- if( !draggable.options.refreshPositions ) {
- $.ui.ddmanager.prepareOffsets( draggable, event );
- }
- });
- },
- drag: function(draggable, event) {
-
- //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
- if(draggable.options.refreshPositions) {
- $.ui.ddmanager.prepareOffsets(draggable, event);
- }
-
- //Run through all droppables and check their positions based on specific tolerance options
- $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
-
- if(this.options.disabled || this.greedyChild || !this.visible) {
- return;
- }
-
- var parentInstance, scope, parent,
- intersects = $.ui.intersect(draggable, this, this.options.tolerance),
- c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
- if(!c) {
- return;
- }
-
- if (this.options.greedy) {
- // find droppable parents with same scope
- scope = this.options.scope;
- parent = this.element.parents(":data(ui-droppable)").filter(function () {
- return $.data(this, "ui-droppable").options.scope === scope;
- });
-
- if (parent.length) {
- parentInstance = $.data(parent[0], "ui-droppable");
- parentInstance.greedyChild = (c === "isover");
- }
- }
-
- // we just moved into a greedy child
- if (parentInstance && c === "isover") {
- parentInstance.isover = false;
- parentInstance.isout = true;
- parentInstance._out.call(parentInstance, event);
- }
-
- this[c] = true;
- this[c === "isout" ? "isover" : "isout"] = false;
- this[c === "isover" ? "_over" : "_out"].call(this, event);
-
- // we just moved out of a greedy child
- if (parentInstance && c === "isout") {
- parentInstance.isout = false;
- parentInstance.isover = true;
- parentInstance._over.call(parentInstance, event);
- }
- });
-
- },
- dragStop: function( draggable, event ) {
- draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
- //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
- if( !draggable.options.refreshPositions ) {
- $.ui.ddmanager.prepareOffsets( draggable, event );
- }
- }
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-function num(v) {
- return parseInt(v, 10) || 0;
-}
-
-function isNumber(value) {
- return !isNaN(parseInt(value, 10));
-}
-
-$.widget("ui.resizable", $.ui.mouse, {
- version: "1.10.1",
- widgetEventPrefix: "resize",
- options: {
- alsoResize: false,
- animate: false,
- animateDuration: "slow",
- animateEasing: "swing",
- aspectRatio: false,
- autoHide: false,
- containment: false,
- ghost: false,
- grid: false,
- handles: "e,s,se",
- helper: false,
- maxHeight: null,
- maxWidth: null,
- minHeight: 10,
- minWidth: 10,
- // See #7960
- zIndex: 90,
-
- // callbacks
- resize: null,
- start: null,
- stop: null
- },
- _create: function() {
-
- var n, i, handle, axis, hname,
- that = this,
- o = this.options;
- this.element.addClass("ui-resizable");
-
- $.extend(this, {
- _aspectRatio: !!(o.aspectRatio),
- aspectRatio: o.aspectRatio,
- originalElement: this.element,
- _proportionallyResizeElements: [],
- _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
- });
-
- //Wrap the element if it cannot hold child nodes
- if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
-
- //Create a wrapper element and set the wrapper to the new current internal element
- this.element.wrap(
- $("<div class='ui-wrapper' style='overflow: hidden;'></div>").css({
- position: this.element.css("position"),
- width: this.element.outerWidth(),
- height: this.element.outerHeight(),
- top: this.element.css("top"),
- left: this.element.css("left")
- })
- );
-
- //Overwrite the original this.element
- this.element = this.element.parent().data(
- "ui-resizable", this.element.data("ui-resizable")
- );
-
- this.elementIsWrapper = true;
-
- //Move margins to the wrapper
- this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
- this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
-
- //Prevent Safari textarea resize
- this.originalResizeStyle = this.originalElement.css("resize");
- this.originalElement.css("resize", "none");
-
- //Push the actual element to our proportionallyResize internal array
- this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
-
- // avoid IE jump (hard set the margin)
- this.originalElement.css({ margin: this.originalElement.css("margin") });
-
- // fix handlers offset
- this._proportionallyResize();
-
- }
-
- this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
- if(this.handles.constructor === String) {
-
- if ( this.handles === "all") {
- this.handles = "n,e,s,w,se,sw,ne,nw";
- }
-
- n = this.handles.split(",");
- this.handles = {};
-
- for(i = 0; i < n.length; i++) {
-
- handle = $.trim(n[i]);
- hname = "ui-resizable-"+handle;
- axis = $("<div class='ui-resizable-handle " + hname + "'></div>");
-
- // Apply zIndex to all handles - see #7960
- axis.css({ zIndex: o.zIndex });
-
- //TODO : What's going on here?
- if ("se" === handle) {
- axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
- }
-
- //Insert into internal handles object and append to element
- this.handles[handle] = ".ui-resizable-"+handle;
- this.element.append(axis);
- }
-
- }
-
- this._renderAxis = function(target) {
-
- var i, axis, padPos, padWrapper;
-
- target = target || this.element;
-
- for(i in this.handles) {
-
- if(this.handles[i].constructor === String) {
- this.handles[i] = $(this.handles[i], this.element).show();
- }
-
- //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
- if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
-
- axis = $(this.handles[i], this.element);
-
- //Checking the correct pad and border
- padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
-
- //The padding type i have to apply...
- padPos = [ "padding",
- /ne|nw|n/.test(i) ? "Top" :
- /se|sw|s/.test(i) ? "Bottom" :
- /^e$/.test(i) ? "Right" : "Left" ].join("");
-
- target.css(padPos, padWrapper);
-
- this._proportionallyResize();
-
- }
-
- //TODO: What's that good for? There's not anything to be executed left
- if(!$(this.handles[i]).length) {
- continue;
- }
- }
- };
-
- //TODO: make renderAxis a prototype function
- this._renderAxis(this.element);
-
- this._handles = $(".ui-resizable-handle", this.element)
- .disableSelection();
-
- //Matching axis name
- this._handles.mouseover(function() {
- if (!that.resizing) {
- if (this.className) {
- axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
- }
- //Axis, default = se
- that.axis = axis && axis[1] ? axis[1] : "se";
- }
- });
-
- //If we want to auto hide the elements
- if (o.autoHide) {
- this._handles.hide();
- $(this.element)
- .addClass("ui-resizable-autohide")
- .mouseenter(function() {
- if (o.disabled) {
- return;
- }
- $(this).removeClass("ui-resizable-autohide");
- that._handles.show();
- })
- .mouseleave(function(){
- if (o.disabled) {
- return;
- }
- if (!that.resizing) {
- $(this).addClass("ui-resizable-autohide");
- that._handles.hide();
- }
- });
- }
-
- //Initialize the mouse interaction
- this._mouseInit();
-
- },
-
- _destroy: function() {
-
- this._mouseDestroy();
-
- var wrapper,
- _destroy = function(exp) {
- $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
- .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
- };
-
- //TODO: Unwrap at same DOM position
- if (this.elementIsWrapper) {
- _destroy(this.element);
- wrapper = this.element;
- this.originalElement.css({
- position: wrapper.css("position"),
- width: wrapper.outerWidth(),
- height: wrapper.outerHeight(),
- top: wrapper.css("top"),
- left: wrapper.css("left")
- }).insertAfter( wrapper );
- wrapper.remove();
- }
-
- this.originalElement.css("resize", this.originalResizeStyle);
- _destroy(this.originalElement);
-
- return this;
- },
-
- _mouseCapture: function(event) {
- var i, handle,
- capture = false;
-
- for (i in this.handles) {
- handle = $(this.handles[i])[0];
- if (handle === event.target || $.contains(handle, event.target)) {
- capture = true;
- }
- }
-
- return !this.options.disabled && capture;
- },
-
- _mouseStart: function(event) {
-
- var curleft, curtop, cursor,
- o = this.options,
- iniPos = this.element.position(),
- el = this.element;
-
- this.resizing = true;
-
- // bugfix for http://dev.jquery.com/ticket/1749
- if ( (/absolute/).test( el.css("position") ) ) {
- el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
- } else if (el.is(".ui-draggable")) {
- el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
- }
-
- this._renderProxy();
-
- curleft = num(this.helper.css("left"));
- curtop = num(this.helper.css("top"));
-
- if (o.containment) {
- curleft += $(o.containment).scrollLeft() || 0;
- curtop += $(o.containment).scrollTop() || 0;
- }
-
- //Store needed variables
- this.offset = this.helper.offset();
- this.position = { left: curleft, top: curtop };
- this.size = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
- this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
- this.originalPosition = { left: curleft, top: curtop };
- this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
- this.originalMousePosition = { left: event.pageX, top: event.pageY };
-
- //Aspect Ratio
- this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
-
- cursor = $(".ui-resizable-" + this.axis).css("cursor");
- $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
-
- el.addClass("ui-resizable-resizing");
- this._propagate("start", event);
- return true;
- },
-
- _mouseDrag: function(event) {
-
- //Increase performance, avoid regex
- var data,
- el = this.helper, props = {},
- smp = this.originalMousePosition,
- a = this.axis,
- prevTop = this.position.top,
- prevLeft = this.position.left,
- prevWidth = this.size.width,
- prevHeight = this.size.height,
- dx = (event.pageX-smp.left)||0,
- dy = (event.pageY-smp.top)||0,
- trigger = this._change[a];
-
- if (!trigger) {
- return false;
- }
-
- // Calculate the attrs that will be change
- data = trigger.apply(this, [event, dx, dy]);
-
- // Put this in the mouseDrag handler since the user can start pressing shift while resizing
- this._updateVirtualBoundaries(event.shiftKey);
- if (this._aspectRatio || event.shiftKey) {
- data = this._updateRatio(data, event);
- }
-
- data = this._respectSize(data, event);
-
- this._updateCache(data);
-
- // plugins callbacks need to be called first
- this._propagate("resize", event);
-
- if (this.position.top !== prevTop) {
- props.top = this.position.top + "px";
- }
- if (this.position.left !== prevLeft) {
- props.left = this.position.left + "px";
- }
- if (this.size.width !== prevWidth) {
- props.width = this.size.width + "px";
- }
- if (this.size.height !== prevHeight) {
- props.height = this.size.height + "px";
- }
- el.css(props);
-
- if (!this._helper && this._proportionallyResizeElements.length) {
- this._proportionallyResize();
- }
-
- // Call the user callback if the element was resized
- if ( ! $.isEmptyObject(props) ) {
- this._trigger("resize", event, this.ui());
- }
-
- return false;
- },
-
- _mouseStop: function(event) {
-
- this.resizing = false;
- var pr, ista, soffseth, soffsetw, s, left, top,
- o = this.options, that = this;
-
- if(this._helper) {
-
- pr = this._proportionallyResizeElements;
- ista = pr.length && (/textarea/i).test(pr[0].nodeName);
- soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
- soffsetw = ista ? 0 : that.sizeDiff.width;
-
- s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
- left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
- top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
-
- if (!o.animate) {
- this.element.css($.extend(s, { top: top, left: left }));
- }
-
- that.helper.height(that.size.height);
- that.helper.width(that.size.width);
-
- if (this._helper && !o.animate) {
- this._proportionallyResize();
- }
- }
-
- $("body").css("cursor", "auto");
-
- this.element.removeClass("ui-resizable-resizing");
-
- this._propagate("stop", event);
-
- if (this._helper) {
- this.helper.remove();
- }
-
- return false;
-
- },
-
- _updateVirtualBoundaries: function(forceAspectRatio) {
- var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
- o = this.options;
-
- b = {
- minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
- maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
- minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
- maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
- };
-
- if(this._aspectRatio || forceAspectRatio) {
- // We want to create an enclosing box whose aspect ration is the requested one
- // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
- pMinWidth = b.minHeight * this.aspectRatio;
- pMinHeight = b.minWidth / this.aspectRatio;
- pMaxWidth = b.maxHeight * this.aspectRatio;
- pMaxHeight = b.maxWidth / this.aspectRatio;
-
- if(pMinWidth > b.minWidth) {
- b.minWidth = pMinWidth;
- }
- if(pMinHeight > b.minHeight) {
- b.minHeight = pMinHeight;
- }
- if(pMaxWidth < b.maxWidth) {
- b.maxWidth = pMaxWidth;
- }
- if(pMaxHeight < b.maxHeight) {
- b.maxHeight = pMaxHeight;
- }
- }
- this._vBoundaries = b;
- },
-
- _updateCache: function(data) {
- this.offset = this.helper.offset();
- if (isNumber(data.left)) {
- this.position.left = data.left;
- }
- if (isNumber(data.top)) {
- this.position.top = data.top;
- }
- if (isNumber(data.height)) {
- this.size.height = data.height;
- }
- if (isNumber(data.width)) {
- this.size.width = data.width;
- }
- },
-
- _updateRatio: function( data ) {
-
- var cpos = this.position,
- csize = this.size,
- a = this.axis;
-
- if (isNumber(data.height)) {
- data.width = (data.height * this.aspectRatio);
- } else if (isNumber(data.width)) {
- data.height = (data.width / this.aspectRatio);
- }
-
- if (a === "sw") {
- data.left = cpos.left + (csize.width - data.width);
- data.top = null;
- }
- if (a === "nw") {
- data.top = cpos.top + (csize.height - data.height);
- data.left = cpos.left + (csize.width - data.width);
- }
-
- return data;
- },
-
- _respectSize: function( data ) {
-
- var o = this._vBoundaries,
- a = this.axis,
- ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
- isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
- dw = this.originalPosition.left + this.originalSize.width,
- dh = this.position.top + this.size.height,
- cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
- if (isminw) {
- data.width = o.minWidth;
- }
- if (isminh) {
- data.height = o.minHeight;
- }
- if (ismaxw) {
- data.width = o.maxWidth;
- }
- if (ismaxh) {
- data.height = o.maxHeight;
- }
-
- if (isminw && cw) {
- data.left = dw - o.minWidth;
- }
- if (ismaxw && cw) {
- data.left = dw - o.maxWidth;
- }
- if (isminh && ch) {
- data.top = dh - o.minHeight;
- }
- if (ismaxh && ch) {
- data.top = dh - o.maxHeight;
- }
-
- // fixing jump error on top/left - bug #2330
- if (!data.width && !data.height && !data.left && data.top) {
- data.top = null;
- } else if (!data.width && !data.height && !data.top && data.left) {
- data.left = null;
- }
-
- return data;
- },
-
- _proportionallyResize: function() {
-
- if (!this._proportionallyResizeElements.length) {
- return;
- }
-
- var i, j, borders, paddings, prel,
- element = this.helper || this.element;
-
- for ( i=0; i < this._proportionallyResizeElements.length; i++) {
-
- prel = this._proportionallyResizeElements[i];
-
- if (!this.borderDif) {
- this.borderDif = [];
- borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
- paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
-
- for ( j = 0; j < borders.length; j++ ) {
- this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
- }
- }
-
- prel.css({
- height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
- width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
- });
-
- }
-
- },
-
- _renderProxy: function() {
-
- var el = this.element, o = this.options;
- this.elementOffset = el.offset();
-
- if(this._helper) {
-
- this.helper = this.helper || $("<div style='overflow:hidden;'></div>");
-
- this.helper.addClass(this._helper).css({
- width: this.element.outerWidth() - 1,
- height: this.element.outerHeight() - 1,
- position: "absolute",
- left: this.elementOffset.left +"px",
- top: this.elementOffset.top +"px",
- zIndex: ++o.zIndex //TODO: Don't modify option
- });
-
- this.helper
- .appendTo("body")
- .disableSelection();
-
- } else {
- this.helper = this.element;
- }
-
- },
-
- _change: {
- e: function(event, dx) {
- return { width: this.originalSize.width + dx };
- },
- w: function(event, dx) {
- var cs = this.originalSize, sp = this.originalPosition;
- return { left: sp.left + dx, width: cs.width - dx };
- },
- n: function(event, dx, dy) {
- var cs = this.originalSize, sp = this.originalPosition;
- return { top: sp.top + dy, height: cs.height - dy };
- },
- s: function(event, dx, dy) {
- return { height: this.originalSize.height + dy };
- },
- se: function(event, dx, dy) {
- return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
- },
- sw: function(event, dx, dy) {
- return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
- },
- ne: function(event, dx, dy) {
- return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
- },
- nw: function(event, dx, dy) {
- return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
- }
- },
-
- _propagate: function(n, event) {
- $.ui.plugin.call(this, n, [event, this.ui()]);
- (n !== "resize" && this._trigger(n, event, this.ui()));
- },
-
- plugins: {},
-
- ui: function() {
- return {
- originalElement: this.originalElement,
- element: this.element,
- helper: this.helper,
- position: this.position,
- size: this.size,
- originalSize: this.originalSize,
- originalPosition: this.originalPosition
- };
- }
-
-});
-
-/*
- * Resizable Extensions
- */
-
-$.ui.plugin.add("resizable", "animate", {
-
- stop: function( event ) {
- var that = $(this).data("ui-resizable"),
- o = that.options,
- pr = that._proportionallyResizeElements,
- ista = pr.length && (/textarea/i).test(pr[0].nodeName),
- soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
- soffsetw = ista ? 0 : that.sizeDiff.width,
- style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
- left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
- top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
-
- that.element.animate(
- $.extend(style, top && left ? { top: top, left: left } : {}), {
- duration: o.animateDuration,
- easing: o.animateEasing,
- step: function() {
-
- var data = {
- width: parseInt(that.element.css("width"), 10),
- height: parseInt(that.element.css("height"), 10),
- top: parseInt(that.element.css("top"), 10),
- left: parseInt(that.element.css("left"), 10)
- };
-
- if (pr && pr.length) {
- $(pr[0]).css({ width: data.width, height: data.height });
- }
-
- // propagating resize, and updating values for each animation step
- that._updateCache(data);
- that._propagate("resize", event);
-
- }
- }
- );
- }
-
-});
-
-$.ui.plugin.add("resizable", "containment", {
-
- start: function() {
- var element, p, co, ch, cw, width, height,
- that = $(this).data("ui-resizable"),
- o = that.options,
- el = that.element,
- oc = o.containment,
- ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
-
- if (!ce) {
- return;
- }
-
- that.containerElement = $(ce);
-
- if (/document/.test(oc) || oc === document) {
- that.containerOffset = { left: 0, top: 0 };
- that.containerPosition = { left: 0, top: 0 };
-
- that.parentData = {
- element: $(document), left: 0, top: 0,
- width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
- };
- }
-
- // i'm a node, so compute top, left, right, bottom
- else {
- element = $(ce);
- p = [];
- $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
-
- that.containerOffset = element.offset();
- that.containerPosition = element.position();
- that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
-
- co = that.containerOffset;
- ch = that.containerSize.height;
- cw = that.containerSize.width;
- width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
- height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
-
- that.parentData = {
- element: ce, left: co.left, top: co.top, width: width, height: height
- };
- }
- },
-
- resize: function( event ) {
- var woset, hoset, isParent, isOffsetRelative,
- that = $(this).data("ui-resizable"),
- o = that.options,
- co = that.containerOffset, cp = that.position,
- pRatio = that._aspectRatio || event.shiftKey,
- cop = { top:0, left:0 }, ce = that.containerElement;
-
- if (ce[0] !== document && (/static/).test(ce.css("position"))) {
- cop = co;
- }
-
- if (cp.left < (that._helper ? co.left : 0)) {
- that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
- if (pRatio) {
- that.size.height = that.size.width / that.aspectRatio;
- }
- that.position.left = o.helper ? co.left : 0;
- }
-
- if (cp.top < (that._helper ? co.top : 0)) {
- that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
- if (pRatio) {
- that.size.width = that.size.height * that.aspectRatio;
- }
- that.position.top = that._helper ? co.top : 0;
- }
-
- that.offset.left = that.parentData.left+that.position.left;
- that.offset.top = that.parentData.top+that.position.top;
-
- woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
- hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
-
- isParent = that.containerElement.get(0) === that.element.parent().get(0);
- isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
-
- if(isParent && isOffsetRelative) {
- woset -= that.parentData.left;
- }
-
- if (woset + that.size.width >= that.parentData.width) {
- that.size.width = that.parentData.width - woset;
- if (pRatio) {
- that.size.height = that.size.width / that.aspectRatio;
- }
- }
-
- if (hoset + that.size.height >= that.parentData.height) {
- that.size.height = that.parentData.height - hoset;
- if (pRatio) {
- that.size.width = that.size.height * that.aspectRatio;
- }
- }
- },
-
- stop: function(){
- var that = $(this).data("ui-resizable"),
- o = that.options,
- co = that.containerOffset,
- cop = that.containerPosition,
- ce = that.containerElement,
- helper = $(that.helper),
- ho = helper.offset(),
- w = helper.outerWidth() - that.sizeDiff.width,
- h = helper.outerHeight() - that.sizeDiff.height;
-
- if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
- $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
- }
-
- if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
- $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
- }
-
- }
-});
-
-$.ui.plugin.add("resizable", "alsoResize", {
-
- start: function () {
- var that = $(this).data("ui-resizable"),
- o = that.options,
- _store = function (exp) {
- $(exp).each(function() {
- var el = $(this);
- el.data("ui-resizable-alsoresize", {
- width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
- left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
- });
- });
- };
-
- if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
- if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
- else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
- }else{
- _store(o.alsoResize);
- }
- },
-
- resize: function (event, ui) {
- var that = $(this).data("ui-resizable"),
- o = that.options,
- os = that.originalSize,
- op = that.originalPosition,
- delta = {
- height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
- top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
- },
-
- _alsoResize = function (exp, c) {
- $(exp).each(function() {
- var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
- css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
-
- $.each(css, function (i, prop) {
- var sum = (start[prop]||0) + (delta[prop]||0);
- if (sum && sum >= 0) {
- style[prop] = sum || null;
- }
- });
-
- el.css(style);
- });
- };
-
- if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
- $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
- }else{
- _alsoResize(o.alsoResize);
- }
- },
-
- stop: function () {
- $(this).removeData("resizable-alsoresize");
- }
-});
-
-$.ui.plugin.add("resizable", "ghost", {
-
- start: function() {
-
- var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
-
- that.ghost = that.originalElement.clone();
- that.ghost
- .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
- .addClass("ui-resizable-ghost")
- .addClass(typeof o.ghost === "string" ? o.ghost : "");
-
- that.ghost.appendTo(that.helper);
-
- },
-
- resize: function(){
- var that = $(this).data("ui-resizable");
- if (that.ghost) {
- that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
- }
- },
-
- stop: function() {
- var that = $(this).data("ui-resizable");
- if (that.ghost && that.helper) {
- that.helper.get(0).removeChild(that.ghost.get(0));
- }
- }
-
-});
-
-$.ui.plugin.add("resizable", "grid", {
-
- resize: function() {
- var that = $(this).data("ui-resizable"),
- o = that.options,
- cs = that.size,
- os = that.originalSize,
- op = that.originalPosition,
- a = that.axis,
- grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
- gridX = (grid[0]||1),
- gridY = (grid[1]||1),
- ox = Math.round((cs.width - os.width) / gridX) * gridX,
- oy = Math.round((cs.height - os.height) / gridY) * gridY,
- newWidth = os.width + ox,
- newHeight = os.height + oy,
- isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
- isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
- isMinWidth = o.minWidth && (o.minWidth > newWidth),
- isMinHeight = o.minHeight && (o.minHeight > newHeight);
-
- o.grid = grid;
-
- if (isMinWidth) {
- newWidth = newWidth + gridX;
- }
- if (isMinHeight) {
- newHeight = newHeight + gridY;
- }
- if (isMaxWidth) {
- newWidth = newWidth - gridX;
- }
- if (isMaxHeight) {
- newHeight = newHeight - gridY;
- }
-
- if (/^(se|s|e)$/.test(a)) {
- that.size.width = newWidth;
- that.size.height = newHeight;
- } else if (/^(ne)$/.test(a)) {
- that.size.width = newWidth;
- that.size.height = newHeight;
- that.position.top = op.top - oy;
- } else if (/^(sw)$/.test(a)) {
- that.size.width = newWidth;
- that.size.height = newHeight;
- that.position.left = op.left - ox;
- } else {
- that.size.width = newWidth;
- that.size.height = newHeight;
- that.position.top = op.top - oy;
- that.position.left = op.left - ox;
- }
- }
-
-});
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.widget("ui.selectable", $.ui.mouse, {
- version: "1.10.1",
- options: {
- appendTo: "body",
- autoRefresh: true,
- distance: 0,
- filter: "*",
- tolerance: "touch",
-
- // callbacks
- selected: null,
- selecting: null,
- start: null,
- stop: null,
- unselected: null,
- unselecting: null
- },
- _create: function() {
- var selectees,
- that = this;
-
- this.element.addClass("ui-selectable");
-
- this.dragged = false;
-
- // cache selectee children based on filter
- this.refresh = function() {
- selectees = $(that.options.filter, that.element[0]);
- selectees.addClass("ui-selectee");
- selectees.each(function() {
- var $this = $(this),
- pos = $this.offset();
- $.data(this, "selectable-item", {
- element: this,
- $element: $this,
- left: pos.left,
- top: pos.top,
- right: pos.left + $this.outerWidth(),
- bottom: pos.top + $this.outerHeight(),
- startselected: false,
- selected: $this.hasClass("ui-selected"),
- selecting: $this.hasClass("ui-selecting"),
- unselecting: $this.hasClass("ui-unselecting")
- });
- });
- };
- this.refresh();
-
- this.selectees = selectees.addClass("ui-selectee");
-
- this._mouseInit();
-
- this.helper = $("<div class='ui-selectable-helper'></div>");
- },
-
- _destroy: function() {
- this.selectees
- .removeClass("ui-selectee")
- .removeData("selectable-item");
- this.element
- .removeClass("ui-selectable ui-selectable-disabled");
- this._mouseDestroy();
- },
-
- _mouseStart: function(event) {
- var that = this,
- options = this.options;
-
- this.opos = [event.pageX, event.pageY];
-
- if (this.options.disabled) {
- return;
- }
-
- this.selectees = $(options.filter, this.element[0]);
-
- this._trigger("start", event);
-
- $(options.appendTo).append(this.helper);
- // position helper (lasso)
- this.helper.css({
- "left": event.pageX,
- "top": event.pageY,
- "width": 0,
- "height": 0
- });
-
- if (options.autoRefresh) {
- this.refresh();
- }
-
- this.selectees.filter(".ui-selected").each(function() {
- var selectee = $.data(this, "selectable-item");
- selectee.startselected = true;
- if (!event.metaKey && !event.ctrlKey) {
- selectee.$element.removeClass("ui-selected");
- selectee.selected = false;
- selectee.$element.addClass("ui-unselecting");
- selectee.unselecting = true;
- // selectable UNSELECTING callback
- that._trigger("unselecting", event, {
- unselecting: selectee.element
- });
- }
- });
-
- $(event.target).parents().addBack().each(function() {
- var doSelect,
- selectee = $.data(this, "selectable-item");
- if (selectee) {
- doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
- selectee.$element
- .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
- .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
- selectee.unselecting = !doSelect;
- selectee.selecting = doSelect;
- selectee.selected = doSelect;
- // selectable (UN)SELECTING callback
- if (doSelect) {
- that._trigger("selecting", event, {
- selecting: selectee.element
- });
- } else {
- that._trigger("unselecting", event, {
- unselecting: selectee.element
- });
- }
- return false;
- }
- });
-
- },
-
- _mouseDrag: function(event) {
-
- this.dragged = true;
-
- if (this.options.disabled) {
- return;
- }
-
- var tmp,
- that = this,
- options = this.options,
- x1 = this.opos[0],
- y1 = this.opos[1],
- x2 = event.pageX,
- y2 = event.pageY;
-
- if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
- if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
- this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
-
- this.selectees.each(function() {
- var selectee = $.data(this, "selectable-item"),
- hit = false;
-
- //prevent helper from being selected if appendTo: selectable
- if (!selectee || selectee.element === that.element[0]) {
- return;
- }
-
- if (options.tolerance === "touch") {
- hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
- } else if (options.tolerance === "fit") {
- hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
- }
-
- if (hit) {
- // SELECT
- if (selectee.selected) {
- selectee.$element.removeClass("ui-selected");
- selectee.selected = false;
- }
- if (selectee.unselecting) {
- selectee.$element.removeClass("ui-unselecting");
- selectee.unselecting = false;
- }
- if (!selectee.selecting) {
- selectee.$element.addClass("ui-selecting");
- selectee.selecting = true;
- // selectable SELECTING callback
- that._trigger("selecting", event, {
- selecting: selectee.element
- });
- }
- } else {
- // UNSELECT
- if (selectee.selecting) {
- if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
- selectee.$element.removeClass("ui-selecting");
- selectee.selecting = false;
- selectee.$element.addClass("ui-selected");
- selectee.selected = true;
- } else {
- selectee.$element.removeClass("ui-selecting");
- selectee.selecting = false;
- if (selectee.startselected) {
- selectee.$element.addClass("ui-unselecting");
- selectee.unselecting = true;
- }
- // selectable UNSELECTING callback
- that._trigger("unselecting", event, {
- unselecting: selectee.element
- });
- }
- }
- if (selectee.selected) {
- if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
- selectee.$element.removeClass("ui-selected");
- selectee.selected = false;
-
- selectee.$element.addClass("ui-unselecting");
- selectee.unselecting = true;
- // selectable UNSELECTING callback
- that._trigger("unselecting", event, {
- unselecting: selectee.element
- });
- }
- }
- }
- });
-
- return false;
- },
-
- _mouseStop: function(event) {
- var that = this;
-
- this.dragged = false;
-
- $(".ui-unselecting", this.element[0]).each(function() {
- var selectee = $.data(this, "selectable-item");
- selectee.$element.removeClass("ui-unselecting");
- selectee.unselecting = false;
- selectee.startselected = false;
- that._trigger("unselected", event, {
- unselected: selectee.element
- });
- });
- $(".ui-selecting", this.element[0]).each(function() {
- var selectee = $.data(this, "selectable-item");
- selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
- selectee.selecting = false;
- selectee.selected = true;
- selectee.startselected = true;
- that._trigger("selected", event, {
- selected: selectee.element
- });
- });
- this._trigger("stop", event);
-
- this.helper.remove();
-
- return false;
- }
-
-});
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-/*jshint loopfunc: true */
-
-function isOverAxis( x, reference, size ) {
- return ( x > reference ) && ( x < ( reference + size ) );
-}
-
-$.widget("ui.sortable", $.ui.mouse, {
- version: "1.10.1",
- widgetEventPrefix: "sort",
- ready: false,
- options: {
- appendTo: "parent",
- axis: false,
- connectWith: false,
- containment: false,
- cursor: "auto",
- cursorAt: false,
- dropOnEmpty: true,
- forcePlaceholderSize: false,
- forceHelperSize: false,
- grid: false,
- handle: false,
- helper: "original",
- items: "> *",
- opacity: false,
- placeholder: false,
- revert: false,
- scroll: true,
- scrollSensitivity: 20,
- scrollSpeed: 20,
- scope: "default",
- tolerance: "intersect",
- zIndex: 1000,
-
- // callbacks
- activate: null,
- beforeStop: null,
- change: null,
- deactivate: null,
- out: null,
- over: null,
- receive: null,
- remove: null,
- sort: null,
- start: null,
- stop: null,
- update: null
- },
- _create: function() {
-
- var o = this.options;
- this.containerCache = {};
- this.element.addClass("ui-sortable");
-
- //Get the items
- this.refresh();
-
- //Let's determine if the items are being displayed horizontally
- this.floating = this.items.length ? o.axis === "x" || (/left|right/).test(this.items[0].item.css("float")) || (/inline|table-cell/).test(this.items[0].item.css("display")) : false;
-
- //Let's determine the parent's offset
- this.offset = this.element.offset();
-
- //Initialize mouse events for interaction
- this._mouseInit();
-
- //We're ready to go
- this.ready = true;
-
- },
-
- _destroy: function() {
- this.element
- .removeClass("ui-sortable ui-sortable-disabled");
- this._mouseDestroy();
-
- for ( var i = this.items.length - 1; i >= 0; i-- ) {
- this.items[i].item.removeData(this.widgetName + "-item");
- }
-
- return this;
- },
-
- _setOption: function(key, value){
- if ( key === "disabled" ) {
- this.options[ key ] = value;
-
- this.widget().toggleClass( "ui-sortable-disabled", !!value );
- } else {
- // Don't call widget base _setOption for disable as it adds ui-state-disabled class
- $.Widget.prototype._setOption.apply(this, arguments);
- }
- },
-
- _mouseCapture: function(event, overrideHandle) {
- var currentItem = null,
- validHandle = false,
- that = this;
-
- if (this.reverting) {
- return false;
- }
-
- if(this.options.disabled || this.options.type === "static") {
- return false;
- }
-
- //We have to refresh the items data once first
- this._refreshItems(event);
-
- //Find out if the clicked node (or one of its parents) is a actual item in this.items
- $(event.target).parents().each(function() {
- if($.data(this, that.widgetName + "-item") === that) {
- currentItem = $(this);
- return false;
- }
- });
- if($.data(event.target, that.widgetName + "-item") === that) {
- currentItem = $(event.target);
- }
-
- if(!currentItem) {
- return false;
- }
- if(this.options.handle && !overrideHandle) {
- $(this.options.handle, currentItem).find("*").addBack().each(function() {
- if(this === event.target) {
- validHandle = true;
- }
- });
- if(!validHandle) {
- return false;
- }
- }
-
- this.currentItem = currentItem;
- this._removeCurrentsFromItems();
- return true;
-
- },
-
- _mouseStart: function(event, overrideHandle, noActivation) {
-
- var i,
- o = this.options;
-
- this.currentContainer = this;
-
- //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
- this.refreshPositions();
-
- //Create and append the visible helper
- this.helper = this._createHelper(event);
-
- //Cache the helper size
- this._cacheHelperProportions();
-
- /*
- * - Position generation -
- * This block generates everything position related - it's the core of draggables.
- */
-
- //Cache the margins of the original element
- this._cacheMargins();
-
- //Get the next scrolling parent
- this.scrollParent = this.helper.scrollParent();
-
- //The element's absolute position on the page minus margins
- this.offset = this.currentItem.offset();
- this.offset = {
- top: this.offset.top - this.margins.top,
- left: this.offset.left - this.margins.left
- };
-
- $.extend(this.offset, {
- click: { //Where the click happened, relative to the element
- left: event.pageX - this.offset.left,
- top: event.pageY - this.offset.top
- },
- parent: this._getParentOffset(),
- relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
- });
-
- // Only after we got the offset, we can change the helper's position to absolute
- // TODO: Still need to figure out a way to make relative sorting possible
- this.helper.css("position", "absolute");
- this.cssPosition = this.helper.css("position");
-
- //Generate the original position
- this.originalPosition = this._generatePosition(event);
- this.originalPageX = event.pageX;
- this.originalPageY = event.pageY;
-
- //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
- (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
-
- //Cache the former DOM position
- this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
-
- //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
- if(this.helper[0] !== this.currentItem[0]) {
- this.currentItem.hide();
- }
-
- //Create the placeholder
- this._createPlaceholder();
-
- //Set a containment if given in the options
- if(o.containment) {
- this._setContainment();
- }
-
- if(o.cursor) { // cursor option
- if ($("body").css("cursor")) {
- this._storedCursor = $("body").css("cursor");
- }
- $("body").css("cursor", o.cursor);
- }
-
- if(o.opacity) { // opacity option
- if (this.helper.css("opacity")) {
- this._storedOpacity = this.helper.css("opacity");
- }
- this.helper.css("opacity", o.opacity);
- }
-
- if(o.zIndex) { // zIndex option
- if (this.helper.css("zIndex")) {
- this._storedZIndex = this.helper.css("zIndex");
- }
- this.helper.css("zIndex", o.zIndex);
- }
-
- //Prepare scrolling
- if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
- this.overflowOffset = this.scrollParent.offset();
- }
-
- //Call callbacks
- this._trigger("start", event, this._uiHash());
-
- //Recache the helper size
- if(!this._preserveHelperProportions) {
- this._cacheHelperProportions();
- }
-
-
- //Post "activate" events to possible containers
- if( !noActivation ) {
- for ( i = this.containers.length - 1; i >= 0; i-- ) {
- this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
- }
- }
-
- //Prepare possible droppables
- if($.ui.ddmanager) {
- $.ui.ddmanager.current = this;
- }
-
- if ($.ui.ddmanager && !o.dropBehaviour) {
- $.ui.ddmanager.prepareOffsets(this, event);
- }
-
- this.dragging = true;
-
- this.helper.addClass("ui-sortable-helper");
- this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
- return true;
-
- },
-
- _mouseDrag: function(event) {
- var i, item, itemElement, intersection,
- o = this.options,
- scrolled = false;
-
- //Compute the helpers position
- this.position = this._generatePosition(event);
- this.positionAbs = this._convertPositionTo("absolute");
-
- if (!this.lastPositionAbs) {
- this.lastPositionAbs = this.positionAbs;
- }
-
- //Do scrolling
- if(this.options.scroll) {
- if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
-
- if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
- this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
- } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
- this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
- }
-
- if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
- this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
- } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
- this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
- }
-
- } else {
-
- if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
- scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
- } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
- scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
- }
-
- if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
- scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
- } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
- scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
- }
-
- }
-
- if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
- $.ui.ddmanager.prepareOffsets(this, event);
- }
- }
-
- //Regenerate the absolute position used for position checks
- this.positionAbs = this._convertPositionTo("absolute");
-
- //Set the helper position
- if(!this.options.axis || this.options.axis !== "y") {
- this.helper[0].style.left = this.position.left+"px";
- }
- if(!this.options.axis || this.options.axis !== "x") {
- this.helper[0].style.top = this.position.top+"px";
- }
-
- //Rearrange
- for (i = this.items.length - 1; i >= 0; i--) {
-
- //Cache variables and intersection, continue if no intersection
- item = this.items[i];
- itemElement = item.item[0];
- intersection = this._intersectsWithPointer(item);
- if (!intersection) {
- continue;
- }
-
- // Only put the placeholder inside the current Container, skip all
- // items form other containers. This works because when moving
- // an item from one container to another the
- // currentContainer is switched before the placeholder is moved.
- //
- // Without this moving items in "sub-sortables" can cause the placeholder to jitter
- // beetween the outer and inner container.
- if (item.instance !== this.currentContainer) {
- continue;
- }
-
- // cannot intersect with itself
- // no useless actions that have been done before
- // no action if the item moved is the parent of the item checked
- if (itemElement !== this.currentItem[0] &&
- this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
- !$.contains(this.placeholder[0], itemElement) &&
- (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
- ) {
-
- this.direction = intersection === 1 ? "down" : "up";
-
- if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
- this._rearrange(event, item);
- } else {
- break;
- }
-
- this._trigger("change", event, this._uiHash());
- break;
- }
- }
-
- //Post events to containers
- this._contactContainers(event);
-
- //Interconnect with droppables
- if($.ui.ddmanager) {
- $.ui.ddmanager.drag(this, event);
- }
-
- //Call callbacks
- this._trigger("sort", event, this._uiHash());
-
- this.lastPositionAbs = this.positionAbs;
- return false;
-
- },
-
- _mouseStop: function(event, noPropagation) {
-
- if(!event) {
- return;
- }
-
- //If we are using droppables, inform the manager about the drop
- if ($.ui.ddmanager && !this.options.dropBehaviour) {
- $.ui.ddmanager.drop(this, event);
- }
-
- if(this.options.revert) {
- var that = this,
- cur = this.placeholder.offset();
-
- this.reverting = true;
-
- $(this.helper).animate({
- left: cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft),
- top: cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop)
- }, parseInt(this.options.revert, 10) || 500, function() {
- that._clear(event);
- });
- } else {
- this._clear(event, noPropagation);
- }
-
- return false;
-
- },
-
- cancel: function() {
-
- if(this.dragging) {
-
- this._mouseUp({ target: null });
-
- if(this.options.helper === "original") {
- this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
- } else {
- this.currentItem.show();
- }
-
- //Post deactivating events to containers
- for (var i = this.containers.length - 1; i >= 0; i--){
- this.containers[i]._trigger("deactivate", null, this._uiHash(this));
- if(this.containers[i].containerCache.over) {
- this.containers[i]._trigger("out", null, this._uiHash(this));
- this.containers[i].containerCache.over = 0;
- }
- }
-
- }
-
- if (this.placeholder) {
- //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
- if(this.placeholder[0].parentNode) {
- this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
- }
- if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
- this.helper.remove();
- }
-
- $.extend(this, {
- helper: null,
- dragging: false,
- reverting: false,
- _noFinalSort: null
- });
-
- if(this.domPosition.prev) {
- $(this.domPosition.prev).after(this.currentItem);
- } else {
- $(this.domPosition.parent).prepend(this.currentItem);
- }
- }
-
- return this;
-
- },
-
- serialize: function(o) {
-
- var items = this._getItemsAsjQuery(o && o.connected),
- str = [];
- o = o || {};
-
- $(items).each(function() {
- var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
- if (res) {
- str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
- }
- });
-
- if(!str.length && o.key) {
- str.push(o.key + "=");
- }
-
- return str.join("&");
-
- },
-
- toArray: function(o) {
-
- var items = this._getItemsAsjQuery(o && o.connected),
- ret = [];
-
- o = o || {};
-
- items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
- return ret;
-
- },
-
- /* Be careful with the following core functions */
- _intersectsWith: function(item) {
-
- var x1 = this.positionAbs.left,
- x2 = x1 + this.helperProportions.width,
- y1 = this.positionAbs.top,
- y2 = y1 + this.helperProportions.height,
- l = item.left,
- r = l + item.width,
- t = item.top,
- b = t + item.height,
- dyClick = this.offset.click.top,
- dxClick = this.offset.click.left,
- isOverElement = (y1 + dyClick) > t && (y1 + dyClick) < b && (x1 + dxClick) > l && (x1 + dxClick) < r;
-
- if ( this.options.tolerance === "pointer" ||
- this.options.forcePointerForContainers ||
- (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
- ) {
- return isOverElement;
- } else {
-
- return (l < x1 + (this.helperProportions.width / 2) && // Right Half
- x2 - (this.helperProportions.width / 2) < r && // Left Half
- t < y1 + (this.helperProportions.height / 2) && // Bottom Half
- y2 - (this.helperProportions.height / 2) < b ); // Top Half
-
- }
- },
-
- _intersectsWithPointer: function(item) {
-
- var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
- isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
- isOverElement = isOverElementHeight && isOverElementWidth,
- verticalDirection = this._getDragVerticalDirection(),
- horizontalDirection = this._getDragHorizontalDirection();
-
- if (!isOverElement) {
- return false;
- }
-
- return this.floating ?
- ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
- : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
-
- },
-
- _intersectsWithSides: function(item) {
-
- var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
- isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
- verticalDirection = this._getDragVerticalDirection(),
- horizontalDirection = this._getDragHorizontalDirection();
-
- if (this.floating && horizontalDirection) {
- return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
- } else {
- return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
- }
-
- },
-
- _getDragVerticalDirection: function() {
- var delta = this.positionAbs.top - this.lastPositionAbs.top;
- return delta !== 0 && (delta > 0 ? "down" : "up");
- },
-
- _getDragHorizontalDirection: function() {
- var delta = this.positionAbs.left - this.lastPositionAbs.left;
- return delta !== 0 && (delta > 0 ? "right" : "left");
- },
-
- refresh: function(event) {
- this._refreshItems(event);
- this.refreshPositions();
- return this;
- },
-
- _connectWith: function() {
- var options = this.options;
- return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
- },
-
- _getItemsAsjQuery: function(connected) {
-
- var i, j, cur, inst,
- items = [],
- queries = [],
- connectWith = this._connectWith();
-
- if(connectWith && connected) {
- for (i = connectWith.length - 1; i >= 0; i--){
- cur = $(connectWith[i]);
- for ( j = cur.length - 1; j >= 0; j--){
- inst = $.data(cur[j], this.widgetFullName);
- if(inst && inst !== this && !inst.options.disabled) {
- queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]);
- }
- }
- }
- }
-
- queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
-
- for (i = queries.length - 1; i >= 0; i--){
- queries[i][0].each(function() {
- items.push(this);
- });
- }
-
- return $(items);
-
- },
-
- _removeCurrentsFromItems: function() {
-
- var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
-
- this.items = $.grep(this.items, function (item) {
- for (var j=0; j < list.length; j++) {
- if(list[j] === item.item[0]) {
- return false;
- }
- }
- return true;
- });
-
- },
-
- _refreshItems: function(event) {
-
- this.items = [];
- this.containers = [this];
-
- var i, j, cur, inst, targetData, _queries, item, queriesLength,
- items = this.items,
- queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
- connectWith = this._connectWith();
-
- if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
- for (i = connectWith.length - 1; i >= 0; i--){
- cur = $(connectWith[i]);
- for (j = cur.length - 1; j >= 0; j--){
- inst = $.data(cur[j], this.widgetFullName);
- if(inst && inst !== this && !inst.options.disabled) {
- queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
- this.containers.push(inst);
- }
- }
- }
- }
-
- for (i = queries.length - 1; i >= 0; i--) {
- targetData = queries[i][1];
- _queries = queries[i][0];
-
- for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
- item = $(_queries[j]);
-
- item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
-
- items.push({
- item: item,
- instance: targetData,
- width: 0, height: 0,
- left: 0, top: 0
- });
- }
- }
-
- },
-
- refreshPositions: function(fast) {
-
- //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
- if(this.offsetParent && this.helper) {
- this.offset.parent = this._getParentOffset();
- }
-
- var i, item, t, p;
-
- for (i = this.items.length - 1; i >= 0; i--){
- item = this.items[i];
-
- //We ignore calculating positions of all connected containers when we're not over them
- if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
- continue;
- }
-
- t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
-
- if (!fast) {
- item.width = t.outerWidth();
- item.height = t.outerHeight();
- }
-
- p = t.offset();
- item.left = p.left;
- item.top = p.top;
- }
-
- if(this.options.custom && this.options.custom.refreshContainers) {
- this.options.custom.refreshContainers.call(this);
- } else {
- for (i = this.containers.length - 1; i >= 0; i--){
- p = this.containers[i].element.offset();
- this.containers[i].containerCache.left = p.left;
- this.containers[i].containerCache.top = p.top;
- this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
- this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
- }
- }
-
- return this;
- },
-
- _createPlaceholder: function(that) {
- that = that || this;
- var className,
- o = that.options;
-
- if(!o.placeholder || o.placeholder.constructor === String) {
- className = o.placeholder;
- o.placeholder = {
- element: function() {
-
- var el = $(document.createElement(that.currentItem[0].nodeName))
- .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
- .removeClass("ui-sortable-helper")[0];
-
- if(!className) {
- el.style.visibility = "hidden";
- }
-
- return el;
- },
- update: function(container, p) {
-
- // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
- // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
- if(className && !o.forcePlaceholderSize) {
- return;
- }
-
- //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
- if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
- if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
- }
- };
- }
-
- //Create the placeholder
- that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
-
- //Append it after the actual current item
- that.currentItem.after(that.placeholder);
-
- //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
- o.placeholder.update(that, that.placeholder);
-
- },
-
- _contactContainers: function(event) {
- var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom,
- innermostContainer = null,
- innermostIndex = null;
-
- // get innermost container that intersects with item
- for (i = this.containers.length - 1; i >= 0; i--) {
-
- // never consider a container that's located within the item itself
- if($.contains(this.currentItem[0], this.containers[i].element[0])) {
- continue;
- }
-
- if(this._intersectsWith(this.containers[i].containerCache)) {
-
- // if we've already found a container and it's more "inner" than this, then continue
- if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
- continue;
- }
-
- innermostContainer = this.containers[i];
- innermostIndex = i;
-
- } else {
- // container doesn't intersect. trigger "out" event if necessary
- if(this.containers[i].containerCache.over) {
- this.containers[i]._trigger("out", event, this._uiHash(this));
- this.containers[i].containerCache.over = 0;
- }
- }
-
- }
-
- // if no intersecting containers found, return
- if(!innermostContainer) {
- return;
- }
-
- // move the item into the container if it's not there already
- if(this.containers.length === 1) {
- this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
- this.containers[innermostIndex].containerCache.over = 1;
- } else {
-
- //When entering a new container, we will find the item with the least distance and append our item near it
- dist = 10000;
- itemWithLeastDistance = null;
- posProperty = this.containers[innermostIndex].floating ? "left" : "top";
- sizeProperty = this.containers[innermostIndex].floating ? "width" : "height";
- base = this.positionAbs[posProperty] + this.offset.click[posProperty];
- for (j = this.items.length - 1; j >= 0; j--) {
- if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
- continue;
- }
- if(this.items[j].item[0] === this.currentItem[0]) {
- continue;
- }
- cur = this.items[j].item.offset()[posProperty];
- nearBottom = false;
- if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
- nearBottom = true;
- cur += this.items[j][sizeProperty];
- }
-
- if(Math.abs(cur - base) < dist) {
- dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
- this.direction = nearBottom ? "up": "down";
- }
- }
-
- //Check if dropOnEmpty is enabled
- if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
- return;
- }
-
- this.currentContainer = this.containers[innermostIndex];
- itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
- this._trigger("change", event, this._uiHash());
- this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
-
- //Update the placeholder
- this.options.placeholder.update(this.currentContainer, this.placeholder);
-
- this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
- this.containers[innermostIndex].containerCache.over = 1;
- }
-
-
- },
-
- _createHelper: function(event) {
-
- var o = this.options,
- helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
-
- //Add the helper to the DOM if that didn't happen already
- if(!helper.parents("body").length) {
- $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
- }
-
- if(helper[0] === this.currentItem[0]) {
- this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
- }
-
- if(!helper[0].style.width || o.forceHelperSize) {
- helper.width(this.currentItem.width());
- }
- if(!helper[0].style.height || o.forceHelperSize) {
- helper.height(this.currentItem.height());
- }
-
- return helper;
-
- },
-
- _adjustOffsetFromHelper: function(obj) {
- if (typeof obj === "string") {
- obj = obj.split(" ");
- }
- if ($.isArray(obj)) {
- obj = {left: +obj[0], top: +obj[1] || 0};
- }
- if ("left" in obj) {
- this.offset.click.left = obj.left + this.margins.left;
- }
- if ("right" in obj) {
- this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
- }
- if ("top" in obj) {
- this.offset.click.top = obj.top + this.margins.top;
- }
- if ("bottom" in obj) {
- this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
- }
- },
-
- _getParentOffset: function() {
-
-
- //Get the offsetParent and cache its position
- this.offsetParent = this.helper.offsetParent();
- var po = this.offsetParent.offset();
-
- // This is a special case where we need to modify a offset calculated on start, since the following happened:
- // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
- // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
- // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
- if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
- po.left += this.scrollParent.scrollLeft();
- po.top += this.scrollParent.scrollTop();
- }
-
- // This needs to be actually done for all browsers, since pageX/pageY includes this information
- // with an ugly IE fix
- if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
- po = { top: 0, left: 0 };
- }
-
- return {
- top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
- left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
- };
-
- },
-
- _getRelativeOffset: function() {
-
- if(this.cssPosition === "relative") {
- var p = this.currentItem.position();
- return {
- top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
- left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
- };
- } else {
- return { top: 0, left: 0 };
- }
-
- },
-
- _cacheMargins: function() {
- this.margins = {
- left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
- top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
- };
- },
-
- _cacheHelperProportions: function() {
- this.helperProportions = {
- width: this.helper.outerWidth(),
- height: this.helper.outerHeight()
- };
- },
-
- _setContainment: function() {
-
- var ce, co, over,
- o = this.options;
- if(o.containment === "parent") {
- o.containment = this.helper[0].parentNode;
- }
- if(o.containment === "document" || o.containment === "window") {
- this.containment = [
- 0 - this.offset.relative.left - this.offset.parent.left,
- 0 - this.offset.relative.top - this.offset.parent.top,
- $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
- ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
- ];
- }
-
- if(!(/^(document|window|parent)$/).test(o.containment)) {
- ce = $(o.containment)[0];
- co = $(o.containment).offset();
- over = ($(ce).css("overflow") !== "hidden");
-
- this.containment = [
- co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
- co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
- co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
- co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
- ];
- }
-
- },
-
- _convertPositionTo: function(d, pos) {
-
- if(!pos) {
- pos = this.position;
- }
- var mod = d === "absolute" ? 1 : -1,
- scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
- scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
-
- return {
- top: (
- pos.top + // The absolute mouse position
- this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
- ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
- ),
- left: (
- pos.left + // The absolute mouse position
- this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
- ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
- )
- };
-
- },
-
- _generatePosition: function(event) {
-
- var top, left,
- o = this.options,
- pageX = event.pageX,
- pageY = event.pageY,
- scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
-
- // This is another very weird special case that only happens for relative elements:
- // 1. If the css position is relative
- // 2. and the scroll parent is the document or similar to the offset parent
- // we have to refresh the relative offset during the scroll so there are no jumps
- if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
- this.offset.relative = this._getRelativeOffset();
- }
-
- /*
- * - Position constraining -
- * Constrain the position to a mix of grid, containment.
- */
-
- if(this.originalPosition) { //If we are not dragging yet, we won't check for options
-
- if(this.containment) {
- if(event.pageX - this.offset.click.left < this.containment[0]) {
- pageX = this.containment[0] + this.offset.click.left;
- }
- if(event.pageY - this.offset.click.top < this.containment[1]) {
- pageY = this.containment[1] + this.offset.click.top;
- }
- if(event.pageX - this.offset.click.left > this.containment[2]) {
- pageX = this.containment[2] + this.offset.click.left;
- }
- if(event.pageY - this.offset.click.top > this.containment[3]) {
- pageY = this.containment[3] + this.offset.click.top;
- }
- }
-
- if(o.grid) {
- top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
- pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
-
- left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
- pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
- }
-
- }
-
- return {
- top: (
- pageY - // The absolute mouse position
- this.offset.click.top - // Click offset (relative to the element)
- this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
- ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
- ),
- left: (
- pageX - // The absolute mouse position
- this.offset.click.left - // Click offset (relative to the element)
- this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
- this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
- ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
- )
- };
-
- },
-
- _rearrange: function(event, i, a, hardRefresh) {
-
- a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
-
- //Various things done here to improve the performance:
- // 1. we create a setTimeout, that calls refreshPositions
- // 2. on the instance, we have a counter variable, that get's higher after every append
- // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
- // 4. this lets only the last addition to the timeout stack through
- this.counter = this.counter ? ++this.counter : 1;
- var counter = this.counter;
-
- this._delay(function() {
- if(counter === this.counter) {
- this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
- }
- });
-
- },
-
- _clear: function(event, noPropagation) {
-
- this.reverting = false;
- // We delay all events that have to be triggered to after the point where the placeholder has been removed and
- // everything else normalized again
- var i,
- delayedTriggers = [];
-
- // We first have to update the dom position of the actual currentItem
- // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
- if(!this._noFinalSort && this.currentItem.parent().length) {
- this.placeholder.before(this.currentItem);
- }
- this._noFinalSort = null;
-
- if(this.helper[0] === this.currentItem[0]) {
- for(i in this._storedCSS) {
- if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
- this._storedCSS[i] = "";
- }
- }
- this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
- } else {
- this.currentItem.show();
- }
-
- if(this.fromOutside && !noPropagation) {
- delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
- }
- if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
- delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
- }
-
- // Check if the items Container has Changed and trigger appropriate
- // events.
- if (this !== this.currentContainer) {
- if(!noPropagation) {
- delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
- delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
- delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
- }
- }
-
-
- //Post events to containers
- for (i = this.containers.length - 1; i >= 0; i--){
- if(!noPropagation) {
- delayedTriggers.push((function(c) { return function(event) { c._trigger("deactivate", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
- }
- if(this.containers[i].containerCache.over) {
- delayedTriggers.push((function(c) { return function(event) { c._trigger("out", event, this._uiHash(this)); }; }).call(this, this.containers[i]));
- this.containers[i].containerCache.over = 0;
- }
- }
-
- //Do what was originally in plugins
- if(this._storedCursor) {
- $("body").css("cursor", this._storedCursor);
- }
- if(this._storedOpacity) {
- this.helper.css("opacity", this._storedOpacity);
- }
- if(this._storedZIndex) {
- this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
- }
-
- this.dragging = false;
- if(this.cancelHelperRemoval) {
- if(!noPropagation) {
- this._trigger("beforeStop", event, this._uiHash());
- for (i=0; i < delayedTriggers.length; i++) {
- delayedTriggers[i].call(this, event);
- } //Trigger all delayed events
- this._trigger("stop", event, this._uiHash());
- }
-
- this.fromOutside = false;
- return false;
- }
-
- if(!noPropagation) {
- this._trigger("beforeStop", event, this._uiHash());
- }
-
- //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
- this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
-
- if(this.helper[0] !== this.currentItem[0]) {
- this.helper.remove();
- }
- this.helper = null;
-
- if(!noPropagation) {
- for (i=0; i < delayedTriggers.length; i++) {
- delayedTriggers[i].call(this, event);
- } //Trigger all delayed events
- this._trigger("stop", event, this._uiHash());
- }
-
- this.fromOutside = false;
- return true;
-
- },
-
- _trigger: function() {
- if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
- this.cancel();
- }
- },
-
- _uiHash: function(_inst) {
- var inst = _inst || this;
- return {
- helper: inst.helper,
- placeholder: inst.placeholder || $([]),
- position: inst.position,
- originalPosition: inst.originalPosition,
- offset: inst.positionAbs,
- item: inst.currentItem,
- sender: _inst ? _inst.element : null
- };
- }
-
-});
-
-})(jQuery);
-
-;(jQuery.effects || (function($, undefined) {
-
-var dataSpace = "ui-effects-";
-
-$.effects = {
- effect: {}
-};
-
-/*!
- * jQuery Color Animations v2.1.2
- * https://github.com/jquery/jquery-color
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- *
- * Date: Wed Jan 16 08:47:09 2013 -0600
- */
-(function( jQuery, undefined ) {
-
- var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
-
- // plusequals test for += 100 -= 100
- rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
- // a set of RE's that can match strings and generate color tuples.
- stringParsers = [{
- re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
- parse: function( execResult ) {
- return [
- execResult[ 1 ],
- execResult[ 2 ],
- execResult[ 3 ],
- execResult[ 4 ]
- ];
- }
- }, {
- re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
- parse: function( execResult ) {
- return [
- execResult[ 1 ] * 2.55,
- execResult[ 2 ] * 2.55,
- execResult[ 3 ] * 2.55,
- execResult[ 4 ]
- ];
- }
- }, {
- // this regex ignores A-F because it's compared against an already lowercased string
- re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
- parse: function( execResult ) {
- return [
- parseInt( execResult[ 1 ], 16 ),
- parseInt( execResult[ 2 ], 16 ),
- parseInt( execResult[ 3 ], 16 )
- ];
- }
- }, {
- // this regex ignores A-F because it's compared against an already lowercased string
- re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
- parse: function( execResult ) {
- return [
- parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
- parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
- parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
- ];
- }
- }, {
- re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
- space: "hsla",
- parse: function( execResult ) {
- return [
- execResult[ 1 ],
- execResult[ 2 ] / 100,
- execResult[ 3 ] / 100,
- execResult[ 4 ]
- ];
- }
- }],
-
- // jQuery.Color( )
- color = jQuery.Color = function( color, green, blue, alpha ) {
- return new jQuery.Color.fn.parse( color, green, blue, alpha );
- },
- spaces = {
- rgba: {
- props: {
- red: {
- idx: 0,
- type: "byte"
- },
- green: {
- idx: 1,
- type: "byte"
- },
- blue: {
- idx: 2,
- type: "byte"
- }
- }
- },
-
- hsla: {
- props: {
- hue: {
- idx: 0,
- type: "degrees"
- },
- saturation: {
- idx: 1,
- type: "percent"
- },
- lightness: {
- idx: 2,
- type: "percent"
- }
- }
- }
- },
- propTypes = {
- "byte": {
- floor: true,
- max: 255
- },
- "percent": {
- max: 1
- },
- "degrees": {
- mod: 360,
- floor: true
- }
- },
- support = color.support = {},
-
- // element for support tests
- supportElem = jQuery( "<p>" )[ 0 ],
-
- // colors = jQuery.Color.names
- colors,
-
- // local aliases of functions called often
- each = jQuery.each;
-
-// determine rgba support immediately
-supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
-support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
-
-// define cache name and alpha properties
-// for rgba and hsla spaces
-each( spaces, function( spaceName, space ) {
- space.cache = "_" + spaceName;
- space.props.alpha = {
- idx: 3,
- type: "percent",
- def: 1
- };
-});
-
-function clamp( value, prop, allowEmpty ) {
- var type = propTypes[ prop.type ] || {};
-
- if ( value == null ) {
- return (allowEmpty || !prop.def) ? null : prop.def;
- }
-
- // ~~ is an short way of doing floor for positive numbers
- value = type.floor ? ~~value : parseFloat( value );
-
- // IE will pass in empty strings as value for alpha,
- // which will hit this case
- if ( isNaN( value ) ) {
- return prop.def;
- }
-
- if ( type.mod ) {
- // we add mod before modding to make sure that negatives values
- // get converted properly: -10 -> 350
- return (value + type.mod) % type.mod;
- }
-
- // for now all property types without mod have min and max
- return 0 > value ? 0 : type.max < value ? type.max : value;
-}
-
-function stringParse( string ) {
- var inst = color(),
- rgba = inst._rgba = [];
-
- string = string.toLowerCase();
-
- each( stringParsers, function( i, parser ) {
- var parsed,
- match = parser.re.exec( string ),
- values = match && parser.parse( match ),
- spaceName = parser.space || "rgba";
-
- if ( values ) {
- parsed = inst[ spaceName ]( values );
-
- // if this was an rgba parse the assignment might happen twice
- // oh well....
- inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
- rgba = inst._rgba = parsed._rgba;
-
- // exit each( stringParsers ) here because we matched
- return false;
- }
- });
-
- // Found a stringParser that handled it
- if ( rgba.length ) {
-
- // if this came from a parsed string, force "transparent" when alpha is 0
- // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
- if ( rgba.join() === "0,0,0,0" ) {
- jQuery.extend( rgba, colors.transparent );
- }
- return inst;
- }
-
- // named colors
- return colors[ string ];
-}
-
-color.fn = jQuery.extend( color.prototype, {
- parse: function( red, green, blue, alpha ) {
- if ( red === undefined ) {
- this._rgba = [ null, null, null, null ];
- return this;
- }
- if ( red.jquery || red.nodeType ) {
- red = jQuery( red ).css( green );
- green = undefined;
- }
-
- var inst = this,
- type = jQuery.type( red ),
- rgba = this._rgba = [];
-
- // more than 1 argument specified - assume ( red, green, blue, alpha )
- if ( green !== undefined ) {
- red = [ red, green, blue, alpha ];
- type = "array";
- }
-
- if ( type === "string" ) {
- return this.parse( stringParse( red ) || colors._default );
- }
-
- if ( type === "array" ) {
- each( spaces.rgba.props, function( key, prop ) {
- rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
- });
- return this;
- }
-
- if ( type === "object" ) {
- if ( red instanceof color ) {
- each( spaces, function( spaceName, space ) {
- if ( red[ space.cache ] ) {
- inst[ space.cache ] = red[ space.cache ].slice();
- }
- });
- } else {
- each( spaces, function( spaceName, space ) {
- var cache = space.cache;
- each( space.props, function( key, prop ) {
-
- // if the cache doesn't exist, and we know how to convert
- if ( !inst[ cache ] && space.to ) {
-
- // if the value was null, we don't need to copy it
- // if the key was alpha, we don't need to copy it either
- if ( key === "alpha" || red[ key ] == null ) {
- return;
- }
- inst[ cache ] = space.to( inst._rgba );
- }
-
- // this is the only case where we allow nulls for ALL properties.
- // call clamp with alwaysAllowEmpty
- inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
- });
-
- // everything defined but alpha?
- if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
- // use the default of 1
- inst[ cache ][ 3 ] = 1;
- if ( space.from ) {
- inst._rgba = space.from( inst[ cache ] );
- }
- }
- });
- }
- return this;
- }
- },
- is: function( compare ) {
- var is = color( compare ),
- same = true,
- inst = this;
-
- each( spaces, function( _, space ) {
- var localCache,
- isCache = is[ space.cache ];
- if (isCache) {
- localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
- each( space.props, function( _, prop ) {
- if ( isCache[ prop.idx ] != null ) {
- same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
- return same;
- }
- });
- }
- return same;
- });
- return same;
- },
- _space: function() {
- var used = [],
- inst = this;
- each( spaces, function( spaceName, space ) {
- if ( inst[ space.cache ] ) {
- used.push( spaceName );
- }
- });
- return used.pop();
- },
- transition: function( other, distance ) {
- var end = color( other ),
- spaceName = end._space(),
- space = spaces[ spaceName ],
- startColor = this.alpha() === 0 ? color( "transparent" ) : this,
- start = startColor[ space.cache ] || space.to( startColor._rgba ),
- result = start.slice();
-
- end = end[ space.cache ];
- each( space.props, function( key, prop ) {
- var index = prop.idx,
- startValue = start[ index ],
- endValue = end[ index ],
- type = propTypes[ prop.type ] || {};
-
- // if null, don't override start value
- if ( endValue === null ) {
- return;
- }
- // if null - use end
- if ( startValue === null ) {
- result[ index ] = endValue;
- } else {
- if ( type.mod ) {
- if ( endValue - startValue > type.mod / 2 ) {
- startValue += type.mod;
- } else if ( startValue - endValue > type.mod / 2 ) {
- startValue -= type.mod;
- }
- }
- result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
- }
- });
- return this[ spaceName ]( result );
- },
- blend: function( opaque ) {
- // if we are already opaque - return ourself
- if ( this._rgba[ 3 ] === 1 ) {
- return this;
- }
-
- var rgb = this._rgba.slice(),
- a = rgb.pop(),
- blend = color( opaque )._rgba;
-
- return color( jQuery.map( rgb, function( v, i ) {
- return ( 1 - a ) * blend[ i ] + a * v;
- }));
- },
- toRgbaString: function() {
- var prefix = "rgba(",
- rgba = jQuery.map( this._rgba, function( v, i ) {
- return v == null ? ( i > 2 ? 1 : 0 ) : v;
- });
-
- if ( rgba[ 3 ] === 1 ) {
- rgba.pop();
- prefix = "rgb(";
- }
-
- return prefix + rgba.join() + ")";
- },
- toHslaString: function() {
- var prefix = "hsla(",
- hsla = jQuery.map( this.hsla(), function( v, i ) {
- if ( v == null ) {
- v = i > 2 ? 1 : 0;
- }
-
- // catch 1 and 2
- if ( i && i < 3 ) {
- v = Math.round( v * 100 ) + "%";
- }
- return v;
- });
-
- if ( hsla[ 3 ] === 1 ) {
- hsla.pop();
- prefix = "hsl(";
- }
- return prefix + hsla.join() + ")";
- },
- toHexString: function( includeAlpha ) {
- var rgba = this._rgba.slice(),
- alpha = rgba.pop();
-
- if ( includeAlpha ) {
- rgba.push( ~~( alpha * 255 ) );
- }
-
- return "#" + jQuery.map( rgba, function( v ) {
-
- // default to 0 when nulls exist
- v = ( v || 0 ).toString( 16 );
- return v.length === 1 ? "0" + v : v;
- }).join("");
- },
- toString: function() {
- return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
- }
-});
-color.fn.parse.prototype = color.fn;
-
-// hsla conversions adapted from:
-// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/s…
-
-function hue2rgb( p, q, h ) {
- h = ( h + 1 ) % 1;
- if ( h * 6 < 1 ) {
- return p + (q - p) * h * 6;
- }
- if ( h * 2 < 1) {
- return q;
- }
- if ( h * 3 < 2 ) {
- return p + (q - p) * ((2/3) - h) * 6;
- }
- return p;
-}
-
-spaces.hsla.to = function ( rgba ) {
- if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
- return [ null, null, null, rgba[ 3 ] ];
- }
- var r = rgba[ 0 ] / 255,
- g = rgba[ 1 ] / 255,
- b = rgba[ 2 ] / 255,
- a = rgba[ 3 ],
- max = Math.max( r, g, b ),
- min = Math.min( r, g, b ),
- diff = max - min,
- add = max + min,
- l = add * 0.5,
- h, s;
-
- if ( min === max ) {
- h = 0;
- } else if ( r === max ) {
- h = ( 60 * ( g - b ) / diff ) + 360;
- } else if ( g === max ) {
- h = ( 60 * ( b - r ) / diff ) + 120;
- } else {
- h = ( 60 * ( r - g ) / diff ) + 240;
- }
-
- // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
- // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
- if ( diff === 0 ) {
- s = 0;
- } else if ( l <= 0.5 ) {
- s = diff / add;
- } else {
- s = diff / ( 2 - add );
- }
- return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
-};
-
-spaces.hsla.from = function ( hsla ) {
- if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
- return [ null, null, null, hsla[ 3 ] ];
- }
- var h = hsla[ 0 ] / 360,
- s = hsla[ 1 ],
- l = hsla[ 2 ],
- a = hsla[ 3 ],
- q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
- p = 2 * l - q;
-
- return [
- Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
- Math.round( hue2rgb( p, q, h ) * 255 ),
- Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
- a
- ];
-};
-
-
-each( spaces, function( spaceName, space ) {
- var props = space.props,
- cache = space.cache,
- to = space.to,
- from = space.from;
-
- // makes rgba() and hsla()
- color.fn[ spaceName ] = function( value ) {
-
- // generate a cache for this space if it doesn't exist
- if ( to && !this[ cache ] ) {
- this[ cache ] = to( this._rgba );
- }
- if ( value === undefined ) {
- return this[ cache ].slice();
- }
-
- var ret,
- type = jQuery.type( value ),
- arr = ( type === "array" || type === "object" ) ? value : arguments,
- local = this[ cache ].slice();
-
- each( props, function( key, prop ) {
- var val = arr[ type === "object" ? key : prop.idx ];
- if ( val == null ) {
- val = local[ prop.idx ];
- }
- local[ prop.idx ] = clamp( val, prop );
- });
-
- if ( from ) {
- ret = color( from( local ) );
- ret[ cache ] = local;
- return ret;
- } else {
- return color( local );
- }
- };
-
- // makes red() green() blue() alpha() hue() saturation() lightness()
- each( props, function( key, prop ) {
- // alpha is included in more than one space
- if ( color.fn[ key ] ) {
- return;
- }
- color.fn[ key ] = function( value ) {
- var vtype = jQuery.type( value ),
- fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
- local = this[ fn ](),
- cur = local[ prop.idx ],
- match;
-
- if ( vtype === "undefined" ) {
- return cur;
- }
-
- if ( vtype === "function" ) {
- value = value.call( this, cur );
- vtype = jQuery.type( value );
- }
- if ( value == null && prop.empty ) {
- return this;
- }
- if ( vtype === "string" ) {
- match = rplusequals.exec( value );
- if ( match ) {
- value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
- }
- }
- local[ prop.idx ] = value;
- return this[ fn ]( local );
- };
- });
-});
-
-// add cssHook and .fx.step function for each named hook.
-// accept a space separated string of properties
-color.hook = function( hook ) {
- var hooks = hook.split( " " );
- each( hooks, function( i, hook ) {
- jQuery.cssHooks[ hook ] = {
- set: function( elem, value ) {
- var parsed, curElem,
- backgroundColor = "";
-
- if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
- value = color( parsed || value );
- if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
- curElem = hook === "backgroundColor" ? elem.parentNode : elem;
- while (
- (backgroundColor === "" || backgroundColor === "transparent") &&
- curElem && curElem.style
- ) {
- try {
- backgroundColor = jQuery.css( curElem, "backgroundColor" );
- curElem = curElem.parentNode;
- } catch ( e ) {
- }
- }
-
- value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
- backgroundColor :
- "_default" );
- }
-
- value = value.toRgbaString();
- }
- try {
- elem.style[ hook ] = value;
- } catch( e ) {
- // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
- }
- }
- };
- jQuery.fx.step[ hook ] = function( fx ) {
- if ( !fx.colorInit ) {
- fx.start = color( fx.elem, hook );
- fx.end = color( fx.end );
- fx.colorInit = true;
- }
- jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
- };
- });
-
-};
-
-color.hook( stepHooks );
-
-jQuery.cssHooks.borderColor = {
- expand: function( value ) {
- var expanded = {};
-
- each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
- expanded[ "border" + part + "Color" ] = value;
- });
- return expanded;
- }
-};
-
-// Basic color names only.
-// Usage of any of the other color names requires adding yourself or including
-// jquery.color.svg-names.js.
-colors = jQuery.Color.names = {
- // 4.1. Basic color keywords
- aqua: "#00ffff",
- black: "#000000",
- blue: "#0000ff",
- fuchsia: "#ff00ff",
- gray: "#808080",
- green: "#008000",
- lime: "#00ff00",
- maroon: "#800000",
- navy: "#000080",
- olive: "#808000",
- purple: "#800080",
- red: "#ff0000",
- silver: "#c0c0c0",
- teal: "#008080",
- white: "#ffffff",
- yellow: "#ffff00",
-
- // 4.2.3. "transparent" color keyword
- transparent: [ null, null, null, 0 ],
-
- _default: "#ffffff"
-};
-
-})( jQuery );
-
-
-/******************************************************************************/
-/****************************** CLASS ANIMATIONS ******************************/
-/******************************************************************************/
-(function() {
-
-var classAnimationActions = [ "add", "remove", "toggle" ],
- shorthandStyles = {
- border: 1,
- borderBottom: 1,
- borderColor: 1,
- borderLeft: 1,
- borderRight: 1,
- borderTop: 1,
- borderWidth: 1,
- margin: 1,
- padding: 1
- };
-
-$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
- $.fx.step[ prop ] = function( fx ) {
- if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
- jQuery.style( fx.elem, prop, fx.end );
- fx.setAttr = true;
- }
- };
-});
-
-function getElementStyles( elem ) {
- var key, len,
- style = elem.ownerDocument.defaultView ?
- elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
- elem.currentStyle,
- styles = {};
-
- if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
- len = style.length;
- while ( len-- ) {
- key = style[ len ];
- if ( typeof style[ key ] === "string" ) {
- styles[ $.camelCase( key ) ] = style[ key ];
- }
- }
- // support: Opera, IE <9
- } else {
- for ( key in style ) {
- if ( typeof style[ key ] === "string" ) {
- styles[ key ] = style[ key ];
- }
- }
- }
-
- return styles;
-}
-
-
-function styleDifference( oldStyle, newStyle ) {
- var diff = {},
- name, value;
-
- for ( name in newStyle ) {
- value = newStyle[ name ];
- if ( oldStyle[ name ] !== value ) {
- if ( !shorthandStyles[ name ] ) {
- if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
- diff[ name ] = value;
- }
- }
- }
- }
-
- return diff;
-}
-
-// support: jQuery <1.8
-if ( !$.fn.addBack ) {
- $.fn.addBack = function( selector ) {
- return this.add( selector == null ?
- this.prevObject : this.prevObject.filter( selector )
- );
- };
-}
-
-$.effects.animateClass = function( value, duration, easing, callback ) {
- var o = $.speed( duration, easing, callback );
-
- return this.queue( function() {
- var animated = $( this ),
- baseClass = animated.attr( "class" ) || "",
- applyClassChange,
- allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
-
- // map the animated objects to store the original styles.
- allAnimations = allAnimations.map(function() {
- var el = $( this );
- return {
- el: el,
- start: getElementStyles( this )
- };
- });
-
- // apply class change
- applyClassChange = function() {
- $.each( classAnimationActions, function(i, action) {
- if ( value[ action ] ) {
- animated[ action + "Class" ]( value[ action ] );
- }
- });
- };
- applyClassChange();
-
- // map all animated objects again - calculate new styles and diff
- allAnimations = allAnimations.map(function() {
- this.end = getElementStyles( this.el[ 0 ] );
- this.diff = styleDifference( this.start, this.end );
- return this;
- });
-
- // apply original class
- animated.attr( "class", baseClass );
-
- // map all animated objects again - this time collecting a promise
- allAnimations = allAnimations.map(function() {
- var styleInfo = this,
- dfd = $.Deferred(),
- opts = $.extend({}, o, {
- queue: false,
- complete: function() {
- dfd.resolve( styleInfo );
- }
- });
-
- this.el.animate( this.diff, opts );
- return dfd.promise();
- });
-
- // once all animations have completed:
- $.when.apply( $, allAnimations.get() ).done(function() {
-
- // set the final class
- applyClassChange();
-
- // for each animated element,
- // clear all css properties that were animated
- $.each( arguments, function() {
- var el = this.el;
- $.each( this.diff, function(key) {
- el.css( key, "" );
- });
- });
-
- // this is guarnteed to be there if you use jQuery.speed()
- // it also handles dequeuing the next anim...
- o.complete.call( animated[ 0 ] );
- });
- });
-};
-
-$.fn.extend({
- _addClass: $.fn.addClass,
- addClass: function( classNames, speed, easing, callback ) {
- return speed ?
- $.effects.animateClass.call( this,
- { add: classNames }, speed, easing, callback ) :
- this._addClass( classNames );
- },
-
- _removeClass: $.fn.removeClass,
- removeClass: function( classNames, speed, easing, callback ) {
- return arguments.length > 1 ?
- $.effects.animateClass.call( this,
- { remove: classNames }, speed, easing, callback ) :
- this._removeClass.apply( this, arguments );
- },
-
- _toggleClass: $.fn.toggleClass,
- toggleClass: function( classNames, force, speed, easing, callback ) {
- if ( typeof force === "boolean" || force === undefined ) {
- if ( !speed ) {
- // without speed parameter
- return this._toggleClass( classNames, force );
- } else {
- return $.effects.animateClass.call( this,
- (force ? { add: classNames } : { remove: classNames }),
- speed, easing, callback );
- }
- } else {
- // without force parameter
- return $.effects.animateClass.call( this,
- { toggle: classNames }, force, speed, easing );
- }
- },
-
- switchClass: function( remove, add, speed, easing, callback) {
- return $.effects.animateClass.call( this, {
- add: add,
- remove: remove
- }, speed, easing, callback );
- }
-});
-
-})();
-
-/******************************************************************************/
-/*********************************** EFFECTS **********************************/
-/******************************************************************************/
-
-(function() {
-
-$.extend( $.effects, {
- version: "1.10.1",
-
- // Saves a set of properties in a data storage
- save: function( element, set ) {
- for( var i=0; i < set.length; i++ ) {
- if ( set[ i ] !== null ) {
- element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
- }
- }
- },
-
- // Restores a set of previously saved properties from a data storage
- restore: function( element, set ) {
- var val, i;
- for( i=0; i < set.length; i++ ) {
- if ( set[ i ] !== null ) {
- val = element.data( dataSpace + set[ i ] );
- // support: jQuery 1.6.2
- // http://bugs.jquery.com/ticket/9917
- // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
- // We can't differentiate between "" and 0 here, so we just assume
- // empty string since it's likely to be a more common value...
- if ( val === undefined ) {
- val = "";
- }
- element.css( set[ i ], val );
- }
- }
- },
-
- setMode: function( el, mode ) {
- if (mode === "toggle") {
- mode = el.is( ":hidden" ) ? "show" : "hide";
- }
- return mode;
- },
-
- // Translates a [top,left] array into a baseline value
- // this should be a little more flexible in the future to handle a string & hash
- getBaseline: function( origin, original ) {
- var y, x;
- switch ( origin[ 0 ] ) {
- case "top": y = 0; break;
- case "middle": y = 0.5; break;
- case "bottom": y = 1; break;
- default: y = origin[ 0 ] / original.height;
- }
- switch ( origin[ 1 ] ) {
- case "left": x = 0; break;
- case "center": x = 0.5; break;
- case "right": x = 1; break;
- default: x = origin[ 1 ] / original.width;
- }
- return {
- x: x,
- y: y
- };
- },
-
- // Wraps the element around a wrapper that copies position properties
- createWrapper: function( element ) {
-
- // if the element is already wrapped, return it
- if ( element.parent().is( ".ui-effects-wrapper" )) {
- return element.parent();
- }
-
- // wrap the element
- var props = {
- width: element.outerWidth(true),
- height: element.outerHeight(true),
- "float": element.css( "float" )
- },
- wrapper = $( "<div></div>" )
- .addClass( "ui-effects-wrapper" )
- .css({
- fontSize: "100%",
- background: "transparent",
- border: "none",
- margin: 0,
- padding: 0
- }),
- // Store the size in case width/height are defined in % - Fixes #5245
- size = {
- width: element.width(),
- height: element.height()
- },
- active = document.activeElement;
-
- // support: Firefox
- // Firefox incorrectly exposes anonymous content
- // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
- try {
- active.id;
- } catch( e ) {
- active = document.body;
- }
-
- element.wrap( wrapper );
-
- // Fixes #7595 - Elements lose focus when wrapped.
- if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
- $( active ).focus();
- }
-
- wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
-
- // transfer positioning properties to the wrapper
- if ( element.css( "position" ) === "static" ) {
- wrapper.css({ position: "relative" });
- element.css({ position: "relative" });
- } else {
- $.extend( props, {
- position: element.css( "position" ),
- zIndex: element.css( "z-index" )
- });
- $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
- props[ pos ] = element.css( pos );
- if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
- props[ pos ] = "auto";
- }
- });
- element.css({
- position: "relative",
- top: 0,
- left: 0,
- right: "auto",
- bottom: "auto"
- });
- }
- element.css(size);
-
- return wrapper.css( props ).show();
- },
-
- removeWrapper: function( element ) {
- var active = document.activeElement;
-
- if ( element.parent().is( ".ui-effects-wrapper" ) ) {
- element.parent().replaceWith( element );
-
- // Fixes #7595 - Elements lose focus when wrapped.
- if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
- $( active ).focus();
- }
- }
-
-
- return element;
- },
-
- setTransition: function( element, list, factor, value ) {
- value = value || {};
- $.each( list, function( i, x ) {
- var unit = element.cssUnit( x );
- if ( unit[ 0 ] > 0 ) {
- value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
- }
- });
- return value;
- }
-});
-
-// return an effect options object for the given parameters:
-function _normalizeArguments( effect, options, speed, callback ) {
-
- // allow passing all options as the first parameter
- if ( $.isPlainObject( effect ) ) {
- options = effect;
- effect = effect.effect;
- }
-
- // convert to an object
- effect = { effect: effect };
-
- // catch (effect, null, ...)
- if ( options == null ) {
- options = {};
- }
-
- // catch (effect, callback)
- if ( $.isFunction( options ) ) {
- callback = options;
- speed = null;
- options = {};
- }
-
- // catch (effect, speed, ?)
- if ( typeof options === "number" || $.fx.speeds[ options ] ) {
- callback = speed;
- speed = options;
- options = {};
- }
-
- // catch (effect, options, callback)
- if ( $.isFunction( speed ) ) {
- callback = speed;
- speed = null;
- }
-
- // add options to effect
- if ( options ) {
- $.extend( effect, options );
- }
-
- speed = speed || options.duration;
- effect.duration = $.fx.off ? 0 :
- typeof speed === "number" ? speed :
- speed in $.fx.speeds ? $.fx.speeds[ speed ] :
- $.fx.speeds._default;
-
- effect.complete = callback || options.complete;
-
- return effect;
-}
-
-function standardSpeed( speed ) {
- // valid standard speeds
- if ( !speed || typeof speed === "number" || $.fx.speeds[ speed ] ) {
- return true;
- }
-
- // invalid strings - treat as "normal" speed
- return typeof speed === "string" && !$.effects.effect[ speed ];
-}
-
-$.fn.extend({
- effect: function( /* effect, options, speed, callback */ ) {
- var args = _normalizeArguments.apply( this, arguments ),
- mode = args.mode,
- queue = args.queue,
- effectMethod = $.effects.effect[ args.effect ];
-
- if ( $.fx.off || !effectMethod ) {
- // delegate to the original method (e.g., .show()) if possible
- if ( mode ) {
- return this[ mode ]( args.duration, args.complete );
- } else {
- return this.each( function() {
- if ( args.complete ) {
- args.complete.call( this );
- }
- });
- }
- }
-
- function run( next ) {
- var elem = $( this ),
- complete = args.complete,
- mode = args.mode;
-
- function done() {
- if ( $.isFunction( complete ) ) {
- complete.call( elem[0] );
- }
- if ( $.isFunction( next ) ) {
- next();
- }
- }
-
- // if the element is hiddden and mode is hide,
- // or element is visible and mode is show
- if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
- done();
- } else {
- effectMethod.call( elem[0], args, done );
- }
- }
-
- return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
- },
-
- _show: $.fn.show,
- show: function( speed ) {
- if ( standardSpeed( speed ) ) {
- return this._show.apply( this, arguments );
- } else {
- var args = _normalizeArguments.apply( this, arguments );
- args.mode = "show";
- return this.effect.call( this, args );
- }
- },
-
- _hide: $.fn.hide,
- hide: function( speed ) {
- if ( standardSpeed( speed ) ) {
- return this._hide.apply( this, arguments );
- } else {
- var args = _normalizeArguments.apply( this, arguments );
- args.mode = "hide";
- return this.effect.call( this, args );
- }
- },
-
- // jQuery core overloads toggle and creates _toggle
- __toggle: $.fn.toggle,
- toggle: function( speed ) {
- if ( standardSpeed( speed ) || typeof speed === "boolean" || $.isFunction( speed ) ) {
- return this.__toggle.apply( this, arguments );
- } else {
- var args = _normalizeArguments.apply( this, arguments );
- args.mode = "toggle";
- return this.effect.call( this, args );
- }
- },
-
- // helper functions
- cssUnit: function(key) {
- var style = this.css( key ),
- val = [];
-
- $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
- if ( style.indexOf( unit ) > 0 ) {
- val = [ parseFloat( style ), unit ];
- }
- });
- return val;
- }
-});
-
-})();
-
-/******************************************************************************/
-/*********************************** EASING ***********************************/
-/******************************************************************************/
-
-(function() {
-
-// based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
-
-var baseEasings = {};
-
-$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
- baseEasings[ name ] = function( p ) {
- return Math.pow( p, i + 2 );
- };
-});
-
-$.extend( baseEasings, {
- Sine: function ( p ) {
- return 1 - Math.cos( p * Math.PI / 2 );
- },
- Circ: function ( p ) {
- return 1 - Math.sqrt( 1 - p * p );
- },
- Elastic: function( p ) {
- return p === 0 || p === 1 ? p :
- -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
- },
- Back: function( p ) {
- return p * p * ( 3 * p - 2 );
- },
- Bounce: function ( p ) {
- var pow2,
- bounce = 4;
-
- while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
- return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
- }
-});
-
-$.each( baseEasings, function( name, easeIn ) {
- $.easing[ "easeIn" + name ] = easeIn;
- $.easing[ "easeOut" + name ] = function( p ) {
- return 1 - easeIn( 1 - p );
- };
- $.easing[ "easeInOut" + name ] = function( p ) {
- return p < 0.5 ?
- easeIn( p * 2 ) / 2 :
- 1 - easeIn( p * -2 + 2 ) / 2;
- };
-});
-
-})();
-
-})(jQuery));
-
-(function( $, undefined ) {
-
-var uid = 0,
- hideProps = {},
- showProps = {};
-
-hideProps.height = hideProps.paddingTop = hideProps.paddingBottom =
- hideProps.borderTopWidth = hideProps.borderBottomWidth = "hide";
-showProps.height = showProps.paddingTop = showProps.paddingBottom =
- showProps.borderTopWidth = showProps.borderBottomWidth = "show";
-
-$.widget( "ui.accordion", {
- version: "1.10.1",
- options: {
- active: 0,
- animate: {},
- collapsible: false,
- event: "click",
- header: "> li > :first-child,> :not(li):even",
- heightStyle: "auto",
- icons: {
- activeHeader: "ui-icon-triangle-1-s",
- header: "ui-icon-triangle-1-e"
- },
-
- // callbacks
- activate: null,
- beforeActivate: null
- },
-
- _create: function() {
- var options = this.options;
- this.prevShow = this.prevHide = $();
- this.element.addClass( "ui-accordion ui-widget ui-helper-reset" )
- // ARIA
- .attr( "role", "tablist" );
-
- // don't allow collapsible: false and active: false / null
- if ( !options.collapsible && (options.active === false || options.active == null) ) {
- options.active = 0;
- }
-
- this._processPanels();
- // handle negative values
- if ( options.active < 0 ) {
- options.active += this.headers.length;
- }
- this._refresh();
- },
-
- _getCreateEventData: function() {
- return {
- header: this.active,
- panel: !this.active.length ? $() : this.active.next(),
- content: !this.active.length ? $() : this.active.next()
- };
- },
-
- _createIcons: function() {
- var icons = this.options.icons;
- if ( icons ) {
- $( "<span>" )
- .addClass( "ui-accordion-header-icon ui-icon " + icons.header )
- .prependTo( this.headers );
- this.active.children( ".ui-accordion-header-icon" )
- .removeClass( icons.header )
- .addClass( icons.activeHeader );
- this.headers.addClass( "ui-accordion-icons" );
- }
- },
-
- _destroyIcons: function() {
- this.headers
- .removeClass( "ui-accordion-icons" )
- .children( ".ui-accordion-header-icon" )
- .remove();
- },
-
- _destroy: function() {
- var contents;
-
- // clean up main element
- this.element
- .removeClass( "ui-accordion ui-widget ui-helper-reset" )
- .removeAttr( "role" );
-
- // clean up headers
- this.headers
- .removeClass( "ui-accordion-header ui-accordion-header-active ui-helper-reset ui-state-default ui-corner-all ui-state-active ui-state-disabled ui-corner-top" )
- .removeAttr( "role" )
- .removeAttr( "aria-selected" )
- .removeAttr( "aria-controls" )
- .removeAttr( "tabIndex" )
- .each(function() {
- if ( /^ui-accordion/.test( this.id ) ) {
- this.removeAttribute( "id" );
- }
- });
- this._destroyIcons();
-
- // clean up content panels
- contents = this.headers.next()
- .css( "display", "" )
- .removeAttr( "role" )
- .removeAttr( "aria-expanded" )
- .removeAttr( "aria-hidden" )
- .removeAttr( "aria-labelledby" )
- .removeClass( "ui-helper-reset ui-widget-content ui-corner-bottom ui-accordion-content ui-accordion-content-active ui-state-disabled" )
- .each(function() {
- if ( /^ui-accordion/.test( this.id ) ) {
- this.removeAttribute( "id" );
- }
- });
- if ( this.options.heightStyle !== "content" ) {
- contents.css( "height", "" );
- }
- },
-
- _setOption: function( key, value ) {
- if ( key === "active" ) {
- // _activate() will handle invalid values and update this.options
- this._activate( value );
- return;
- }
-
- if ( key === "event" ) {
- if ( this.options.event ) {
- this._off( this.headers, this.options.event );
- }
- this._setupEvents( value );
- }
-
- this._super( key, value );
-
- // setting collapsible: false while collapsed; open first panel
- if ( key === "collapsible" && !value && this.options.active === false ) {
- this._activate( 0 );
- }
-
- if ( key === "icons" ) {
- this._destroyIcons();
- if ( value ) {
- this._createIcons();
- }
- }
-
- // #5332 - opacity doesn't cascade to positioned elements in IE
- // so we need to add the disabled class to the headers and panels
- if ( key === "disabled" ) {
- this.headers.add( this.headers.next() )
- .toggleClass( "ui-state-disabled", !!value );
- }
- },
-
- _keydown: function( event ) {
- /*jshint maxcomplexity:15*/
- if ( event.altKey || event.ctrlKey ) {
- return;
- }
-
- var keyCode = $.ui.keyCode,
- length = this.headers.length,
- currentIndex = this.headers.index( event.target ),
- toFocus = false;
-
- switch ( event.keyCode ) {
- case keyCode.RIGHT:
- case keyCode.DOWN:
- toFocus = this.headers[ ( currentIndex + 1 ) % length ];
- break;
- case keyCode.LEFT:
- case keyCode.UP:
- toFocus = this.headers[ ( currentIndex - 1 + length ) % length ];
- break;
- case keyCode.SPACE:
- case keyCode.ENTER:
- this._eventHandler( event );
- break;
- case keyCode.HOME:
- toFocus = this.headers[ 0 ];
- break;
- case keyCode.END:
- toFocus = this.headers[ length - 1 ];
- break;
- }
-
- if ( toFocus ) {
- $( event.target ).attr( "tabIndex", -1 );
- $( toFocus ).attr( "tabIndex", 0 );
- toFocus.focus();
- event.preventDefault();
- }
- },
-
- _panelKeyDown : function( event ) {
- if ( event.keyCode === $.ui.keyCode.UP && event.ctrlKey ) {
- $( event.currentTarget ).prev().focus();
- }
- },
-
- refresh: function() {
- var options = this.options;
- this._processPanels();
-
- // was collapsed or no panel
- if ( ( options.active === false && options.collapsible === true ) || !this.headers.length ) {
- options.active = false;
- this.active = $();
- // active false only when collapsible is true
- } if ( options.active === false ) {
- this._activate( 0 );
- // was active, but active panel is gone
- } else if ( this.active.length && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
- // all remaining panel are disabled
- if ( this.headers.length === this.headers.find(".ui-state-disabled").length ) {
- options.active = false;
- this.active = $();
- // activate previous panel
- } else {
- this._activate( Math.max( 0, options.active - 1 ) );
- }
- // was active, active panel still exists
- } else {
- // make sure active index is correct
- options.active = this.headers.index( this.active );
- }
-
- this._destroyIcons();
-
- this._refresh();
- },
-
- _processPanels: function() {
- this.headers = this.element.find( this.options.header )
- .addClass( "ui-accordion-header ui-helper-reset ui-state-default ui-corner-all" );
-
- this.headers.next()
- .addClass( "ui-accordion-content ui-helper-reset ui-widget-content ui-corner-bottom" )
- .filter(":not(.ui-accordion-content-active)")
- .hide();
- },
-
- _refresh: function() {
- var maxHeight,
- options = this.options,
- heightStyle = options.heightStyle,
- parent = this.element.parent(),
- accordionId = this.accordionId = "ui-accordion-" +
- (this.element.attr( "id" ) || ++uid);
-
- this.active = this._findActive( options.active )
- .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" )
- .removeClass( "ui-corner-all" );
- this.active.next()
- .addClass( "ui-accordion-content-active" )
- .show();
-
- this.headers
- .attr( "role", "tab" )
- .each(function( i ) {
- var header = $( this ),
- headerId = header.attr( "id" ),
- panel = header.next(),
- panelId = panel.attr( "id" );
- if ( !headerId ) {
- headerId = accordionId + "-header-" + i;
- header.attr( "id", headerId );
- }
- if ( !panelId ) {
- panelId = accordionId + "-panel-" + i;
- panel.attr( "id", panelId );
- }
- header.attr( "aria-controls", panelId );
- panel.attr( "aria-labelledby", headerId );
- })
- .next()
- .attr( "role", "tabpanel" );
-
- this.headers
- .not( this.active )
- .attr({
- "aria-selected": "false",
- tabIndex: -1
- })
- .next()
- .attr({
- "aria-expanded": "false",
- "aria-hidden": "true"
- })
- .hide();
-
- // make sure at least one header is in the tab order
- if ( !this.active.length ) {
- this.headers.eq( 0 ).attr( "tabIndex", 0 );
- } else {
- this.active.attr({
- "aria-selected": "true",
- tabIndex: 0
- })
- .next()
- .attr({
- "aria-expanded": "true",
- "aria-hidden": "false"
- });
- }
-
- this._createIcons();
-
- this._setupEvents( options.event );
-
- if ( heightStyle === "fill" ) {
- maxHeight = parent.height();
- this.element.siblings( ":visible" ).each(function() {
- var elem = $( this ),
- position = elem.css( "position" );
-
- if ( position === "absolute" || position === "fixed" ) {
- return;
- }
- maxHeight -= elem.outerHeight( true );
- });
-
- this.headers.each(function() {
- maxHeight -= $( this ).outerHeight( true );
- });
-
- this.headers.next()
- .each(function() {
- $( this ).height( Math.max( 0, maxHeight -
- $( this ).innerHeight() + $( this ).height() ) );
- })
- .css( "overflow", "auto" );
- } else if ( heightStyle === "auto" ) {
- maxHeight = 0;
- this.headers.next()
- .each(function() {
- maxHeight = Math.max( maxHeight, $( this ).css( "height", "" ).height() );
- })
- .height( maxHeight );
- }
- },
-
- _activate: function( index ) {
- var active = this._findActive( index )[ 0 ];
-
- // trying to activate the already active panel
- if ( active === this.active[ 0 ] ) {
- return;
- }
-
- // trying to collapse, simulate a click on the currently active header
- active = active || this.active[ 0 ];
-
- this._eventHandler({
- target: active,
- currentTarget: active,
- preventDefault: $.noop
- });
- },
-
- _findActive: function( selector ) {
- return typeof selector === "number" ? this.headers.eq( selector ) : $();
- },
-
- _setupEvents: function( event ) {
- var events = {
- keydown: "_keydown"
- };
- if ( event ) {
- $.each( event.split(" "), function( index, eventName ) {
- events[ eventName ] = "_eventHandler";
- });
- }
-
- this._off( this.headers.add( this.headers.next() ) );
- this._on( this.headers, events );
- this._on( this.headers.next(), { keydown: "_panelKeyDown" });
- this._hoverable( this.headers );
- this._focusable( this.headers );
- },
-
- _eventHandler: function( event ) {
- var options = this.options,
- active = this.active,
- clicked = $( event.currentTarget ),
- clickedIsActive = clicked[ 0 ] === active[ 0 ],
- collapsing = clickedIsActive && options.collapsible,
- toShow = collapsing ? $() : clicked.next(),
- toHide = active.next(),
- eventData = {
- oldHeader: active,
- oldPanel: toHide,
- newHeader: collapsing ? $() : clicked,
- newPanel: toShow
- };
-
- event.preventDefault();
-
- if (
- // click on active header, but not collapsible
- ( clickedIsActive && !options.collapsible ) ||
- // allow canceling activation
- ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
- return;
- }
-
- options.active = collapsing ? false : this.headers.index( clicked );
-
- // when the call to ._toggle() comes after the class changes
- // it causes a very odd bug in IE 8 (see #6720)
- this.active = clickedIsActive ? $() : clicked;
- this._toggle( eventData );
-
- // switch classes
- // corner classes on the previously active header stay after the animation
- active.removeClass( "ui-accordion-header-active ui-state-active" );
- if ( options.icons ) {
- active.children( ".ui-accordion-header-icon" )
- .removeClass( options.icons.activeHeader )
- .addClass( options.icons.header );
- }
-
- if ( !clickedIsActive ) {
- clicked
- .removeClass( "ui-corner-all" )
- .addClass( "ui-accordion-header-active ui-state-active ui-corner-top" );
- if ( options.icons ) {
- clicked.children( ".ui-accordion-header-icon" )
- .removeClass( options.icons.header )
- .addClass( options.icons.activeHeader );
- }
-
- clicked
- .next()
- .addClass( "ui-accordion-content-active" );
- }
- },
-
- _toggle: function( data ) {
- var toShow = data.newPanel,
- toHide = this.prevShow.length ? this.prevShow : data.oldPanel;
-
- // handle activating a panel during the animation for another activation
- this.prevShow.add( this.prevHide ).stop( true, true );
- this.prevShow = toShow;
- this.prevHide = toHide;
-
- if ( this.options.animate ) {
- this._animate( toShow, toHide, data );
- } else {
- toHide.hide();
- toShow.show();
- this._toggleComplete( data );
- }
-
- toHide.attr({
- "aria-expanded": "false",
- "aria-hidden": "true"
- });
- toHide.prev().attr( "aria-selected", "false" );
- // if we're switching panels, remove the old header from the tab order
- // if we're opening from collapsed state, remove the previous header from the tab order
- // if we're collapsing, then keep the collapsing header in the tab order
- if ( toShow.length && toHide.length ) {
- toHide.prev().attr( "tabIndex", -1 );
- } else if ( toShow.length ) {
- this.headers.filter(function() {
- return $( this ).attr( "tabIndex" ) === 0;
- })
- .attr( "tabIndex", -1 );
- }
-
- toShow
- .attr({
- "aria-expanded": "true",
- "aria-hidden": "false"
- })
- .prev()
- .attr({
- "aria-selected": "true",
- tabIndex: 0
- });
- },
-
- _animate: function( toShow, toHide, data ) {
- var total, easing, duration,
- that = this,
- adjust = 0,
- down = toShow.length &&
- ( !toHide.length || ( toShow.index() < toHide.index() ) ),
- animate = this.options.animate || {},
- options = down && animate.down || animate,
- complete = function() {
- that._toggleComplete( data );
- };
-
- if ( typeof options === "number" ) {
- duration = options;
- }
- if ( typeof options === "string" ) {
- easing = options;
- }
- // fall back from options to animation in case of partial down settings
- easing = easing || options.easing || animate.easing;
- duration = duration || options.duration || animate.duration;
-
- if ( !toHide.length ) {
- return toShow.animate( showProps, duration, easing, complete );
- }
- if ( !toShow.length ) {
- return toHide.animate( hideProps, duration, easing, complete );
- }
-
- total = toShow.show().outerHeight();
- toHide.animate( hideProps, {
- duration: duration,
- easing: easing,
- step: function( now, fx ) {
- fx.now = Math.round( now );
- }
- });
- toShow
- .hide()
- .animate( showProps, {
- duration: duration,
- easing: easing,
- complete: complete,
- step: function( now, fx ) {
- fx.now = Math.round( now );
- if ( fx.prop !== "height" ) {
- adjust += fx.now;
- } else if ( that.options.heightStyle !== "content" ) {
- fx.now = Math.round( total - toHide.outerHeight() - adjust );
- adjust = 0;
- }
- }
- });
- },
-
- _toggleComplete: function( data ) {
- var toHide = data.oldPanel;
-
- toHide
- .removeClass( "ui-accordion-content-active" )
- .prev()
- .removeClass( "ui-corner-top" )
- .addClass( "ui-corner-all" );
-
- // Work around for rendering bug in IE (#5421)
- if ( toHide.length ) {
- toHide.parent()[0].className = toHide.parent()[0].className;
- }
-
- this._trigger( "activate", null, data );
- }
-});
-
-})( jQuery );
-
-(function( $, undefined ) {
-
-// used to prevent race conditions with remote data sources
-var requestIndex = 0;
-
-$.widget( "ui.autocomplete", {
- version: "1.10.1",
- defaultElement: "<input>",
- options: {
- appendTo: null,
- autoFocus: false,
- delay: 300,
- minLength: 1,
- position: {
- my: "left top",
- at: "left bottom",
- collision: "none"
- },
- source: null,
-
- // callbacks
- change: null,
- close: null,
- focus: null,
- open: null,
- response: null,
- search: null,
- select: null
- },
-
- pending: 0,
-
- _create: function() {
- // Some browsers only repeat keydown events, not keypress events,
- // so we use the suppressKeyPress flag to determine if we've already
- // handled the keydown event. #7269
- // Unfortunately the code for & in keypress is the same as the up arrow,
- // so we use the suppressKeyPressRepeat flag to avoid handling keypress
- // events when we know the keydown event was used to modify the
- // search term. #7799
- var suppressKeyPress, suppressKeyPressRepeat, suppressInput,
- nodeName = this.element[0].nodeName.toLowerCase(),
- isTextarea = nodeName === "textarea",
- isInput = nodeName === "input";
-
- this.isMultiLine =
- // Textareas are always multi-line
- isTextarea ? true :
- // Inputs are always single-line, even if inside a contentEditable element
- // IE also treats inputs as contentEditable
- isInput ? false :
- // All other element types are determined by whether or not they're contentEditable
- this.element.prop( "isContentEditable" );
-
- this.valueMethod = this.element[ isTextarea || isInput ? "val" : "text" ];
- this.isNewMenu = true;
-
- this.element
- .addClass( "ui-autocomplete-input" )
- .attr( "autocomplete", "off" );
-
- this._on( this.element, {
- keydown: function( event ) {
- /*jshint maxcomplexity:15*/
- if ( this.element.prop( "readOnly" ) ) {
- suppressKeyPress = true;
- suppressInput = true;
- suppressKeyPressRepeat = true;
- return;
- }
-
- suppressKeyPress = false;
- suppressInput = false;
- suppressKeyPressRepeat = false;
- var keyCode = $.ui.keyCode;
- switch( event.keyCode ) {
- case keyCode.PAGE_UP:
- suppressKeyPress = true;
- this._move( "previousPage", event );
- break;
- case keyCode.PAGE_DOWN:
- suppressKeyPress = true;
- this._move( "nextPage", event );
- break;
- case keyCode.UP:
- suppressKeyPress = true;
- this._keyEvent( "previous", event );
- break;
- case keyCode.DOWN:
- suppressKeyPress = true;
- this._keyEvent( "next", event );
- break;
- case keyCode.ENTER:
- case keyCode.NUMPAD_ENTER:
- // when menu is open and has focus
- if ( this.menu.active ) {
- // #6055 - Opera still allows the keypress to occur
- // which causes forms to submit
- suppressKeyPress = true;
- event.preventDefault();
- this.menu.select( event );
- }
- break;
- case keyCode.TAB:
- if ( this.menu.active ) {
- this.menu.select( event );
- }
- break;
- case keyCode.ESCAPE:
- if ( this.menu.element.is( ":visible" ) ) {
- this._value( this.term );
- this.close( event );
- // Different browsers have different default behavior for escape
- // Single press can mean undo or clear
- // Double press in IE means clear the whole form
- event.preventDefault();
- }
- break;
- default:
- suppressKeyPressRepeat = true;
- // search timeout should be triggered before the input value is changed
- this._searchTimeout( event );
- break;
- }
- },
- keypress: function( event ) {
- if ( suppressKeyPress ) {
- suppressKeyPress = false;
- event.preventDefault();
- return;
- }
- if ( suppressKeyPressRepeat ) {
- return;
- }
-
- // replicate some key handlers to allow them to repeat in Firefox and Opera
- var keyCode = $.ui.keyCode;
- switch( event.keyCode ) {
- case keyCode.PAGE_UP:
- this._move( "previousPage", event );
- break;
- case keyCode.PAGE_DOWN:
- this._move( "nextPage", event );
- break;
- case keyCode.UP:
- this._keyEvent( "previous", event );
- break;
- case keyCode.DOWN:
- this._keyEvent( "next", event );
- break;
- }
- },
- input: function( event ) {
- if ( suppressInput ) {
- suppressInput = false;
- event.preventDefault();
- return;
- }
- this._searchTimeout( event );
- },
- focus: function() {
- this.selectedItem = null;
- this.previous = this._value();
- },
- blur: function( event ) {
- if ( this.cancelBlur ) {
- delete this.cancelBlur;
- return;
- }
-
- clearTimeout( this.searching );
- this.close( event );
- this._change( event );
- }
- });
-
- this._initSource();
- this.menu = $( "<ul>" )
- .addClass( "ui-autocomplete ui-front" )
- .appendTo( this._appendTo() )
- .menu({
- // custom key handling for now
- input: $(),
- // disable ARIA support, the live region takes care of that
- role: null
- })
- .hide()
- .data( "ui-menu" );
-
- this._on( this.menu.element, {
- mousedown: function( event ) {
- // prevent moving focus out of the text field
- event.preventDefault();
-
- // IE doesn't prevent moving focus even with event.preventDefault()
- // so we set a flag to know when we should ignore the blur event
- this.cancelBlur = true;
- this._delay(function() {
- delete this.cancelBlur;
- });
-
- // clicking on the scrollbar causes focus to shift to the body
- // but we can't detect a mouseup or a click immediately afterward
- // so we have to track the next mousedown and close the menu if
- // the user clicks somewhere outside of the autocomplete
- var menuElement = this.menu.element[ 0 ];
- if ( !$( event.target ).closest( ".ui-menu-item" ).length ) {
- this._delay(function() {
- var that = this;
- this.document.one( "mousedown", function( event ) {
- if ( event.target !== that.element[ 0 ] &&
- event.target !== menuElement &&
- !$.contains( menuElement, event.target ) ) {
- that.close();
- }
- });
- });
- }
- },
- menufocus: function( event, ui ) {
- // #7024 - Prevent accidental activation of menu items in Firefox
- if ( this.isNewMenu ) {
- this.isNewMenu = false;
- if ( event.originalEvent && /^mouse/.test( event.originalEvent.type ) ) {
- this.menu.blur();
-
- this.document.one( "mousemove", function() {
- $( event.target ).trigger( event.originalEvent );
- });
-
- return;
- }
- }
-
- var item = ui.item.data( "ui-autocomplete-item" );
- if ( false !== this._trigger( "focus", event, { item: item } ) ) {
- // use value to match what will end up in the input, if it was a key event
- if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
- this._value( item.value );
- }
- } else {
- // Normally the input is populated with the item's value as the
- // menu is navigated, causing screen readers to notice a change and
- // announce the item. Since the focus event was canceled, this doesn't
- // happen, so we update the live region so that screen readers can
- // still notice the change and announce it.
- this.liveRegion.text( item.value );
- }
- },
- menuselect: function( event, ui ) {
- var item = ui.item.data( "ui-autocomplete-item" ),
- previous = this.previous;
-
- // only trigger when focus was lost (click on menu)
- if ( this.element[0] !== this.document[0].activeElement ) {
- this.element.focus();
- this.previous = previous;
- // #6109 - IE triggers two focus events and the second
- // is asynchronous, so we need to reset the previous
- // term synchronously and asynchronously :-(
- this._delay(function() {
- this.previous = previous;
- this.selectedItem = item;
- });
- }
-
- if ( false !== this._trigger( "select", event, { item: item } ) ) {
- this._value( item.value );
- }
- // reset the term after the select event
- // this allows custom select handling to work properly
- this.term = this._value();
-
- this.close( event );
- this.selectedItem = item;
- }
- });
-
- this.liveRegion = $( "<span>", {
- role: "status",
- "aria-live": "polite"
- })
- .addClass( "ui-helper-hidden-accessible" )
- .insertAfter( this.element );
-
- // turning off autocomplete prevents the browser from remembering the
- // value when navigating through history, so we re-enable autocomplete
- // if the page is unloaded before the widget is destroyed. #7790
- this._on( this.window, {
- beforeunload: function() {
- this.element.removeAttr( "autocomplete" );
- }
- });
- },
-
- _destroy: function() {
- clearTimeout( this.searching );
- this.element
- .removeClass( "ui-autocomplete-input" )
- .removeAttr( "autocomplete" );
- this.menu.element.remove();
- this.liveRegion.remove();
- },
-
- _setOption: function( key, value ) {
- this._super( key, value );
- if ( key === "source" ) {
- this._initSource();
- }
- if ( key === "appendTo" ) {
- this.menu.element.appendTo( this._appendTo() );
- }
- if ( key === "disabled" && value && this.xhr ) {
- this.xhr.abort();
- }
- },
-
- _appendTo: function() {
- var element = this.options.appendTo;
-
- if ( element ) {
- element = element.jquery || element.nodeType ?
- $( element ) :
- this.document.find( element ).eq( 0 );
- }
-
- if ( !element ) {
- element = this.element.closest( ".ui-front" );
- }
-
- if ( !element.length ) {
- element = this.document[0].body;
- }
-
- return element;
- },
-
- _initSource: function() {
- var array, url,
- that = this;
- if ( $.isArray(this.options.source) ) {
- array = this.options.source;
- this.source = function( request, response ) {
- response( $.ui.autocomplete.filter( array, request.term ) );
- };
- } else if ( typeof this.options.source === "string" ) {
- url = this.options.source;
- this.source = function( request, response ) {
- if ( that.xhr ) {
- that.xhr.abort();
- }
- that.xhr = $.ajax({
- url: url,
- data: request,
- dataType: "json",
- success: function( data ) {
- response( data );
- },
- error: function() {
- response( [] );
- }
- });
- };
- } else {
- this.source = this.options.source;
- }
- },
-
- _searchTimeout: function( event ) {
- clearTimeout( this.searching );
- this.searching = this._delay(function() {
- // only search if the value has changed
- if ( this.term !== this._value() ) {
- this.selectedItem = null;
- this.search( null, event );
- }
- }, this.options.delay );
- },
-
- search: function( value, event ) {
- value = value != null ? value : this._value();
-
- // always save the actual value, not the one passed as an argument
- this.term = this._value();
-
- if ( value.length < this.options.minLength ) {
- return this.close( event );
- }
-
- if ( this._trigger( "search", event ) === false ) {
- return;
- }
-
- return this._search( value );
- },
-
- _search: function( value ) {
- this.pending++;
- this.element.addClass( "ui-autocomplete-loading" );
- this.cancelSearch = false;
-
- this.source( { term: value }, this._response() );
- },
-
- _response: function() {
- var that = this,
- index = ++requestIndex;
-
- return function( content ) {
- if ( index === requestIndex ) {
- that.__response( content );
- }
-
- that.pending--;
- if ( !that.pending ) {
- that.element.removeClass( "ui-autocomplete-loading" );
- }
- };
- },
-
- __response: function( content ) {
- if ( content ) {
- content = this._normalize( content );
- }
- this._trigger( "response", null, { content: content } );
- if ( !this.options.disabled && content && content.length && !this.cancelSearch ) {
- this._suggest( content );
- this._trigger( "open" );
- } else {
- // use ._close() instead of .close() so we don't cancel future searches
- this._close();
- }
- },
-
- close: function( event ) {
- this.cancelSearch = true;
- this._close( event );
- },
-
- _close: function( event ) {
- if ( this.menu.element.is( ":visible" ) ) {
- this.menu.element.hide();
- this.menu.blur();
- this.isNewMenu = true;
- this._trigger( "close", event );
- }
- },
-
- _change: function( event ) {
- if ( this.previous !== this._value() ) {
- this._trigger( "change", event, { item: this.selectedItem } );
- }
- },
-
- _normalize: function( items ) {
- // assume all items have the right format when the first item is complete
- if ( items.length && items[0].label && items[0].value ) {
- return items;
- }
- return $.map( items, function( item ) {
- if ( typeof item === "string" ) {
- return {
- label: item,
- value: item
- };
- }
- return $.extend({
- label: item.label || item.value,
- value: item.value || item.label
- }, item );
- });
- },
-
- _suggest: function( items ) {
- var ul = this.menu.element.empty();
- this._renderMenu( ul, items );
- this.menu.refresh();
-
- // size and position menu
- ul.show();
- this._resizeMenu();
- ul.position( $.extend({
- of: this.element
- }, this.options.position ));
-
- if ( this.options.autoFocus ) {
- this.menu.next();
- }
- },
-
- _resizeMenu: function() {
- var ul = this.menu.element;
- ul.outerWidth( Math.max(
- // Firefox wraps long text (possibly a rounding bug)
- // so we add 1px to avoid the wrapping (#7513)
- ul.width( "" ).outerWidth() + 1,
- this.element.outerWidth()
- ) );
- },
-
- _renderMenu: function( ul, items ) {
- var that = this;
- $.each( items, function( index, item ) {
- that._renderItemData( ul, item );
- });
- },
-
- _renderItemData: function( ul, item ) {
- return this._renderItem( ul, item ).data( "ui-autocomplete-item", item );
- },
-
- _renderItem: function( ul, item ) {
- return $( "<li>" )
- .append( $( "<a>" ).text( item.label ) )
- .appendTo( ul );
- },
-
- _move: function( direction, event ) {
- if ( !this.menu.element.is( ":visible" ) ) {
- this.search( null, event );
- return;
- }
- if ( this.menu.isFirstItem() && /^previous/.test( direction ) ||
- this.menu.isLastItem() && /^next/.test( direction ) ) {
- this._value( this.term );
- this.menu.blur();
- return;
- }
- this.menu[ direction ]( event );
- },
-
- widget: function() {
- return this.menu.element;
- },
-
- _value: function() {
- return this.valueMethod.apply( this.element, arguments );
- },
-
- _keyEvent: function( keyEvent, event ) {
- if ( !this.isMultiLine || this.menu.element.is( ":visible" ) ) {
- this._move( keyEvent, event );
-
- // prevents moving cursor to beginning/end of the text field in some browsers
- event.preventDefault();
- }
- }
-});
-
-$.extend( $.ui.autocomplete, {
- escapeRegex: function( value ) {
- return value.replace(/[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&");
- },
- filter: function(array, term) {
- var matcher = new RegExp( $.ui.autocomplete.escapeRegex(term), "i" );
- return $.grep( array, function(value) {
- return matcher.test( value.label || value.value || value );
- });
- }
-});
-
-
-// live region extension, adding a `messages` option
-// NOTE: This is an experimental API. We are still investigating
-// a full solution for string manipulation and internationalization.
-$.widget( "ui.autocomplete", $.ui.autocomplete, {
- options: {
- messages: {
- noResults: "No search results.",
- results: function( amount ) {
- return amount + ( amount > 1 ? " results are" : " result is" ) +
- " available, use up and down arrow keys to navigate.";
- }
- }
- },
-
- __response: function( content ) {
- var message;
- this._superApply( arguments );
- if ( this.options.disabled || this.cancelSearch ) {
- return;
- }
- if ( content && content.length ) {
- message = this.options.messages.results( content.length );
- } else {
- message = this.options.messages.noResults;
- }
- this.liveRegion.text( message );
- }
-});
-
-}( jQuery ));
-
-(function( $, undefined ) {
-
-var lastActive, startXPos, startYPos, clickDragged,
- baseClasses = "ui-button ui-widget ui-state-default ui-corner-all",
- stateClasses = "ui-state-hover ui-state-active ",
- typeClasses = "ui-button-icons-only ui-button-icon-only ui-button-text-icons ui-button-text-icon-primary ui-button-text-icon-secondary ui-button-text-only",
- formResetHandler = function() {
- var buttons = $( this ).find( ":ui-button" );
- setTimeout(function() {
- buttons.button( "refresh" );
- }, 1 );
- },
- radioGroup = function( radio ) {
- var name = radio.name,
- form = radio.form,
- radios = $( [] );
- if ( name ) {
- name = name.replace( /'/g, "\\'" );
- if ( form ) {
- radios = $( form ).find( "[name='" + name + "']" );
- } else {
- radios = $( "[name='" + name + "']", radio.ownerDocument )
- .filter(function() {
- return !this.form;
- });
- }
- }
- return radios;
- };
-
-$.widget( "ui.button", {
- version: "1.10.1",
- defaultElement: "<button>",
- options: {
- disabled: null,
- text: true,
- label: null,
- icons: {
- primary: null,
- secondary: null
- }
- },
- _create: function() {
- this.element.closest( "form" )
- .unbind( "reset" + this.eventNamespace )
- .bind( "reset" + this.eventNamespace, formResetHandler );
-
- if ( typeof this.options.disabled !== "boolean" ) {
- this.options.disabled = !!this.element.prop( "disabled" );
- } else {
- this.element.prop( "disabled", this.options.disabled );
- }
-
- this._determineButtonType();
- this.hasTitle = !!this.buttonElement.attr( "title" );
-
- var that = this,
- options = this.options,
- toggleButton = this.type === "checkbox" || this.type === "radio",
- activeClass = !toggleButton ? "ui-state-active" : "",
- focusClass = "ui-state-focus";
-
- if ( options.label === null ) {
- options.label = (this.type === "input" ? this.buttonElement.val() : this.buttonElement.html());
- }
-
- this._hoverable( this.buttonElement );
-
- this.buttonElement
- .addClass( baseClasses )
- .attr( "role", "button" )
- .bind( "mouseenter" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return;
- }
- if ( this === lastActive ) {
- $( this ).addClass( "ui-state-active" );
- }
- })
- .bind( "mouseleave" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return;
- }
- $( this ).removeClass( activeClass );
- })
- .bind( "click" + this.eventNamespace, function( event ) {
- if ( options.disabled ) {
- event.preventDefault();
- event.stopImmediatePropagation();
- }
- });
-
- this.element
- .bind( "focus" + this.eventNamespace, function() {
- // no need to check disabled, focus won't be triggered anyway
- that.buttonElement.addClass( focusClass );
- })
- .bind( "blur" + this.eventNamespace, function() {
- that.buttonElement.removeClass( focusClass );
- });
-
- if ( toggleButton ) {
- this.element.bind( "change" + this.eventNamespace, function() {
- if ( clickDragged ) {
- return;
- }
- that.refresh();
- });
- // if mouse moves between mousedown and mouseup (drag) set clickDragged flag
- // prevents issue where button state changes but checkbox/radio checked state
- // does not in Firefox (see ticket #6970)
- this.buttonElement
- .bind( "mousedown" + this.eventNamespace, function( event ) {
- if ( options.disabled ) {
- return;
- }
- clickDragged = false;
- startXPos = event.pageX;
- startYPos = event.pageY;
- })
- .bind( "mouseup" + this.eventNamespace, function( event ) {
- if ( options.disabled ) {
- return;
- }
- if ( startXPos !== event.pageX || startYPos !== event.pageY ) {
- clickDragged = true;
- }
- });
- }
-
- if ( this.type === "checkbox" ) {
- this.buttonElement.bind( "click" + this.eventNamespace, function() {
- if ( options.disabled || clickDragged ) {
- return false;
- }
- });
- } else if ( this.type === "radio" ) {
- this.buttonElement.bind( "click" + this.eventNamespace, function() {
- if ( options.disabled || clickDragged ) {
- return false;
- }
- $( this ).addClass( "ui-state-active" );
- that.buttonElement.attr( "aria-pressed", "true" );
-
- var radio = that.element[ 0 ];
- radioGroup( radio )
- .not( radio )
- .map(function() {
- return $( this ).button( "widget" )[ 0 ];
- })
- .removeClass( "ui-state-active" )
- .attr( "aria-pressed", "false" );
- });
- } else {
- this.buttonElement
- .bind( "mousedown" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return false;
- }
- $( this ).addClass( "ui-state-active" );
- lastActive = this;
- that.document.one( "mouseup", function() {
- lastActive = null;
- });
- })
- .bind( "mouseup" + this.eventNamespace, function() {
- if ( options.disabled ) {
- return false;
- }
- $( this ).removeClass( "ui-state-active" );
- })
- .bind( "keydown" + this.eventNamespace, function(event) {
- if ( options.disabled ) {
- return false;
- }
- if ( event.keyCode === $.ui.keyCode.SPACE || event.keyCode === $.ui.keyCode.ENTER ) {
- $( this ).addClass( "ui-state-active" );
- }
- })
- // see #8559, we bind to blur here in case the button element loses
- // focus between keydown and keyup, it would be left in an "active" state
- .bind( "keyup" + this.eventNamespace + " blur" + this.eventNamespace, function() {
- $( this ).removeClass( "ui-state-active" );
- });
-
- if ( this.buttonElement.is("a") ) {
- this.buttonElement.keyup(function(event) {
- if ( event.keyCode === $.ui.keyCode.SPACE ) {
- // TODO pass through original event correctly (just as 2nd argument doesn't work)
- $( this ).click();
- }
- });
- }
- }
-
- // TODO: pull out $.Widget's handling for the disabled option into
- // $.Widget.prototype._setOptionDisabled so it's easy to proxy and can
- // be overridden by individual plugins
- this._setOption( "disabled", options.disabled );
- this._resetButton();
- },
-
- _determineButtonType: function() {
- var ancestor, labelSelector, checked;
-
- if ( this.element.is("[type=checkbox]") ) {
- this.type = "checkbox";
- } else if ( this.element.is("[type=radio]") ) {
- this.type = "radio";
- } else if ( this.element.is("input") ) {
- this.type = "input";
- } else {
- this.type = "button";
- }
-
- if ( this.type === "checkbox" || this.type === "radio" ) {
- // we don't search against the document in case the element
- // is disconnected from the DOM
- ancestor = this.element.parents().last();
- labelSelector = "label[for='" + this.element.attr("id") + "']";
- this.buttonElement = ancestor.find( labelSelector );
- if ( !this.buttonElement.length ) {
- ancestor = ancestor.length ? ancestor.siblings() : this.element.siblings();
- this.buttonElement = ancestor.filter( labelSelector );
- if ( !this.buttonElement.length ) {
- this.buttonElement = ancestor.find( labelSelector );
- }
- }
- this.element.addClass( "ui-helper-hidden-accessible" );
-
- checked = this.element.is( ":checked" );
- if ( checked ) {
- this.buttonElement.addClass( "ui-state-active" );
- }
- this.buttonElement.prop( "aria-pressed", checked );
- } else {
- this.buttonElement = this.element;
- }
- },
-
- widget: function() {
- return this.buttonElement;
- },
-
- _destroy: function() {
- this.element
- .removeClass( "ui-helper-hidden-accessible" );
- this.buttonElement
- .removeClass( baseClasses + " " + stateClasses + " " + typeClasses )
- .removeAttr( "role" )
- .removeAttr( "aria-pressed" )
- .html( this.buttonElement.find(".ui-button-text").html() );
-
- if ( !this.hasTitle ) {
- this.buttonElement.removeAttr( "title" );
- }
- },
-
- _setOption: function( key, value ) {
- this._super( key, value );
- if ( key === "disabled" ) {
- if ( value ) {
- this.element.prop( "disabled", true );
- } else {
- this.element.prop( "disabled", false );
- }
- return;
- }
- this._resetButton();
- },
-
- refresh: function() {
- //See #8237 & #8828
- var isDisabled = this.element.is( "input, button" ) ? this.element.is( ":disabled" ) : this.element.hasClass( "ui-button-disabled" );
-
- if ( isDisabled !== this.options.disabled ) {
- this._setOption( "disabled", isDisabled );
- }
- if ( this.type === "radio" ) {
- radioGroup( this.element[0] ).each(function() {
- if ( $( this ).is( ":checked" ) ) {
- $( this ).button( "widget" )
- .addClass( "ui-state-active" )
- .attr( "aria-pressed", "true" );
- } else {
- $( this ).button( "widget" )
- .removeClass( "ui-state-active" )
- .attr( "aria-pressed", "false" );
- }
- });
- } else if ( this.type === "checkbox" ) {
- if ( this.element.is( ":checked" ) ) {
- this.buttonElement
- .addClass( "ui-state-active" )
- .attr( "aria-pressed", "true" );
- } else {
- this.buttonElement
- .removeClass( "ui-state-active" )
- .attr( "aria-pressed", "false" );
- }
- }
- },
-
- _resetButton: function() {
- if ( this.type === "input" ) {
- if ( this.options.label ) {
- this.element.val( this.options.label );
- }
- return;
- }
- var buttonElement = this.buttonElement.removeClass( typeClasses ),
- buttonText = $( "<span></span>", this.document[0] )
- .addClass( "ui-button-text" )
- .html( this.options.label )
- .appendTo( buttonElement.empty() )
- .text(),
- icons = this.options.icons,
- multipleIcons = icons.primary && icons.secondary,
- buttonClasses = [];
-
- if ( icons.primary || icons.secondary ) {
- if ( this.options.text ) {
- buttonClasses.push( "ui-button-text-icon" + ( multipleIcons ? "s" : ( icons.primary ? "-primary" : "-secondary" ) ) );
- }
-
- if ( icons.primary ) {
- buttonElement.prepend( "<span class='ui-button-icon-primary ui-icon " + icons.primary + "'></span>" );
- }
-
- if ( icons.secondary ) {
- buttonElement.append( "<span class='ui-button-icon-secondary ui-icon " + icons.secondary + "'></span>" );
- }
-
- if ( !this.options.text ) {
- buttonClasses.push( multipleIcons ? "ui-button-icons-only" : "ui-button-icon-only" );
-
- if ( !this.hasTitle ) {
- buttonElement.attr( "title", $.trim( buttonText ) );
- }
- }
- } else {
- buttonClasses.push( "ui-button-text-only" );
- }
- buttonElement.addClass( buttonClasses.join( " " ) );
- }
-});
-
-$.widget( "ui.buttonset", {
- version: "1.10.1",
- options: {
- items: "button, input[type=button], input[type=submit], input[type=reset], input[type=checkbox], input[type=radio], a, :data(ui-button)"
- },
-
- _create: function() {
- this.element.addClass( "ui-buttonset" );
- },
-
- _init: function() {
- this.refresh();
- },
-
- _setOption: function( key, value ) {
- if ( key === "disabled" ) {
- this.buttons.button( "option", key, value );
- }
-
- this._super( key, value );
- },
-
- refresh: function() {
- var rtl = this.element.css( "direction" ) === "rtl";
-
- this.buttons = this.element.find( this.options.items )
- .filter( ":ui-button" )
- .button( "refresh" )
- .end()
- .not( ":ui-button" )
- .button()
- .end()
- .map(function() {
- return $( this ).button( "widget" )[ 0 ];
- })
- .removeClass( "ui-corner-all ui-corner-left ui-corner-right" )
- .filter( ":first" )
- .addClass( rtl ? "ui-corner-right" : "ui-corner-left" )
- .end()
- .filter( ":last" )
- .addClass( rtl ? "ui-corner-left" : "ui-corner-right" )
- .end()
- .end();
- },
-
- _destroy: function() {
- this.element.removeClass( "ui-buttonset" );
- this.buttons
- .map(function() {
- return $( this ).button( "widget" )[ 0 ];
- })
- .removeClass( "ui-corner-left ui-corner-right" )
- .end()
- .button( "destroy" );
- }
-});
-
-}( jQuery ) );
-
-(function( $, undefined ) {
-
-$.extend($.ui, { datepicker: { version: "1.10.1" } });
-
-var PROP_NAME = "datepicker",
- dpuuid = new Date().getTime(),
- instActive;
-
-/* Date picker manager.
- Use the singleton instance of this class, $.datepicker, to interact with the date picker.
- Settings for (groups of) date pickers are maintained in an instance object,
- allowing multiple different settings on the same page. */
-
-function Datepicker() {
- this._curInst = null; // The current instance in use
- this._keyEvent = false; // If the last event was a key event
- this._disabledInputs = []; // List of date picker inputs that have been disabled
- this._datepickerShowing = false; // True if the popup picker is showing , false if not
- this._inDialog = false; // True if showing within a "dialog", false if not
- this._mainDivId = "ui-datepicker-div"; // The ID of the main datepicker division
- this._inlineClass = "ui-datepicker-inline"; // The name of the inline marker class
- this._appendClass = "ui-datepicker-append"; // The name of the append marker class
- this._triggerClass = "ui-datepicker-trigger"; // The name of the trigger marker class
- this._dialogClass = "ui-datepicker-dialog"; // The name of the dialog marker class
- this._disableClass = "ui-datepicker-disabled"; // The name of the disabled covering marker class
- this._unselectableClass = "ui-datepicker-unselectable"; // The name of the unselectable cell marker class
- this._currentClass = "ui-datepicker-current-day"; // The name of the current day marker class
- this._dayOverClass = "ui-datepicker-days-cell-over"; // The name of the day hover marker class
- this.regional = []; // Available regional settings, indexed by language code
- this.regional[""] = { // Default regional settings
- closeText: "Done", // Display text for close link
- prevText: "Prev", // Display text for previous month link
- nextText: "Next", // Display text for next month link
- currentText: "Today", // Display text for current month link
- monthNames: ["January","February","March","April","May","June",
- "July","August","September","October","November","December"], // Names of months for drop-down and formatting
- monthNamesShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], // For formatting
- dayNames: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"], // For formatting
- dayNamesShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], // For formatting
- dayNamesMin: ["Su","Mo","Tu","We","Th","Fr","Sa"], // Column headings for days starting at Sunday
- weekHeader: "Wk", // Column header for week of the year
- dateFormat: "mm/dd/yy", // See format options on parseDate
- firstDay: 0, // The first day of the week, Sun = 0, Mon = 1, ...
- isRTL: false, // True if right-to-left language, false if left-to-right
- showMonthAfterYear: false, // True if the year select precedes month, false for month then year
- yearSuffix: "" // Additional text to append to the year in the month headers
- };
- this._defaults = { // Global defaults for all the date picker instances
- showOn: "focus", // "focus" for popup on focus,
- // "button" for trigger button, or "both" for either
- showAnim: "fadeIn", // Name of jQuery animation for popup
- showOptions: {}, // Options for enhanced animations
- defaultDate: null, // Used when field is blank: actual date,
- // +/-number for offset from today, null for today
- appendText: "", // Display text following the input box, e.g. showing the format
- buttonText: "...", // Text for trigger button
- buttonImage: "", // URL for trigger button image
- buttonImageOnly: false, // True if the image appears alone, false if it appears on a button
- hideIfNoPrevNext: false, // True to hide next/previous month links
- // if not applicable, false to just disable them
- navigationAsDateFormat: false, // True if date formatting applied to prev/today/next links
- gotoCurrent: false, // True if today link goes back to current selection instead
- changeMonth: false, // True if month can be selected directly, false if only prev/next
- changeYear: false, // True if year can be selected directly, false if only prev/next
- yearRange: "c-10:c+10", // Range of years to display in drop-down,
- // either relative to today's year (-nn:+nn), relative to currently displayed year
- // (c-nn:c+nn), absolute (nnnn:nnnn), or a combination of the above (nnnn:-n)
- showOtherMonths: false, // True to show dates in other months, false to leave blank
- selectOtherMonths: false, // True to allow selection of dates in other months, false for unselectable
- showWeek: false, // True to show week of the year, false to not show it
- calculateWeek: this.iso8601Week, // How to calculate the week of the year,
- // takes a Date and returns the number of the week for it
- shortYearCutoff: "+10", // Short year values < this are in the current century,
- // > this are in the previous century,
- // string value starting with "+" for current year + value
- minDate: null, // The earliest selectable date, or null for no limit
- maxDate: null, // The latest selectable date, or null for no limit
- duration: "fast", // Duration of display/closure
- beforeShowDay: null, // Function that takes a date and returns an array with
- // [0] = true if selectable, false if not, [1] = custom CSS class name(s) or "",
- // [2] = cell title (optional), e.g. $.datepicker.noWeekends
- beforeShow: null, // Function that takes an input field and
- // returns a set of custom settings for the date picker
- onSelect: null, // Define a callback function when a date is selected
- onChangeMonthYear: null, // Define a callback function when the month or year is changed
- onClose: null, // Define a callback function when the datepicker is closed
- numberOfMonths: 1, // Number of months to show at a time
- showCurrentAtPos: 0, // The position in multipe months at which to show the current month (starting at 0)
- stepMonths: 1, // Number of months to step back/forward
- stepBigMonths: 12, // Number of months to step back/forward for the big links
- altField: "", // Selector for an alternate field to store selected dates into
- altFormat: "", // The date format to use for the alternate field
- constrainInput: true, // The input is constrained by the current date format
- showButtonPanel: false, // True to show button panel, false to not show it
- autoSize: false, // True to size the input for the date format, false to leave as is
- disabled: false // The initial disabled state
- };
- $.extend(this._defaults, this.regional[""]);
- this.dpDiv = bindHover($("<div id='" + this._mainDivId + "' class='ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>"));
-}
-
-$.extend(Datepicker.prototype, {
- /* Class name added to elements to indicate already configured with a date picker. */
- markerClassName: "hasDatepicker",
-
- //Keep track of the maximum number of rows displayed (see #7043)
- maxRows: 4,
-
- // TODO rename to "widget" when switching to widget factory
- _widgetDatepicker: function() {
- return this.dpDiv;
- },
-
- /* Override the default settings for all instances of the date picker.
- * @param settings object - the new settings to use as defaults (anonymous object)
- * @return the manager object
- */
- setDefaults: function(settings) {
- extendRemove(this._defaults, settings || {});
- return this;
- },
-
- /* Attach the date picker to a jQuery selection.
- * @param target element - the target input field or division or span
- * @param settings object - the new settings to use for this date picker instance (anonymous)
- */
- _attachDatepicker: function(target, settings) {
- var nodeName, inline, inst;
- nodeName = target.nodeName.toLowerCase();
- inline = (nodeName === "div" || nodeName === "span");
- if (!target.id) {
- this.uuid += 1;
- target.id = "dp" + this.uuid;
- }
- inst = this._newInst($(target), inline);
- inst.settings = $.extend({}, settings || {});
- if (nodeName === "input") {
- this._connectDatepicker(target, inst);
- } else if (inline) {
- this._inlineDatepicker(target, inst);
- }
- },
-
- /* Create a new instance object. */
- _newInst: function(target, inline) {
- var id = target[0].id.replace(/([^A-Za-z0-9_\-])/g, "\\\\$1"); // escape jQuery meta chars
- return {id: id, input: target, // associated target
- selectedDay: 0, selectedMonth: 0, selectedYear: 0, // current selection
- drawMonth: 0, drawYear: 0, // month being drawn
- inline: inline, // is datepicker inline or not
- dpDiv: (!inline ? this.dpDiv : // presentation div
- bindHover($("<div class='" + this._inlineClass + " ui-datepicker ui-widget ui-widget-content ui-helper-clearfix ui-corner-all'></div>")))};
- },
-
- /* Attach the date picker to an input field. */
- _connectDatepicker: function(target, inst) {
- var input = $(target);
- inst.append = $([]);
- inst.trigger = $([]);
- if (input.hasClass(this.markerClassName)) {
- return;
- }
- this._attachments(input, inst);
- input.addClass(this.markerClassName).keydown(this._doKeyDown).
- keypress(this._doKeyPress).keyup(this._doKeyUp);
- this._autoSize(inst);
- $.data(target, PROP_NAME, inst);
- //If disabled option is true, disable the datepicker once it has been attached to the input (see ticket #5665)
- if( inst.settings.disabled ) {
- this._disableDatepicker( target );
- }
- },
-
- /* Make attachments based on settings. */
- _attachments: function(input, inst) {
- var showOn, buttonText, buttonImage,
- appendText = this._get(inst, "appendText"),
- isRTL = this._get(inst, "isRTL");
-
- if (inst.append) {
- inst.append.remove();
- }
- if (appendText) {
- inst.append = $("<span class='" + this._appendClass + "'>" + appendText + "</span>");
- input[isRTL ? "before" : "after"](inst.append);
- }
-
- input.unbind("focus", this._showDatepicker);
-
- if (inst.trigger) {
- inst.trigger.remove();
- }
-
- showOn = this._get(inst, "showOn");
- if (showOn === "focus" || showOn === "both") { // pop-up date picker when in the marked field
- input.focus(this._showDatepicker);
- }
- if (showOn === "button" || showOn === "both") { // pop-up date picker when button clicked
- buttonText = this._get(inst, "buttonText");
- buttonImage = this._get(inst, "buttonImage");
- inst.trigger = $(this._get(inst, "buttonImageOnly") ?
- $("<img/>").addClass(this._triggerClass).
- attr({ src: buttonImage, alt: buttonText, title: buttonText }) :
- $("<button type='button'></button>").addClass(this._triggerClass).
- html(!buttonImage ? buttonText : $("<img/>").attr(
- { src:buttonImage, alt:buttonText, title:buttonText })));
- input[isRTL ? "before" : "after"](inst.trigger);
- inst.trigger.click(function() {
- if ($.datepicker._datepickerShowing && $.datepicker._lastInput === input[0]) {
- $.datepicker._hideDatepicker();
- } else if ($.datepicker._datepickerShowing && $.datepicker._lastInput !== input[0]) {
- $.datepicker._hideDatepicker();
- $.datepicker._showDatepicker(input[0]);
- } else {
- $.datepicker._showDatepicker(input[0]);
- }
- return false;
- });
- }
- },
-
- /* Apply the maximum length for the date format. */
- _autoSize: function(inst) {
- if (this._get(inst, "autoSize") && !inst.inline) {
- var findMax, max, maxI, i,
- date = new Date(2009, 12 - 1, 20), // Ensure double digits
- dateFormat = this._get(inst, "dateFormat");
-
- if (dateFormat.match(/[DM]/)) {
- findMax = function(names) {
- max = 0;
- maxI = 0;
- for (i = 0; i < names.length; i++) {
- if (names[i].length > max) {
- max = names[i].length;
- maxI = i;
- }
- }
- return maxI;
- };
- date.setMonth(findMax(this._get(inst, (dateFormat.match(/MM/) ?
- "monthNames" : "monthNamesShort"))));
- date.setDate(findMax(this._get(inst, (dateFormat.match(/DD/) ?
- "dayNames" : "dayNamesShort"))) + 20 - date.getDay());
- }
- inst.input.attr("size", this._formatDate(inst, date).length);
- }
- },
-
- /* Attach an inline date picker to a div. */
- _inlineDatepicker: function(target, inst) {
- var divSpan = $(target);
- if (divSpan.hasClass(this.markerClassName)) {
- return;
- }
- divSpan.addClass(this.markerClassName).append(inst.dpDiv);
- $.data(target, PROP_NAME, inst);
- this._setDate(inst, this._getDefaultDate(inst), true);
- this._updateDatepicker(inst);
- this._updateAlternate(inst);
- //If disabled option is true, disable the datepicker before showing it (see ticket #5665)
- if( inst.settings.disabled ) {
- this._disableDatepicker( target );
- }
- // Set display:block in place of inst.dpDiv.show() which won't work on disconnected elements
- // http://bugs.jqueryui.com/ticket/7552 - A Datepicker created on a detached div has zero height
- inst.dpDiv.css( "display", "block" );
- },
-
- /* Pop-up the date picker in a "dialog" box.
- * @param input element - ignored
- * @param date string or Date - the initial date to display
- * @param onSelect function - the function to call when a date is selected
- * @param settings object - update the dialog date picker instance's settings (anonymous object)
- * @param pos int[2] - coordinates for the dialog's position within the screen or
- * event - with x/y coordinates or
- * leave empty for default (screen centre)
- * @return the manager object
- */
- _dialogDatepicker: function(input, date, onSelect, settings, pos) {
- var id, browserWidth, browserHeight, scrollX, scrollY,
- inst = this._dialogInst; // internal instance
-
- if (!inst) {
- this.uuid += 1;
- id = "dp" + this.uuid;
- this._dialogInput = $("<input type='text' id='" + id +
- "' style='position: absolute; top: -100px; width: 0px;'/>");
- this._dialogInput.keydown(this._doKeyDown);
- $("body").append(this._dialogInput);
- inst = this._dialogInst = this._newInst(this._dialogInput, false);
- inst.settings = {};
- $.data(this._dialogInput[0], PROP_NAME, inst);
- }
- extendRemove(inst.settings, settings || {});
- date = (date && date.constructor === Date ? this._formatDate(inst, date) : date);
- this._dialogInput.val(date);
-
- this._pos = (pos ? (pos.length ? pos : [pos.pageX, pos.pageY]) : null);
- if (!this._pos) {
- browserWidth = document.documentElement.clientWidth;
- browserHeight = document.documentElement.clientHeight;
- scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
- scrollY = document.documentElement.scrollTop || document.body.scrollTop;
- this._pos = // should use actual width/height below
- [(browserWidth / 2) - 100 + scrollX, (browserHeight / 2) - 150 + scrollY];
- }
-
- // move input on screen for focus, but hidden behind dialog
- this._dialogInput.css("left", (this._pos[0] + 20) + "px").css("top", this._pos[1] + "px");
- inst.settings.onSelect = onSelect;
- this._inDialog = true;
- this.dpDiv.addClass(this._dialogClass);
- this._showDatepicker(this._dialogInput[0]);
- if ($.blockUI) {
- $.blockUI(this.dpDiv);
- }
- $.data(this._dialogInput[0], PROP_NAME, inst);
- return this;
- },
-
- /* Detach a datepicker from its control.
- * @param target element - the target input field or division or span
- */
- _destroyDatepicker: function(target) {
- var nodeName,
- $target = $(target),
- inst = $.data(target, PROP_NAME);
-
- if (!$target.hasClass(this.markerClassName)) {
- return;
- }
-
- nodeName = target.nodeName.toLowerCase();
- $.removeData(target, PROP_NAME);
- if (nodeName === "input") {
- inst.append.remove();
- inst.trigger.remove();
- $target.removeClass(this.markerClassName).
- unbind("focus", this._showDatepicker).
- unbind("keydown", this._doKeyDown).
- unbind("keypress", this._doKeyPress).
- unbind("keyup", this._doKeyUp);
- } else if (nodeName === "div" || nodeName === "span") {
- $target.removeClass(this.markerClassName).empty();
- }
- },
-
- /* Enable the date picker to a jQuery selection.
- * @param target element - the target input field or division or span
- */
- _enableDatepicker: function(target) {
- var nodeName, inline,
- $target = $(target),
- inst = $.data(target, PROP_NAME);
-
- if (!$target.hasClass(this.markerClassName)) {
- return;
- }
-
- nodeName = target.nodeName.toLowerCase();
- if (nodeName === "input") {
- target.disabled = false;
- inst.trigger.filter("button").
- each(function() { this.disabled = false; }).end().
- filter("img").css({opacity: "1.0", cursor: ""});
- } else if (nodeName === "div" || nodeName === "span") {
- inline = $target.children("." + this._inlineClass);
- inline.children().removeClass("ui-state-disabled");
- inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
- prop("disabled", false);
- }
- this._disabledInputs = $.map(this._disabledInputs,
- function(value) { return (value === target ? null : value); }); // delete entry
- },
-
- /* Disable the date picker to a jQuery selection.
- * @param target element - the target input field or division or span
- */
- _disableDatepicker: function(target) {
- var nodeName, inline,
- $target = $(target),
- inst = $.data(target, PROP_NAME);
-
- if (!$target.hasClass(this.markerClassName)) {
- return;
- }
-
- nodeName = target.nodeName.toLowerCase();
- if (nodeName === "input") {
- target.disabled = true;
- inst.trigger.filter("button").
- each(function() { this.disabled = true; }).end().
- filter("img").css({opacity: "0.5", cursor: "default"});
- } else if (nodeName === "div" || nodeName === "span") {
- inline = $target.children("." + this._inlineClass);
- inline.children().addClass("ui-state-disabled");
- inline.find("select.ui-datepicker-month, select.ui-datepicker-year").
- prop("disabled", true);
- }
- this._disabledInputs = $.map(this._disabledInputs,
- function(value) { return (value === target ? null : value); }); // delete entry
- this._disabledInputs[this._disabledInputs.length] = target;
- },
-
- /* Is the first field in a jQuery collection disabled as a datepicker?
- * @param target element - the target input field or division or span
- * @return boolean - true if disabled, false if enabled
- */
- _isDisabledDatepicker: function(target) {
- if (!target) {
- return false;
- }
- for (var i = 0; i < this._disabledInputs.length; i++) {
- if (this._disabledInputs[i] === target) {
- return true;
- }
- }
- return false;
- },
-
- /* Retrieve the instance data for the target control.
- * @param target element - the target input field or division or span
- * @return object - the associated instance data
- * @throws error if a jQuery problem getting data
- */
- _getInst: function(target) {
- try {
- return $.data(target, PROP_NAME);
- }
- catch (err) {
- throw "Missing instance data for this datepicker";
- }
- },
-
- /* Update or retrieve the settings for a date picker attached to an input field or division.
- * @param target element - the target input field or division or span
- * @param name object - the new settings to update or
- * string - the name of the setting to change or retrieve,
- * when retrieving also "all" for all instance settings or
- * "defaults" for all global defaults
- * @param value any - the new value for the setting
- * (omit if above is an object or to retrieve a value)
- */
- _optionDatepicker: function(target, name, value) {
- var settings, date, minDate, maxDate,
- inst = this._getInst(target);
-
- if (arguments.length === 2 && typeof name === "string") {
- return (name === "defaults" ? $.extend({}, $.datepicker._defaults) :
- (inst ? (name === "all" ? $.extend({}, inst.settings) :
- this._get(inst, name)) : null));
- }
-
- settings = name || {};
- if (typeof name === "string") {
- settings = {};
- settings[name] = value;
- }
-
- if (inst) {
- if (this._curInst === inst) {
- this._hideDatepicker();
- }
-
- date = this._getDateDatepicker(target, true);
- minDate = this._getMinMaxDate(inst, "min");
- maxDate = this._getMinMaxDate(inst, "max");
- extendRemove(inst.settings, settings);
- // reformat the old minDate/maxDate values if dateFormat changes and a new minDate/maxDate isn't provided
- if (minDate !== null && settings.dateFormat !== undefined && settings.minDate === undefined) {
- inst.settings.minDate = this._formatDate(inst, minDate);
- }
- if (maxDate !== null && settings.dateFormat !== undefined && settings.maxDate === undefined) {
- inst.settings.maxDate = this._formatDate(inst, maxDate);
- }
- if ( "disabled" in settings ) {
- if ( settings.disabled ) {
- this._disableDatepicker(target);
- } else {
- this._enableDatepicker(target);
- }
- }
- this._attachments($(target), inst);
- this._autoSize(inst);
- this._setDate(inst, date);
- this._updateAlternate(inst);
- this._updateDatepicker(inst);
- }
- },
-
- // change method deprecated
- _changeDatepicker: function(target, name, value) {
- this._optionDatepicker(target, name, value);
- },
-
- /* Redraw the date picker attached to an input field or division.
- * @param target element - the target input field or division or span
- */
- _refreshDatepicker: function(target) {
- var inst = this._getInst(target);
- if (inst) {
- this._updateDatepicker(inst);
- }
- },
-
- /* Set the dates for a jQuery selection.
- * @param target element - the target input field or division or span
- * @param date Date - the new date
- */
- _setDateDatepicker: function(target, date) {
- var inst = this._getInst(target);
- if (inst) {
- this._setDate(inst, date);
- this._updateDatepicker(inst);
- this._updateAlternate(inst);
- }
- },
-
- /* Get the date(s) for the first entry in a jQuery selection.
- * @param target element - the target input field or division or span
- * @param noDefault boolean - true if no default date is to be used
- * @return Date - the current date
- */
- _getDateDatepicker: function(target, noDefault) {
- var inst = this._getInst(target);
- if (inst && !inst.inline) {
- this._setDateFromField(inst, noDefault);
- }
- return (inst ? this._getDate(inst) : null);
- },
-
- /* Handle keystrokes. */
- _doKeyDown: function(event) {
- var onSelect, dateStr, sel,
- inst = $.datepicker._getInst(event.target),
- handled = true,
- isRTL = inst.dpDiv.is(".ui-datepicker-rtl");
-
- inst._keyEvent = true;
- if ($.datepicker._datepickerShowing) {
- switch (event.keyCode) {
- case 9: $.datepicker._hideDatepicker();
- handled = false;
- break; // hide on tab out
- case 13: sel = $("td." + $.datepicker._dayOverClass + ":not(." +
- $.datepicker._currentClass + ")", inst.dpDiv);
- if (sel[0]) {
- $.datepicker._selectDay(event.target, inst.selectedMonth, inst.selectedYear, sel[0]);
- }
-
- onSelect = $.datepicker._get(inst, "onSelect");
- if (onSelect) {
- dateStr = $.datepicker._formatDate(inst);
-
- // trigger custom callback
- onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]);
- } else {
- $.datepicker._hideDatepicker();
- }
-
- return false; // don't submit the form
- case 27: $.datepicker._hideDatepicker();
- break; // hide on escape
- case 33: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
- -$.datepicker._get(inst, "stepBigMonths") :
- -$.datepicker._get(inst, "stepMonths")), "M");
- break; // previous month/year on page up/+ ctrl
- case 34: $.datepicker._adjustDate(event.target, (event.ctrlKey ?
- +$.datepicker._get(inst, "stepBigMonths") :
- +$.datepicker._get(inst, "stepMonths")), "M");
- break; // next month/year on page down/+ ctrl
- case 35: if (event.ctrlKey || event.metaKey) {
- $.datepicker._clearDate(event.target);
- }
- handled = event.ctrlKey || event.metaKey;
- break; // clear on ctrl or command +end
- case 36: if (event.ctrlKey || event.metaKey) {
- $.datepicker._gotoToday(event.target);
- }
- handled = event.ctrlKey || event.metaKey;
- break; // current on ctrl or command +home
- case 37: if (event.ctrlKey || event.metaKey) {
- $.datepicker._adjustDate(event.target, (isRTL ? +1 : -1), "D");
- }
- handled = event.ctrlKey || event.metaKey;
- // -1 day on ctrl or command +left
- if (event.originalEvent.altKey) {
- $.datepicker._adjustDate(event.target, (event.ctrlKey ?
- -$.datepicker._get(inst, "stepBigMonths") :
- -$.datepicker._get(inst, "stepMonths")), "M");
- }
- // next month/year on alt +left on Mac
- break;
- case 38: if (event.ctrlKey || event.metaKey) {
- $.datepicker._adjustDate(event.target, -7, "D");
- }
- handled = event.ctrlKey || event.metaKey;
- break; // -1 week on ctrl or command +up
- case 39: if (event.ctrlKey || event.metaKey) {
- $.datepicker._adjustDate(event.target, (isRTL ? -1 : +1), "D");
- }
- handled = event.ctrlKey || event.metaKey;
- // +1 day on ctrl or command +right
- if (event.originalEvent.altKey) {
- $.datepicker._adjustDate(event.target, (event.ctrlKey ?
- +$.datepicker._get(inst, "stepBigMonths") :
- +$.datepicker._get(inst, "stepMonths")), "M");
- }
- // next month/year on alt +right
- break;
- case 40: if (event.ctrlKey || event.metaKey) {
- $.datepicker._adjustDate(event.target, +7, "D");
- }
- handled = event.ctrlKey || event.metaKey;
- break; // +1 week on ctrl or command +down
- default: handled = false;
- }
- } else if (event.keyCode === 36 && event.ctrlKey) { // display the date picker on ctrl+home
- $.datepicker._showDatepicker(this);
- } else {
- handled = false;
- }
-
- if (handled) {
- event.preventDefault();
- event.stopPropagation();
- }
- },
-
- /* Filter entered characters - based on date format. */
- _doKeyPress: function(event) {
- var chars, chr,
- inst = $.datepicker._getInst(event.target);
-
- if ($.datepicker._get(inst, "constrainInput")) {
- chars = $.datepicker._possibleChars($.datepicker._get(inst, "dateFormat"));
- chr = String.fromCharCode(event.charCode == null ? event.keyCode : event.charCode);
- return event.ctrlKey || event.metaKey || (chr < " " || !chars || chars.indexOf(chr) > -1);
- }
- },
-
- /* Synchronise manual entry and field/alternate field. */
- _doKeyUp: function(event) {
- var date,
- inst = $.datepicker._getInst(event.target);
-
- if (inst.input.val() !== inst.lastVal) {
- try {
- date = $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
- (inst.input ? inst.input.val() : null),
- $.datepicker._getFormatConfig(inst));
-
- if (date) { // only if valid
- $.datepicker._setDateFromField(inst);
- $.datepicker._updateAlternate(inst);
- $.datepicker._updateDatepicker(inst);
- }
- }
- catch (err) {
- }
- }
- return true;
- },
-
- /* Pop-up the date picker for a given input field.
- * If false returned from beforeShow event handler do not show.
- * @param input element - the input field attached to the date picker or
- * event - if triggered by focus
- */
- _showDatepicker: function(input) {
- input = input.target || input;
- if (input.nodeName.toLowerCase() !== "input") { // find from button/image trigger
- input = $("input", input.parentNode)[0];
- }
-
- if ($.datepicker._isDisabledDatepicker(input) || $.datepicker._lastInput === input) { // already here
- return;
- }
-
- var inst, beforeShow, beforeShowSettings, isFixed,
- offset, showAnim, duration;
-
- inst = $.datepicker._getInst(input);
- if ($.datepicker._curInst && $.datepicker._curInst !== inst) {
- $.datepicker._curInst.dpDiv.stop(true, true);
- if ( inst && $.datepicker._datepickerShowing ) {
- $.datepicker._hideDatepicker( $.datepicker._curInst.input[0] );
- }
- }
-
- beforeShow = $.datepicker._get(inst, "beforeShow");
- beforeShowSettings = beforeShow ? beforeShow.apply(input, [input, inst]) : {};
- if(beforeShowSettings === false){
- return;
- }
- extendRemove(inst.settings, beforeShowSettings);
-
- inst.lastVal = null;
- $.datepicker._lastInput = input;
- $.datepicker._setDateFromField(inst);
-
- if ($.datepicker._inDialog) { // hide cursor
- input.value = "";
- }
- if (!$.datepicker._pos) { // position below input
- $.datepicker._pos = $.datepicker._findPos(input);
- $.datepicker._pos[1] += input.offsetHeight; // add the height
- }
-
- isFixed = false;
- $(input).parents().each(function() {
- isFixed |= $(this).css("position") === "fixed";
- return !isFixed;
- });
-
- offset = {left: $.datepicker._pos[0], top: $.datepicker._pos[1]};
- $.datepicker._pos = null;
- //to avoid flashes on Firefox
- inst.dpDiv.empty();
- // determine sizing offscreen
- inst.dpDiv.css({position: "absolute", display: "block", top: "-1000px"});
- $.datepicker._updateDatepicker(inst);
- // fix width for dynamic number of date pickers
- // and adjust position before showing
- offset = $.datepicker._checkOffset(inst, offset, isFixed);
- inst.dpDiv.css({position: ($.datepicker._inDialog && $.blockUI ?
- "static" : (isFixed ? "fixed" : "absolute")), display: "none",
- left: offset.left + "px", top: offset.top + "px"});
-
- if (!inst.inline) {
- showAnim = $.datepicker._get(inst, "showAnim");
- duration = $.datepicker._get(inst, "duration");
- inst.dpDiv.zIndex($(input).zIndex()+1);
- $.datepicker._datepickerShowing = true;
-
- if ( $.effects && $.effects.effect[ showAnim ] ) {
- inst.dpDiv.show(showAnim, $.datepicker._get(inst, "showOptions"), duration);
- } else {
- inst.dpDiv[showAnim || "show"](showAnim ? duration : null);
- }
-
- if (inst.input.is(":visible") && !inst.input.is(":disabled")) {
- inst.input.focus();
- }
- $.datepicker._curInst = inst;
- }
- },
-
- /* Generate the date picker content. */
- _updateDatepicker: function(inst) {
- this.maxRows = 4; //Reset the max number of rows being displayed (see #7043)
- instActive = inst; // for delegate hover events
- inst.dpDiv.empty().append(this._generateHTML(inst));
- this._attachHandlers(inst);
- inst.dpDiv.find("." + this._dayOverClass + " a").mouseover();
-
- var origyearshtml,
- numMonths = this._getNumberOfMonths(inst),
- cols = numMonths[1],
- width = 17;
-
- inst.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");
- if (cols > 1) {
- inst.dpDiv.addClass("ui-datepicker-multi-" + cols).css("width", (width * cols) + "em");
- }
- inst.dpDiv[(numMonths[0] !== 1 || numMonths[1] !== 1 ? "add" : "remove") +
- "Class"]("ui-datepicker-multi");
- inst.dpDiv[(this._get(inst, "isRTL") ? "add" : "remove") +
- "Class"]("ui-datepicker-rtl");
-
- // #6694 - don't focus the input if it's already focused
- // this breaks the change event in IE
- if (inst === $.datepicker._curInst && $.datepicker._datepickerShowing && inst.input &&
- inst.input.is(":visible") && !inst.input.is(":disabled") && inst.input[0] !== document.activeElement) {
- inst.input.focus();
- }
-
- // deffered render of the years select (to avoid flashes on Firefox)
- if( inst.yearshtml ){
- origyearshtml = inst.yearshtml;
- setTimeout(function(){
- //assure that inst.yearshtml didn't change.
- if( origyearshtml === inst.yearshtml && inst.yearshtml ){
- inst.dpDiv.find("select.ui-datepicker-year:first").replaceWith(inst.yearshtml);
- }
- origyearshtml = inst.yearshtml = null;
- }, 0);
- }
- },
-
- /* Retrieve the size of left and top borders for an element.
- * @param elem (jQuery object) the element of interest
- * @return (number[2]) the left and top borders
- */
- _getBorders: function(elem) {
- var convert = function(value) {
- return {thin: 1, medium: 2, thick: 3}[value] || value;
- };
- return [parseFloat(convert(elem.css("border-left-width"))),
- parseFloat(convert(elem.css("border-top-width")))];
- },
-
- /* Check positioning to remain on screen. */
- _checkOffset: function(inst, offset, isFixed) {
- var dpWidth = inst.dpDiv.outerWidth(),
- dpHeight = inst.dpDiv.outerHeight(),
- inputWidth = inst.input ? inst.input.outerWidth() : 0,
- inputHeight = inst.input ? inst.input.outerHeight() : 0,
- viewWidth = document.documentElement.clientWidth + (isFixed ? 0 : $(document).scrollLeft()),
- viewHeight = document.documentElement.clientHeight + (isFixed ? 0 : $(document).scrollTop());
-
- offset.left -= (this._get(inst, "isRTL") ? (dpWidth - inputWidth) : 0);
- offset.left -= (isFixed && offset.left === inst.input.offset().left) ? $(document).scrollLeft() : 0;
- offset.top -= (isFixed && offset.top === (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
-
- // now check if datepicker is showing outside window viewport - move to a better place if so.
- offset.left -= Math.min(offset.left, (offset.left + dpWidth > viewWidth && viewWidth > dpWidth) ?
- Math.abs(offset.left + dpWidth - viewWidth) : 0);
- offset.top -= Math.min(offset.top, (offset.top + dpHeight > viewHeight && viewHeight > dpHeight) ?
- Math.abs(dpHeight + inputHeight) : 0);
-
- return offset;
- },
-
- /* Find an object's position on the screen. */
- _findPos: function(obj) {
- var position,
- inst = this._getInst(obj),
- isRTL = this._get(inst, "isRTL");
-
- while (obj && (obj.type === "hidden" || obj.nodeType !== 1 || $.expr.filters.hidden(obj))) {
- obj = obj[isRTL ? "previousSibling" : "nextSibling"];
- }
-
- position = $(obj).offset();
- return [position.left, position.top];
- },
-
- /* Hide the date picker from view.
- * @param input element - the input field attached to the date picker
- */
- _hideDatepicker: function(input) {
- var showAnim, duration, postProcess, onClose,
- inst = this._curInst;
-
- if (!inst || (input && inst !== $.data(input, PROP_NAME))) {
- return;
- }
-
- if (this._datepickerShowing) {
- showAnim = this._get(inst, "showAnim");
- duration = this._get(inst, "duration");
- postProcess = function() {
- $.datepicker._tidyDialog(inst);
- };
-
- // DEPRECATED: after BC for 1.8.x $.effects[ showAnim ] is not needed
- if ( $.effects && ( $.effects.effect[ showAnim ] || $.effects[ showAnim ] ) ) {
- inst.dpDiv.hide(showAnim, $.datepicker._get(inst, "showOptions"), duration, postProcess);
- } else {
- inst.dpDiv[(showAnim === "slideDown" ? "slideUp" :
- (showAnim === "fadeIn" ? "fadeOut" : "hide"))]((showAnim ? duration : null), postProcess);
- }
-
- if (!showAnim) {
- postProcess();
- }
- this._datepickerShowing = false;
-
- onClose = this._get(inst, "onClose");
- if (onClose) {
- onClose.apply((inst.input ? inst.input[0] : null), [(inst.input ? inst.input.val() : ""), inst]);
- }
-
- this._lastInput = null;
- if (this._inDialog) {
- this._dialogInput.css({ position: "absolute", left: "0", top: "-100px" });
- if ($.blockUI) {
- $.unblockUI();
- $("body").append(this.dpDiv);
- }
- }
- this._inDialog = false;
- }
- },
-
- /* Tidy up after a dialog display. */
- _tidyDialog: function(inst) {
- inst.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar");
- },
-
- /* Close date picker if clicked elsewhere. */
- _checkExternalClick: function(event) {
- if (!$.datepicker._curInst) {
- return;
- }
-
- var $target = $(event.target),
- inst = $.datepicker._getInst($target[0]);
-
- if ( ( ( $target[0].id !== $.datepicker._mainDivId &&
- $target.parents("#" + $.datepicker._mainDivId).length === 0 &&
- !$target.hasClass($.datepicker.markerClassName) &&
- !$target.closest("." + $.datepicker._triggerClass).length &&
- $.datepicker._datepickerShowing && !($.datepicker._inDialog && $.blockUI) ) ) ||
- ( $target.hasClass($.datepicker.markerClassName) && $.datepicker._curInst !== inst ) ) {
- $.datepicker._hideDatepicker();
- }
- },
-
- /* Adjust one of the date sub-fields. */
- _adjustDate: function(id, offset, period) {
- var target = $(id),
- inst = this._getInst(target[0]);
-
- if (this._isDisabledDatepicker(target[0])) {
- return;
- }
- this._adjustInstDate(inst, offset +
- (period === "M" ? this._get(inst, "showCurrentAtPos") : 0), // undo positioning
- period);
- this._updateDatepicker(inst);
- },
-
- /* Action for current link. */
- _gotoToday: function(id) {
- var date,
- target = $(id),
- inst = this._getInst(target[0]);
-
- if (this._get(inst, "gotoCurrent") && inst.currentDay) {
- inst.selectedDay = inst.currentDay;
- inst.drawMonth = inst.selectedMonth = inst.currentMonth;
- inst.drawYear = inst.selectedYear = inst.currentYear;
- } else {
- date = new Date();
- inst.selectedDay = date.getDate();
- inst.drawMonth = inst.selectedMonth = date.getMonth();
- inst.drawYear = inst.selectedYear = date.getFullYear();
- }
- this._notifyChange(inst);
- this._adjustDate(target);
- },
-
- /* Action for selecting a new month/year. */
- _selectMonthYear: function(id, select, period) {
- var target = $(id),
- inst = this._getInst(target[0]);
-
- inst["selected" + (period === "M" ? "Month" : "Year")] =
- inst["draw" + (period === "M" ? "Month" : "Year")] =
- parseInt(select.options[select.selectedIndex].value,10);
-
- this._notifyChange(inst);
- this._adjustDate(target);
- },
-
- /* Action for selecting a day. */
- _selectDay: function(id, month, year, td) {
- var inst,
- target = $(id);
-
- if ($(td).hasClass(this._unselectableClass) || this._isDisabledDatepicker(target[0])) {
- return;
- }
-
- inst = this._getInst(target[0]);
- inst.selectedDay = inst.currentDay = $("a", td).html();
- inst.selectedMonth = inst.currentMonth = month;
- inst.selectedYear = inst.currentYear = year;
- this._selectDate(id, this._formatDate(inst,
- inst.currentDay, inst.currentMonth, inst.currentYear));
- },
-
- /* Erase the input field and hide the date picker. */
- _clearDate: function(id) {
- var target = $(id);
- this._selectDate(target, "");
- },
-
- /* Update the input field with the selected date. */
- _selectDate: function(id, dateStr) {
- var onSelect,
- target = $(id),
- inst = this._getInst(target[0]);
-
- dateStr = (dateStr != null ? dateStr : this._formatDate(inst));
- if (inst.input) {
- inst.input.val(dateStr);
- }
- this._updateAlternate(inst);
-
- onSelect = this._get(inst, "onSelect");
- if (onSelect) {
- onSelect.apply((inst.input ? inst.input[0] : null), [dateStr, inst]); // trigger custom callback
- } else if (inst.input) {
- inst.input.trigger("change"); // fire the change event
- }
-
- if (inst.inline){
- this._updateDatepicker(inst);
- } else {
- this._hideDatepicker();
- this._lastInput = inst.input[0];
- if (typeof(inst.input[0]) !== "object") {
- inst.input.focus(); // restore focus
- }
- this._lastInput = null;
- }
- },
-
- /* Update any alternate field to synchronise with the main field. */
- _updateAlternate: function(inst) {
- var altFormat, date, dateStr,
- altField = this._get(inst, "altField");
-
- if (altField) { // update alternate field too
- altFormat = this._get(inst, "altFormat") || this._get(inst, "dateFormat");
- date = this._getDate(inst);
- dateStr = this.formatDate(altFormat, date, this._getFormatConfig(inst));
- $(altField).each(function() { $(this).val(dateStr); });
- }
- },
-
- /* Set as beforeShowDay function to prevent selection of weekends.
- * @param date Date - the date to customise
- * @return [boolean, string] - is this date selectable?, what is its CSS class?
- */
- noWeekends: function(date) {
- var day = date.getDay();
- return [(day > 0 && day < 6), ""];
- },
-
- /* Set as calculateWeek to determine the week of the year based on the ISO 8601 definition.
- * @param date Date - the date to get the week for
- * @return number - the number of the week within the year that contains this date
- */
- iso8601Week: function(date) {
- var time,
- checkDate = new Date(date.getTime());
-
- // Find Thursday of this week starting on Monday
- checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7));
-
- time = checkDate.getTime();
- checkDate.setMonth(0); // Compare with Jan 1
- checkDate.setDate(1);
- return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
- },
-
- /* Parse a string value into a date object.
- * See formatDate below for the possible formats.
- *
- * @param format string - the expected format of the date
- * @param value string - the date in the above format
- * @param settings Object - attributes include:
- * shortYearCutoff number - the cutoff year for determining the century (optional)
- * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
- * dayNames string[7] - names of the days from Sunday (optional)
- * monthNamesShort string[12] - abbreviated names of the months (optional)
- * monthNames string[12] - names of the months (optional)
- * @return Date - the extracted date value or null if value is blank
- */
- parseDate: function (format, value, settings) {
- if (format == null || value == null) {
- throw "Invalid arguments";
- }
-
- value = (typeof value === "object" ? value.toString() : value + "");
- if (value === "") {
- return null;
- }
-
- var iFormat, dim, extra,
- iValue = 0,
- shortYearCutoffTemp = (settings ? settings.shortYearCutoff : null) || this._defaults.shortYearCutoff,
- shortYearCutoff = (typeof shortYearCutoffTemp !== "string" ? shortYearCutoffTemp :
- new Date().getFullYear() % 100 + parseInt(shortYearCutoffTemp, 10)),
- dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
- dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
- monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
- monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
- year = -1,
- month = -1,
- day = -1,
- doy = -1,
- literal = false,
- date,
- // Check whether a format character is doubled
- lookAhead = function(match) {
- var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
- if (matches) {
- iFormat++;
- }
- return matches;
- },
- // Extract a number from the string value
- getNumber = function(match) {
- var isDoubled = lookAhead(match),
- size = (match === "@" ? 14 : (match === "!" ? 20 :
- (match === "y" && isDoubled ? 4 : (match === "o" ? 3 : 2)))),
- digits = new RegExp("^\\d{1," + size + "}"),
- num = value.substring(iValue).match(digits);
- if (!num) {
- throw "Missing number at position " + iValue;
- }
- iValue += num[0].length;
- return parseInt(num[0], 10);
- },
- // Extract a name from the string value and convert to an index
- getName = function(match, shortNames, longNames) {
- var index = -1,
- names = $.map(lookAhead(match) ? longNames : shortNames, function (v, k) {
- return [ [k, v] ];
- }).sort(function (a, b) {
- return -(a[1].length - b[1].length);
- });
-
- $.each(names, function (i, pair) {
- var name = pair[1];
- if (value.substr(iValue, name.length).toLowerCase() === name.toLowerCase()) {
- index = pair[0];
- iValue += name.length;
- return false;
- }
- });
- if (index !== -1) {
- return index + 1;
- } else {
- throw "Unknown name at position " + iValue;
- }
- },
- // Confirm that a literal character matches the string value
- checkLiteral = function() {
- if (value.charAt(iValue) !== format.charAt(iFormat)) {
- throw "Unexpected literal at position " + iValue;
- }
- iValue++;
- };
-
- for (iFormat = 0; iFormat < format.length; iFormat++) {
- if (literal) {
- if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
- literal = false;
- } else {
- checkLiteral();
- }
- } else {
- switch (format.charAt(iFormat)) {
- case "d":
- day = getNumber("d");
- break;
- case "D":
- getName("D", dayNamesShort, dayNames);
- break;
- case "o":
- doy = getNumber("o");
- break;
- case "m":
- month = getNumber("m");
- break;
- case "M":
- month = getName("M", monthNamesShort, monthNames);
- break;
- case "y":
- year = getNumber("y");
- break;
- case "@":
- date = new Date(getNumber("@"));
- year = date.getFullYear();
- month = date.getMonth() + 1;
- day = date.getDate();
- break;
- case "!":
- date = new Date((getNumber("!") - this._ticksTo1970) / 10000);
- year = date.getFullYear();
- month = date.getMonth() + 1;
- day = date.getDate();
- break;
- case "'":
- if (lookAhead("'")){
- checkLiteral();
- } else {
- literal = true;
- }
- break;
- default:
- checkLiteral();
- }
- }
- }
-
- if (iValue < value.length){
- extra = value.substr(iValue);
- if (!/^\s+/.test(extra)) {
- throw "Extra/unparsed characters found in date: " + extra;
- }
- }
-
- if (year === -1) {
- year = new Date().getFullYear();
- } else if (year < 100) {
- year += new Date().getFullYear() - new Date().getFullYear() % 100 +
- (year <= shortYearCutoff ? 0 : -100);
- }
-
- if (doy > -1) {
- month = 1;
- day = doy;
- do {
- dim = this._getDaysInMonth(year, month - 1);
- if (day <= dim) {
- break;
- }
- month++;
- day -= dim;
- } while (true);
- }
-
- date = this._daylightSavingAdjust(new Date(year, month - 1, day));
- if (date.getFullYear() !== year || date.getMonth() + 1 !== month || date.getDate() !== day) {
- throw "Invalid date"; // E.g. 31/02/00
- }
- return date;
- },
-
- /* Standard date formats. */
- ATOM: "yy-mm-dd", // RFC 3339 (ISO 8601)
- COOKIE: "D, dd M yy",
- ISO_8601: "yy-mm-dd",
- RFC_822: "D, d M y",
- RFC_850: "DD, dd-M-y",
- RFC_1036: "D, d M y",
- RFC_1123: "D, d M yy",
- RFC_2822: "D, d M yy",
- RSS: "D, d M y", // RFC 822
- TICKS: "!",
- TIMESTAMP: "@",
- W3C: "yy-mm-dd", // ISO 8601
-
- _ticksTo1970: (((1970 - 1) * 365 + Math.floor(1970 / 4) - Math.floor(1970 / 100) +
- Math.floor(1970 / 400)) * 24 * 60 * 60 * 10000000),
-
- /* Format a date object into a string value.
- * The format can be combinations of the following:
- * d - day of month (no leading zero)
- * dd - day of month (two digit)
- * o - day of year (no leading zeros)
- * oo - day of year (three digit)
- * D - day name short
- * DD - day name long
- * m - month of year (no leading zero)
- * mm - month of year (two digit)
- * M - month name short
- * MM - month name long
- * y - year (two digit)
- * yy - year (four digit)
- * @ - Unix timestamp (ms since 01/01/1970)
- * ! - Windows ticks (100ns since 01/01/0001)
- * "..." - literal text
- * '' - single quote
- *
- * @param format string - the desired format of the date
- * @param date Date - the date value to format
- * @param settings Object - attributes include:
- * dayNamesShort string[7] - abbreviated names of the days from Sunday (optional)
- * dayNames string[7] - names of the days from Sunday (optional)
- * monthNamesShort string[12] - abbreviated names of the months (optional)
- * monthNames string[12] - names of the months (optional)
- * @return string - the date in the above format
- */
- formatDate: function (format, date, settings) {
- if (!date) {
- return "";
- }
-
- var iFormat,
- dayNamesShort = (settings ? settings.dayNamesShort : null) || this._defaults.dayNamesShort,
- dayNames = (settings ? settings.dayNames : null) || this._defaults.dayNames,
- monthNamesShort = (settings ? settings.monthNamesShort : null) || this._defaults.monthNamesShort,
- monthNames = (settings ? settings.monthNames : null) || this._defaults.monthNames,
- // Check whether a format character is doubled
- lookAhead = function(match) {
- var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
- if (matches) {
- iFormat++;
- }
- return matches;
- },
- // Format a number, with leading zero if necessary
- formatNumber = function(match, value, len) {
- var num = "" + value;
- if (lookAhead(match)) {
- while (num.length < len) {
- num = "0" + num;
- }
- }
- return num;
- },
- // Format a name, short or long as requested
- formatName = function(match, value, shortNames, longNames) {
- return (lookAhead(match) ? longNames[value] : shortNames[value]);
- },
- output = "",
- literal = false;
-
- if (date) {
- for (iFormat = 0; iFormat < format.length; iFormat++) {
- if (literal) {
- if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
- literal = false;
- } else {
- output += format.charAt(iFormat);
- }
- } else {
- switch (format.charAt(iFormat)) {
- case "d":
- output += formatNumber("d", date.getDate(), 2);
- break;
- case "D":
- output += formatName("D", date.getDay(), dayNamesShort, dayNames);
- break;
- case "o":
- output += formatNumber("o",
- Math.round((new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000), 3);
- break;
- case "m":
- output += formatNumber("m", date.getMonth() + 1, 2);
- break;
- case "M":
- output += formatName("M", date.getMonth(), monthNamesShort, monthNames);
- break;
- case "y":
- output += (lookAhead("y") ? date.getFullYear() :
- (date.getYear() % 100 < 10 ? "0" : "") + date.getYear() % 100);
- break;
- case "@":
- output += date.getTime();
- break;
- case "!":
- output += date.getTime() * 10000 + this._ticksTo1970;
- break;
- case "'":
- if (lookAhead("'")) {
- output += "'";
- } else {
- literal = true;
- }
- break;
- default:
- output += format.charAt(iFormat);
- }
- }
- }
- }
- return output;
- },
-
- /* Extract all possible characters from the date format. */
- _possibleChars: function (format) {
- var iFormat,
- chars = "",
- literal = false,
- // Check whether a format character is doubled
- lookAhead = function(match) {
- var matches = (iFormat + 1 < format.length && format.charAt(iFormat + 1) === match);
- if (matches) {
- iFormat++;
- }
- return matches;
- };
-
- for (iFormat = 0; iFormat < format.length; iFormat++) {
- if (literal) {
- if (format.charAt(iFormat) === "'" && !lookAhead("'")) {
- literal = false;
- } else {
- chars += format.charAt(iFormat);
- }
- } else {
- switch (format.charAt(iFormat)) {
- case "d": case "m": case "y": case "@":
- chars += "0123456789";
- break;
- case "D": case "M":
- return null; // Accept anything
- case "'":
- if (lookAhead("'")) {
- chars += "'";
- } else {
- literal = true;
- }
- break;
- default:
- chars += format.charAt(iFormat);
- }
- }
- }
- return chars;
- },
-
- /* Get a setting value, defaulting if necessary. */
- _get: function(inst, name) {
- return inst.settings[name] !== undefined ?
- inst.settings[name] : this._defaults[name];
- },
-
- /* Parse existing date and initialise date picker. */
- _setDateFromField: function(inst, noDefault) {
- if (inst.input.val() === inst.lastVal) {
- return;
- }
-
- var dateFormat = this._get(inst, "dateFormat"),
- dates = inst.lastVal = inst.input ? inst.input.val() : null,
- defaultDate = this._getDefaultDate(inst),
- date = defaultDate,
- settings = this._getFormatConfig(inst);
-
- try {
- date = this.parseDate(dateFormat, dates, settings) || defaultDate;
- } catch (event) {
- dates = (noDefault ? "" : dates);
- }
- inst.selectedDay = date.getDate();
- inst.drawMonth = inst.selectedMonth = date.getMonth();
- inst.drawYear = inst.selectedYear = date.getFullYear();
- inst.currentDay = (dates ? date.getDate() : 0);
- inst.currentMonth = (dates ? date.getMonth() : 0);
- inst.currentYear = (dates ? date.getFullYear() : 0);
- this._adjustInstDate(inst);
- },
-
- /* Retrieve the default date shown on opening. */
- _getDefaultDate: function(inst) {
- return this._restrictMinMax(inst,
- this._determineDate(inst, this._get(inst, "defaultDate"), new Date()));
- },
-
- /* A date may be specified as an exact value or a relative one. */
- _determineDate: function(inst, date, defaultDate) {
- var offsetNumeric = function(offset) {
- var date = new Date();
- date.setDate(date.getDate() + offset);
- return date;
- },
- offsetString = function(offset) {
- try {
- return $.datepicker.parseDate($.datepicker._get(inst, "dateFormat"),
- offset, $.datepicker._getFormatConfig(inst));
- }
- catch (e) {
- // Ignore
- }
-
- var date = (offset.toLowerCase().match(/^c/) ?
- $.datepicker._getDate(inst) : null) || new Date(),
- year = date.getFullYear(),
- month = date.getMonth(),
- day = date.getDate(),
- pattern = /([+\-]?[0-9]+)\s*(d|D|w|W|m|M|y|Y)?/g,
- matches = pattern.exec(offset);
-
- while (matches) {
- switch (matches[2] || "d") {
- case "d" : case "D" :
- day += parseInt(matches[1],10); break;
- case "w" : case "W" :
- day += parseInt(matches[1],10) * 7; break;
- case "m" : case "M" :
- month += parseInt(matches[1],10);
- day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
- break;
- case "y": case "Y" :
- year += parseInt(matches[1],10);
- day = Math.min(day, $.datepicker._getDaysInMonth(year, month));
- break;
- }
- matches = pattern.exec(offset);
- }
- return new Date(year, month, day);
- },
- newDate = (date == null || date === "" ? defaultDate : (typeof date === "string" ? offsetString(date) :
- (typeof date === "number" ? (isNaN(date) ? defaultDate : offsetNumeric(date)) : new Date(date.getTime()))));
-
- newDate = (newDate && newDate.toString() === "Invalid Date" ? defaultDate : newDate);
- if (newDate) {
- newDate.setHours(0);
- newDate.setMinutes(0);
- newDate.setSeconds(0);
- newDate.setMilliseconds(0);
- }
- return this._daylightSavingAdjust(newDate);
- },
-
- /* Handle switch to/from daylight saving.
- * Hours may be non-zero on daylight saving cut-over:
- * > 12 when midnight changeover, but then cannot generate
- * midnight datetime, so jump to 1AM, otherwise reset.
- * @param date (Date) the date to check
- * @return (Date) the corrected date
- */
- _daylightSavingAdjust: function(date) {
- if (!date) {
- return null;
- }
- date.setHours(date.getHours() > 12 ? date.getHours() + 2 : 0);
- return date;
- },
-
- /* Set the date(s) directly. */
- _setDate: function(inst, date, noChange) {
- var clear = !date,
- origMonth = inst.selectedMonth,
- origYear = inst.selectedYear,
- newDate = this._restrictMinMax(inst, this._determineDate(inst, date, new Date()));
-
- inst.selectedDay = inst.currentDay = newDate.getDate();
- inst.drawMonth = inst.selectedMonth = inst.currentMonth = newDate.getMonth();
- inst.drawYear = inst.selectedYear = inst.currentYear = newDate.getFullYear();
- if ((origMonth !== inst.selectedMonth || origYear !== inst.selectedYear) && !noChange) {
- this._notifyChange(inst);
- }
- this._adjustInstDate(inst);
- if (inst.input) {
- inst.input.val(clear ? "" : this._formatDate(inst));
- }
- },
-
- /* Retrieve the date(s) directly. */
- _getDate: function(inst) {
- var startDate = (!inst.currentYear || (inst.input && inst.input.val() === "") ? null :
- this._daylightSavingAdjust(new Date(
- inst.currentYear, inst.currentMonth, inst.currentDay)));
- return startDate;
- },
-
- /* Attach the onxxx handlers. These are declared statically so
- * they work with static code transformers like Caja.
- */
- _attachHandlers: function(inst) {
- var stepMonths = this._get(inst, "stepMonths"),
- id = "#" + inst.id.replace( /\\\\/g, "\\" );
- inst.dpDiv.find("[data-handler]").map(function () {
- var handler = {
- prev: function () {
- window["DP_jQuery_" + dpuuid].datepicker._adjustDate(id, -stepMonths, "M");
- },
- next: function () {
- window["DP_jQuery_" + dpuuid].datepicker._adjustDate(id, +stepMonths, "M");
- },
- hide: function () {
- window["DP_jQuery_" + dpuuid].datepicker._hideDatepicker();
- },
- today: function () {
- window["DP_jQuery_" + dpuuid].datepicker._gotoToday(id);
- },
- selectDay: function () {
- window["DP_jQuery_" + dpuuid].datepicker._selectDay(id, +this.getAttribute("data-month"), +this.getAttribute("data-year"), this);
- return false;
- },
- selectMonth: function () {
- window["DP_jQuery_" + dpuuid].datepicker._selectMonthYear(id, this, "M");
- return false;
- },
- selectYear: function () {
- window["DP_jQuery_" + dpuuid].datepicker._selectMonthYear(id, this, "Y");
- return false;
- }
- };
- $(this).bind(this.getAttribute("data-event"), handler[this.getAttribute("data-handler")]);
- });
- },
-
- /* Generate the HTML for the current state of the date picker. */
- _generateHTML: function(inst) {
- var maxDraw, prevText, prev, nextText, next, currentText, gotoDate,
- controls, buttonPanel, firstDay, showWeek, dayNames, dayNamesMin,
- monthNames, monthNamesShort, beforeShowDay, showOtherMonths,
- selectOtherMonths, defaultDate, html, dow, row, group, col, selectedDate,
- cornerClass, calender, thead, day, daysInMonth, leadDays, curRows, numRows,
- printDate, dRow, tbody, daySettings, otherMonth, unselectable,
- tempDate = new Date(),
- today = this._daylightSavingAdjust(
- new Date(tempDate.getFullYear(), tempDate.getMonth(), tempDate.getDate())), // clear time
- isRTL = this._get(inst, "isRTL"),
- showButtonPanel = this._get(inst, "showButtonPanel"),
- hideIfNoPrevNext = this._get(inst, "hideIfNoPrevNext"),
- navigationAsDateFormat = this._get(inst, "navigationAsDateFormat"),
- numMonths = this._getNumberOfMonths(inst),
- showCurrentAtPos = this._get(inst, "showCurrentAtPos"),
- stepMonths = this._get(inst, "stepMonths"),
- isMultiMonth = (numMonths[0] !== 1 || numMonths[1] !== 1),
- currentDate = this._daylightSavingAdjust((!inst.currentDay ? new Date(9999, 9, 9) :
- new Date(inst.currentYear, inst.currentMonth, inst.currentDay))),
- minDate = this._getMinMaxDate(inst, "min"),
- maxDate = this._getMinMaxDate(inst, "max"),
- drawMonth = inst.drawMonth - showCurrentAtPos,
- drawYear = inst.drawYear;
-
- if (drawMonth < 0) {
- drawMonth += 12;
- drawYear--;
- }
- if (maxDate) {
- maxDraw = this._daylightSavingAdjust(new Date(maxDate.getFullYear(),
- maxDate.getMonth() - (numMonths[0] * numMonths[1]) + 1, maxDate.getDate()));
- maxDraw = (minDate && maxDraw < minDate ? minDate : maxDraw);
- while (this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1)) > maxDraw) {
- drawMonth--;
- if (drawMonth < 0) {
- drawMonth = 11;
- drawYear--;
- }
- }
- }
- inst.drawMonth = drawMonth;
- inst.drawYear = drawYear;
-
- prevText = this._get(inst, "prevText");
- prevText = (!navigationAsDateFormat ? prevText : this.formatDate(prevText,
- this._daylightSavingAdjust(new Date(drawYear, drawMonth - stepMonths, 1)),
- this._getFormatConfig(inst)));
-
- prev = (this._canAdjustMonth(inst, -1, drawYear, drawMonth) ?
- "<a class='ui-datepicker-prev ui-corner-all' data-handler='prev' data-event='click'" +
- " title='" + prevText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>" :
- (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-prev ui-corner-all ui-state-disabled' title='"+ prevText +"'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "e" : "w") + "'>" + prevText + "</span></a>"));
-
- nextText = this._get(inst, "nextText");
- nextText = (!navigationAsDateFormat ? nextText : this.formatDate(nextText,
- this._daylightSavingAdjust(new Date(drawYear, drawMonth + stepMonths, 1)),
- this._getFormatConfig(inst)));
-
- next = (this._canAdjustMonth(inst, +1, drawYear, drawMonth) ?
- "<a class='ui-datepicker-next ui-corner-all' data-handler='next' data-event='click'" +
- " title='" + nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>" :
- (hideIfNoPrevNext ? "" : "<a class='ui-datepicker-next ui-corner-all ui-state-disabled' title='"+ nextText + "'><span class='ui-icon ui-icon-circle-triangle-" + ( isRTL ? "w" : "e") + "'>" + nextText + "</span></a>"));
-
- currentText = this._get(inst, "currentText");
- gotoDate = (this._get(inst, "gotoCurrent") && inst.currentDay ? currentDate : today);
- currentText = (!navigationAsDateFormat ? currentText :
- this.formatDate(currentText, gotoDate, this._getFormatConfig(inst)));
-
- controls = (!inst.inline ? "<button type='button' class='ui-datepicker-close ui-state-default ui-priority-primary ui-corner-all' data-handler='hide' data-event='click'>" +
- this._get(inst, "closeText") + "</button>" : "");
-
- buttonPanel = (showButtonPanel) ? "<div class='ui-datepicker-buttonpane ui-widget-content'>" + (isRTL ? controls : "") +
- (this._isInRange(inst, gotoDate) ? "<button type='button' class='ui-datepicker-current ui-state-default ui-priority-secondary ui-corner-all' data-handler='today' data-event='click'" +
- ">" + currentText + "</button>" : "") + (isRTL ? "" : controls) + "</div>" : "";
-
- firstDay = parseInt(this._get(inst, "firstDay"),10);
- firstDay = (isNaN(firstDay) ? 0 : firstDay);
-
- showWeek = this._get(inst, "showWeek");
- dayNames = this._get(inst, "dayNames");
- dayNamesMin = this._get(inst, "dayNamesMin");
- monthNames = this._get(inst, "monthNames");
- monthNamesShort = this._get(inst, "monthNamesShort");
- beforeShowDay = this._get(inst, "beforeShowDay");
- showOtherMonths = this._get(inst, "showOtherMonths");
- selectOtherMonths = this._get(inst, "selectOtherMonths");
- defaultDate = this._getDefaultDate(inst);
- html = "";
- dow;
- for (row = 0; row < numMonths[0]; row++) {
- group = "";
- this.maxRows = 4;
- for (col = 0; col < numMonths[1]; col++) {
- selectedDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, inst.selectedDay));
- cornerClass = " ui-corner-all";
- calender = "";
- if (isMultiMonth) {
- calender += "<div class='ui-datepicker-group";
- if (numMonths[1] > 1) {
- switch (col) {
- case 0: calender += " ui-datepicker-group-first";
- cornerClass = " ui-corner-" + (isRTL ? "right" : "left"); break;
- case numMonths[1]-1: calender += " ui-datepicker-group-last";
- cornerClass = " ui-corner-" + (isRTL ? "left" : "right"); break;
- default: calender += " ui-datepicker-group-middle"; cornerClass = ""; break;
- }
- }
- calender += "'>";
- }
- calender += "<div class='ui-datepicker-header ui-widget-header ui-helper-clearfix" + cornerClass + "'>" +
- (/all|left/.test(cornerClass) && row === 0 ? (isRTL ? next : prev) : "") +
- (/all|right/.test(cornerClass) && row === 0 ? (isRTL ? prev : next) : "") +
- this._generateMonthYearHeader(inst, drawMonth, drawYear, minDate, maxDate,
- row > 0 || col > 0, monthNames, monthNamesShort) + // draw month headers
- "</div><table class='ui-datepicker-calendar'><thead>" +
- "<tr>";
- thead = (showWeek ? "<th class='ui-datepicker-week-col'>" + this._get(inst, "weekHeader") + "</th>" : "");
- for (dow = 0; dow < 7; dow++) { // days of the week
- day = (dow + firstDay) % 7;
- thead += "<th" + ((dow + firstDay + 6) % 7 >= 5 ? " class='ui-datepicker-week-end'" : "") + ">" +
- "<span title='" + dayNames[day] + "'>" + dayNamesMin[day] + "</span></th>";
- }
- calender += thead + "</tr></thead><tbody>";
- daysInMonth = this._getDaysInMonth(drawYear, drawMonth);
- if (drawYear === inst.selectedYear && drawMonth === inst.selectedMonth) {
- inst.selectedDay = Math.min(inst.selectedDay, daysInMonth);
- }
- leadDays = (this._getFirstDayOfMonth(drawYear, drawMonth) - firstDay + 7) % 7;
- curRows = Math.ceil((leadDays + daysInMonth) / 7); // calculate the number of rows to generate
- numRows = (isMultiMonth ? this.maxRows > curRows ? this.maxRows : curRows : curRows); //If multiple months, use the higher number of rows (see #7043)
- this.maxRows = numRows;
- printDate = this._daylightSavingAdjust(new Date(drawYear, drawMonth, 1 - leadDays));
- for (dRow = 0; dRow < numRows; dRow++) { // create date picker rows
- calender += "<tr>";
- tbody = (!showWeek ? "" : "<td class='ui-datepicker-week-col'>" +
- this._get(inst, "calculateWeek")(printDate) + "</td>");
- for (dow = 0; dow < 7; dow++) { // create date picker days
- daySettings = (beforeShowDay ?
- beforeShowDay.apply((inst.input ? inst.input[0] : null), [printDate]) : [true, ""]);
- otherMonth = (printDate.getMonth() !== drawMonth);
- unselectable = (otherMonth && !selectOtherMonths) || !daySettings[0] ||
- (minDate && printDate < minDate) || (maxDate && printDate > maxDate);
- tbody += "<td class='" +
- ((dow + firstDay + 6) % 7 >= 5 ? " ui-datepicker-week-end" : "") + // highlight weekends
- (otherMonth ? " ui-datepicker-other-month" : "") + // highlight days from other months
- ((printDate.getTime() === selectedDate.getTime() && drawMonth === inst.selectedMonth && inst._keyEvent) || // user pressed key
- (defaultDate.getTime() === printDate.getTime() && defaultDate.getTime() === selectedDate.getTime()) ?
- // or defaultDate is current printedDate and defaultDate is selectedDate
- " " + this._dayOverClass : "") + // highlight selected day
- (unselectable ? " " + this._unselectableClass + " ui-state-disabled": "") + // highlight unselectable days
- (otherMonth && !showOtherMonths ? "" : " " + daySettings[1] + // highlight custom dates
- (printDate.getTime() === currentDate.getTime() ? " " + this._currentClass : "") + // highlight selected day
- (printDate.getTime() === today.getTime() ? " ui-datepicker-today" : "")) + "'" + // highlight today (if different)
- ((!otherMonth || showOtherMonths) && daySettings[2] ? " title='" + daySettings[2].replace(/'/g, "'") + "'" : "") + // cell title
- (unselectable ? "" : " data-handler='selectDay' data-event='click' data-month='" + printDate.getMonth() + "' data-year='" + printDate.getFullYear() + "'") + ">" + // actions
- (otherMonth && !showOtherMonths ? " " : // display for other months
- (unselectable ? "<span class='ui-state-default'>" + printDate.getDate() + "</span>" : "<a class='ui-state-default" +
- (printDate.getTime() === today.getTime() ? " ui-state-highlight" : "") +
- (printDate.getTime() === currentDate.getTime() ? " ui-state-active" : "") + // highlight selected day
- (otherMonth ? " ui-priority-secondary" : "") + // distinguish dates from other months
- "' href='#'>" + printDate.getDate() + "</a>")) + "</td>"; // display selectable date
- printDate.setDate(printDate.getDate() + 1);
- printDate = this._daylightSavingAdjust(printDate);
- }
- calender += tbody + "</tr>";
- }
- drawMonth++;
- if (drawMonth > 11) {
- drawMonth = 0;
- drawYear++;
- }
- calender += "</tbody></table>" + (isMultiMonth ? "</div>" +
- ((numMonths[0] > 0 && col === numMonths[1]-1) ? "<div class='ui-datepicker-row-break'></div>" : "") : "");
- group += calender;
- }
- html += group;
- }
- html += buttonPanel;
- inst._keyEvent = false;
- return html;
- },
-
- /* Generate the month and year header. */
- _generateMonthYearHeader: function(inst, drawMonth, drawYear, minDate, maxDate,
- secondary, monthNames, monthNamesShort) {
-
- var inMinYear, inMaxYear, month, years, thisYear, determineYear, year, endYear,
- changeMonth = this._get(inst, "changeMonth"),
- changeYear = this._get(inst, "changeYear"),
- showMonthAfterYear = this._get(inst, "showMonthAfterYear"),
- html = "<div class='ui-datepicker-title'>",
- monthHtml = "";
-
- // month selection
- if (secondary || !changeMonth) {
- monthHtml += "<span class='ui-datepicker-month'>" + monthNames[drawMonth] + "</span>";
- } else {
- inMinYear = (minDate && minDate.getFullYear() === drawYear);
- inMaxYear = (maxDate && maxDate.getFullYear() === drawYear);
- monthHtml += "<select class='ui-datepicker-month' data-handler='selectMonth' data-event='change'>";
- for ( month = 0; month < 12; month++) {
- if ((!inMinYear || month >= minDate.getMonth()) && (!inMaxYear || month <= maxDate.getMonth())) {
- monthHtml += "<option value='" + month + "'" +
- (month === drawMonth ? " selected='selected'" : "") +
- ">" + monthNamesShort[month] + "</option>";
- }
- }
- monthHtml += "</select>";
- }
-
- if (!showMonthAfterYear) {
- html += monthHtml + (secondary || !(changeMonth && changeYear) ? " " : "");
- }
-
- // year selection
- if ( !inst.yearshtml ) {
- inst.yearshtml = "";
- if (secondary || !changeYear) {
- html += "<span class='ui-datepicker-year'>" + drawYear + "</span>";
- } else {
- // determine range of years to display
- years = this._get(inst, "yearRange").split(":");
- thisYear = new Date().getFullYear();
- determineYear = function(value) {
- var year = (value.match(/c[+\-].*/) ? drawYear + parseInt(value.substring(1), 10) :
- (value.match(/[+\-].*/) ? thisYear + parseInt(value, 10) :
- parseInt(value, 10)));
- return (isNaN(year) ? thisYear : year);
- };
- year = determineYear(years[0]);
- endYear = Math.max(year, determineYear(years[1] || ""));
- year = (minDate ? Math.max(year, minDate.getFullYear()) : year);
- endYear = (maxDate ? Math.min(endYear, maxDate.getFullYear()) : endYear);
- inst.yearshtml += "<select class='ui-datepicker-year' data-handler='selectYear' data-event='change'>";
- for (; year <= endYear; year++) {
- inst.yearshtml += "<option value='" + year + "'" +
- (year === drawYear ? " selected='selected'" : "") +
- ">" + year + "</option>";
- }
- inst.yearshtml += "</select>";
-
- html += inst.yearshtml;
- inst.yearshtml = null;
- }
- }
-
- html += this._get(inst, "yearSuffix");
- if (showMonthAfterYear) {
- html += (secondary || !(changeMonth && changeYear) ? " " : "") + monthHtml;
- }
- html += "</div>"; // Close datepicker_header
- return html;
- },
-
- /* Adjust one of the date sub-fields. */
- _adjustInstDate: function(inst, offset, period) {
- var year = inst.drawYear + (period === "Y" ? offset : 0),
- month = inst.drawMonth + (period === "M" ? offset : 0),
- day = Math.min(inst.selectedDay, this._getDaysInMonth(year, month)) + (period === "D" ? offset : 0),
- date = this._restrictMinMax(inst, this._daylightSavingAdjust(new Date(year, month, day)));
-
- inst.selectedDay = date.getDate();
- inst.drawMonth = inst.selectedMonth = date.getMonth();
- inst.drawYear = inst.selectedYear = date.getFullYear();
- if (period === "M" || period === "Y") {
- this._notifyChange(inst);
- }
- },
-
- /* Ensure a date is within any min/max bounds. */
- _restrictMinMax: function(inst, date) {
- var minDate = this._getMinMaxDate(inst, "min"),
- maxDate = this._getMinMaxDate(inst, "max"),
- newDate = (minDate && date < minDate ? minDate : date);
- return (maxDate && newDate > maxDate ? maxDate : newDate);
- },
-
- /* Notify change of month/year. */
- _notifyChange: function(inst) {
- var onChange = this._get(inst, "onChangeMonthYear");
- if (onChange) {
- onChange.apply((inst.input ? inst.input[0] : null),
- [inst.selectedYear, inst.selectedMonth + 1, inst]);
- }
- },
-
- /* Determine the number of months to show. */
- _getNumberOfMonths: function(inst) {
- var numMonths = this._get(inst, "numberOfMonths");
- return (numMonths == null ? [1, 1] : (typeof numMonths === "number" ? [1, numMonths] : numMonths));
- },
-
- /* Determine the current maximum date - ensure no time components are set. */
- _getMinMaxDate: function(inst, minMax) {
- return this._determineDate(inst, this._get(inst, minMax + "Date"), null);
- },
-
- /* Find the number of days in a given month. */
- _getDaysInMonth: function(year, month) {
- return 32 - this._daylightSavingAdjust(new Date(year, month, 32)).getDate();
- },
-
- /* Find the day of the week of the first of a month. */
- _getFirstDayOfMonth: function(year, month) {
- return new Date(year, month, 1).getDay();
- },
-
- /* Determines if we should allow a "next/prev" month display change. */
- _canAdjustMonth: function(inst, offset, curYear, curMonth) {
- var numMonths = this._getNumberOfMonths(inst),
- date = this._daylightSavingAdjust(new Date(curYear,
- curMonth + (offset < 0 ? offset : numMonths[0] * numMonths[1]), 1));
-
- if (offset < 0) {
- date.setDate(this._getDaysInMonth(date.getFullYear(), date.getMonth()));
- }
- return this._isInRange(inst, date);
- },
-
- /* Is the given date in the accepted range? */
- _isInRange: function(inst, date) {
- var yearSplit, currentYear,
- minDate = this._getMinMaxDate(inst, "min"),
- maxDate = this._getMinMaxDate(inst, "max"),
- minYear = null,
- maxYear = null,
- years = this._get(inst, "yearRange");
- if (years){
- yearSplit = years.split(":");
- currentYear = new Date().getFullYear();
- minYear = parseInt(yearSplit[0], 10);
- maxYear = parseInt(yearSplit[1], 10);
- if ( yearSplit[0].match(/[+\-].*/) ) {
- minYear += currentYear;
- }
- if ( yearSplit[1].match(/[+\-].*/) ) {
- maxYear += currentYear;
- }
- }
-
- return ((!minDate || date.getTime() >= minDate.getTime()) &&
- (!maxDate || date.getTime() <= maxDate.getTime()) &&
- (!minYear || date.getFullYear() >= minYear) &&
- (!maxYear || date.getFullYear() <= maxYear));
- },
-
- /* Provide the configuration settings for formatting/parsing. */
- _getFormatConfig: function(inst) {
- var shortYearCutoff = this._get(inst, "shortYearCutoff");
- shortYearCutoff = (typeof shortYearCutoff !== "string" ? shortYearCutoff :
- new Date().getFullYear() % 100 + parseInt(shortYearCutoff, 10));
- return {shortYearCutoff: shortYearCutoff,
- dayNamesShort: this._get(inst, "dayNamesShort"), dayNames: this._get(inst, "dayNames"),
- monthNamesShort: this._get(inst, "monthNamesShort"), monthNames: this._get(inst, "monthNames")};
- },
-
- /* Format the given date for display. */
- _formatDate: function(inst, day, month, year) {
- if (!day) {
- inst.currentDay = inst.selectedDay;
- inst.currentMonth = inst.selectedMonth;
- inst.currentYear = inst.selectedYear;
- }
- var date = (day ? (typeof day === "object" ? day :
- this._daylightSavingAdjust(new Date(year, month, day))) :
- this._daylightSavingAdjust(new Date(inst.currentYear, inst.currentMonth, inst.currentDay)));
- return this.formatDate(this._get(inst, "dateFormat"), date, this._getFormatConfig(inst));
- }
-});
-
-/*
- * Bind hover events for datepicker elements.
- * Done via delegate so the binding only occurs once in the lifetime of the parent div.
- * Global instActive, set by _updateDatepicker allows the handlers to find their way back to the active picker.
- */
-function bindHover(dpDiv) {
- var selector = "button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a";
- return dpDiv.delegate(selector, "mouseout", function() {
- $(this).removeClass("ui-state-hover");
- if (this.className.indexOf("ui-datepicker-prev") !== -1) {
- $(this).removeClass("ui-datepicker-prev-hover");
- }
- if (this.className.indexOf("ui-datepicker-next") !== -1) {
- $(this).removeClass("ui-datepicker-next-hover");
- }
- })
- .delegate(selector, "mouseover", function(){
- if (!$.datepicker._isDisabledDatepicker( instActive.inline ? dpDiv.parent()[0] : instActive.input[0])) {
- $(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");
- $(this).addClass("ui-state-hover");
- if (this.className.indexOf("ui-datepicker-prev") !== -1) {
- $(this).addClass("ui-datepicker-prev-hover");
- }
- if (this.className.indexOf("ui-datepicker-next") !== -1) {
- $(this).addClass("ui-datepicker-next-hover");
- }
- }
- });
-}
-
-/* jQuery extend now ignores nulls! */
-function extendRemove(target, props) {
- $.extend(target, props);
- for (var name in props) {
- if (props[name] == null) {
- target[name] = props[name];
- }
- }
- return target;
-}
-
-/* Invoke the datepicker functionality.
- @param options string - a command, optionally followed by additional parameters or
- Object - settings for attaching new datepicker functionality
- @return jQuery object */
-$.fn.datepicker = function(options){
-
- /* Verify an empty collection wasn't passed - Fixes #6976 */
- if ( !this.length ) {
- return this;
- }
-
- /* Initialise the date picker. */
- if (!$.datepicker.initialized) {
- $(document).mousedown($.datepicker._checkExternalClick);
- $.datepicker.initialized = true;
- }
-
- /* Append datepicker main container to body if not exist. */
- if ($("#"+$.datepicker._mainDivId).length === 0) {
- $("body").append($.datepicker.dpDiv);
- }
-
- var otherArgs = Array.prototype.slice.call(arguments, 1);
- if (typeof options === "string" && (options === "isDisabled" || options === "getDate" || options === "widget")) {
- return $.datepicker["_" + options + "Datepicker"].
- apply($.datepicker, [this[0]].concat(otherArgs));
- }
- if (options === "option" && arguments.length === 2 && typeof arguments[1] === "string") {
- return $.datepicker["_" + options + "Datepicker"].
- apply($.datepicker, [this[0]].concat(otherArgs));
- }
- return this.each(function() {
- typeof options === "string" ?
- $.datepicker["_" + options + "Datepicker"].
- apply($.datepicker, [this].concat(otherArgs)) :
- $.datepicker._attachDatepicker(this, options);
- });
-};
-
-$.datepicker = new Datepicker(); // singleton instance
-$.datepicker.initialized = false;
-$.datepicker.uuid = new Date().getTime();
-$.datepicker.version = "1.10.1";
-
-// Workaround for #4055
-// Add another global to avoid noConflict issues with inline event handlers
-window["DP_jQuery_" + dpuuid] = $;
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-var sizeRelatedOptions = {
- buttons: true,
- height: true,
- maxHeight: true,
- maxWidth: true,
- minHeight: true,
- minWidth: true,
- width: true
- },
- resizableRelatedOptions = {
- maxHeight: true,
- maxWidth: true,
- minHeight: true,
- minWidth: true
- };
-
-$.widget( "ui.dialog", {
- version: "1.10.1",
- options: {
- appendTo: "body",
- autoOpen: true,
- buttons: [],
- closeOnEscape: true,
- closeText: "close",
- dialogClass: "",
- draggable: true,
- hide: null,
- height: "auto",
- maxHeight: null,
- maxWidth: null,
- minHeight: 150,
- minWidth: 150,
- modal: false,
- position: {
- my: "center",
- at: "center",
- of: window,
- collision: "fit",
- // Ensure the titlebar is always visible
- using: function( pos ) {
- var topOffset = $( this ).css( pos ).offset().top;
- if ( topOffset < 0 ) {
- $( this ).css( "top", pos.top - topOffset );
- }
- }
- },
- resizable: true,
- show: null,
- title: null,
- width: 300,
-
- // callbacks
- beforeClose: null,
- close: null,
- drag: null,
- dragStart: null,
- dragStop: null,
- focus: null,
- open: null,
- resize: null,
- resizeStart: null,
- resizeStop: null
- },
-
- _create: function() {
- this.originalCss = {
- display: this.element[0].style.display,
- width: this.element[0].style.width,
- minHeight: this.element[0].style.minHeight,
- maxHeight: this.element[0].style.maxHeight,
- height: this.element[0].style.height
- };
- this.originalPosition = {
- parent: this.element.parent(),
- index: this.element.parent().children().index( this.element )
- };
- this.originalTitle = this.element.attr("title");
- this.options.title = this.options.title || this.originalTitle;
-
- this._createWrapper();
-
- this.element
- .show()
- .removeAttr("title")
- .addClass("ui-dialog-content ui-widget-content")
- .appendTo( this.uiDialog );
-
- this._createTitlebar();
- this._createButtonPane();
-
- if ( this.options.draggable && $.fn.draggable ) {
- this._makeDraggable();
- }
- if ( this.options.resizable && $.fn.resizable ) {
- this._makeResizable();
- }
-
- this._isOpen = false;
- },
-
- _init: function() {
- if ( this.options.autoOpen ) {
- this.open();
- }
- },
-
- _appendTo: function() {
- var element = this.options.appendTo;
- if ( element && (element.jquery || element.nodeType) ) {
- return $( element );
- }
- return this.document.find( element || "body" ).eq( 0 );
- },
-
- _destroy: function() {
- var next,
- originalPosition = this.originalPosition;
-
- this._destroyOverlay();
-
- this.element
- .removeUniqueId()
- .removeClass("ui-dialog-content ui-widget-content")
- .css( this.originalCss )
- // Without detaching first, the following becomes really slow
- .detach();
-
- this.uiDialog.stop( true, true ).remove();
-
- if ( this.originalTitle ) {
- this.element.attr( "title", this.originalTitle );
- }
-
- next = originalPosition.parent.children().eq( originalPosition.index );
- // Don't try to place the dialog next to itself (#8613)
- if ( next.length && next[0] !== this.element[0] ) {
- next.before( this.element );
- } else {
- originalPosition.parent.append( this.element );
- }
- },
-
- widget: function() {
- return this.uiDialog;
- },
-
- disable: $.noop,
- enable: $.noop,
-
- close: function( event ) {
- var that = this;
-
- if ( !this._isOpen || this._trigger( "beforeClose", event ) === false ) {
- return;
- }
-
- this._isOpen = false;
- this._destroyOverlay();
-
- if ( !this.opener.filter(":focusable").focus().length ) {
- // Hiding a focused element doesn't trigger blur in WebKit
- // so in case we have nothing to focus on, explicitly blur the active element
- // https://bugs.webkit.org/show_bug.cgi?id=47182
- $( this.document[0].activeElement ).blur();
- }
-
- this._hide( this.uiDialog, this.options.hide, function() {
- that._trigger( "close", event );
- });
- },
-
- isOpen: function() {
- return this._isOpen;
- },
-
- moveToTop: function() {
- this._moveToTop();
- },
-
- _moveToTop: function( event, silent ) {
- var moved = !!this.uiDialog.nextAll(":visible").insertBefore( this.uiDialog ).length;
- if ( moved && !silent ) {
- this._trigger( "focus", event );
- }
- return moved;
- },
-
- open: function() {
- var that = this;
- if ( this._isOpen ) {
- if ( this._moveToTop() ) {
- this._focusTabbable();
- }
- return;
- }
-
- this._isOpen = true;
- this.opener = $( this.document[0].activeElement );
-
- this._size();
- this._position();
- this._createOverlay();
- this._moveToTop( null, true );
- this._show( this.uiDialog, this.options.show, function() {
- that._focusTabbable();
- that._trigger("focus");
- });
-
- this._trigger("open");
- },
-
- _focusTabbable: function() {
- // Set focus to the first match:
- // 1. First element inside the dialog matching [autofocus]
- // 2. Tabbable element inside the content element
- // 3. Tabbable element inside the buttonpane
- // 4. The close button
- // 5. The dialog itself
- var hasFocus = this.element.find("[autofocus]");
- if ( !hasFocus.length ) {
- hasFocus = this.element.find(":tabbable");
- }
- if ( !hasFocus.length ) {
- hasFocus = this.uiDialogButtonPane.find(":tabbable");
- }
- if ( !hasFocus.length ) {
- hasFocus = this.uiDialogTitlebarClose.filter(":tabbable");
- }
- if ( !hasFocus.length ) {
- hasFocus = this.uiDialog;
- }
- hasFocus.eq( 0 ).focus();
- },
-
- _keepFocus: function( event ) {
- function checkFocus() {
- var activeElement = this.document[0].activeElement,
- isActive = this.uiDialog[0] === activeElement ||
- $.contains( this.uiDialog[0], activeElement );
- if ( !isActive ) {
- this._focusTabbable();
- }
- }
- event.preventDefault();
- checkFocus.call( this );
- // support: IE
- // IE <= 8 doesn't prevent moving focus even with event.preventDefault()
- // so we check again later
- this._delay( checkFocus );
- },
-
- _createWrapper: function() {
- this.uiDialog = $("<div>")
- .addClass( "ui-dialog ui-widget ui-widget-content ui-corner-all ui-front " +
- this.options.dialogClass )
- .hide()
- .attr({
- // Setting tabIndex makes the div focusable
- tabIndex: -1,
- role: "dialog"
- })
- .appendTo( this._appendTo() );
-
- this._on( this.uiDialog, {
- keydown: function( event ) {
- if ( this.options.closeOnEscape && !event.isDefaultPrevented() && event.keyCode &&
- event.keyCode === $.ui.keyCode.ESCAPE ) {
- event.preventDefault();
- this.close( event );
- return;
- }
-
- // prevent tabbing out of dialogs
- if ( event.keyCode !== $.ui.keyCode.TAB ) {
- return;
- }
- var tabbables = this.uiDialog.find(":tabbable"),
- first = tabbables.filter(":first"),
- last = tabbables.filter(":last");
-
- if ( ( event.target === last[0] || event.target === this.uiDialog[0] ) && !event.shiftKey ) {
- first.focus( 1 );
- event.preventDefault();
- } else if ( ( event.target === first[0] || event.target === this.uiDialog[0] ) && event.shiftKey ) {
- last.focus( 1 );
- event.preventDefault();
- }
- },
- mousedown: function( event ) {
- if ( this._moveToTop( event ) ) {
- this._focusTabbable();
- }
- }
- });
-
- // We assume that any existing aria-describedby attribute means
- // that the dialog content is marked up properly
- // otherwise we brute force the content as the description
- if ( !this.element.find("[aria-describedby]").length ) {
- this.uiDialog.attr({
- "aria-describedby": this.element.uniqueId().attr("id")
- });
- }
- },
-
- _createTitlebar: function() {
- var uiDialogTitle;
-
- this.uiDialogTitlebar = $("<div>")
- .addClass("ui-dialog-titlebar ui-widget-header ui-corner-all ui-helper-clearfix")
- .prependTo( this.uiDialog );
- this._on( this.uiDialogTitlebar, {
- mousedown: function( event ) {
- // Don't prevent click on close button (#8838)
- // Focusing a dialog that is partially scrolled out of view
- // causes the browser to scroll it into view, preventing the click event
- if ( !$( event.target ).closest(".ui-dialog-titlebar-close") ) {
- // Dialog isn't getting focus when dragging (#8063)
- this.uiDialog.focus();
- }
- }
- });
-
- this.uiDialogTitlebarClose = $("<button></button>")
- .button({
- label: this.options.closeText,
- icons: {
- primary: "ui-icon-closethick"
- },
- text: false
- })
- .addClass("ui-dialog-titlebar-close")
- .appendTo( this.uiDialogTitlebar );
- this._on( this.uiDialogTitlebarClose, {
- click: function( event ) {
- event.preventDefault();
- this.close( event );
- }
- });
-
- uiDialogTitle = $("<span>")
- .uniqueId()
- .addClass("ui-dialog-title")
- .prependTo( this.uiDialogTitlebar );
- this._title( uiDialogTitle );
-
- this.uiDialog.attr({
- "aria-labelledby": uiDialogTitle.attr("id")
- });
- },
-
- _title: function( title ) {
- if ( !this.options.title ) {
- title.html(" ");
- }
- title.text( this.options.title );
- },
-
- _createButtonPane: function() {
- this.uiDialogButtonPane = $("<div>")
- .addClass("ui-dialog-buttonpane ui-widget-content ui-helper-clearfix");
-
- this.uiButtonSet = $("<div>")
- .addClass("ui-dialog-buttonset")
- .appendTo( this.uiDialogButtonPane );
-
- this._createButtons();
- },
-
- _createButtons: function() {
- var that = this,
- buttons = this.options.buttons;
-
- // if we already have a button pane, remove it
- this.uiDialogButtonPane.remove();
- this.uiButtonSet.empty();
-
- if ( $.isEmptyObject( buttons ) || ($.isArray( buttons ) && !buttons.length) ) {
- this.uiDialog.removeClass("ui-dialog-buttons");
- return;
- }
-
- $.each( buttons, function( name, props ) {
- var click, buttonOptions;
- props = $.isFunction( props ) ?
- { click: props, text: name } :
- props;
- // Default to a non-submitting button
- props = $.extend( { type: "button" }, props );
- // Change the context for the click callback to be the main element
- click = props.click;
- props.click = function() {
- click.apply( that.element[0], arguments );
- };
- buttonOptions = {
- icons: props.icons,
- text: props.showText
- };
- delete props.icons;
- delete props.showText;
- $( "<button></button>", props )
- .button( buttonOptions )
- .appendTo( that.uiButtonSet );
- });
- this.uiDialog.addClass("ui-dialog-buttons");
- this.uiDialogButtonPane.appendTo( this.uiDialog );
- },
-
- _makeDraggable: function() {
- var that = this,
- options = this.options;
-
- function filteredUi( ui ) {
- return {
- position: ui.position,
- offset: ui.offset
- };
- }
-
- this.uiDialog.draggable({
- cancel: ".ui-dialog-content, .ui-dialog-titlebar-close",
- handle: ".ui-dialog-titlebar",
- containment: "document",
- start: function( event, ui ) {
- $( this ).addClass("ui-dialog-dragging");
- that._blockFrames();
- that._trigger( "dragStart", event, filteredUi( ui ) );
- },
- drag: function( event, ui ) {
- that._trigger( "drag", event, filteredUi( ui ) );
- },
- stop: function( event, ui ) {
- options.position = [
- ui.position.left - that.document.scrollLeft(),
- ui.position.top - that.document.scrollTop()
- ];
- $( this ).removeClass("ui-dialog-dragging");
- that._unblockFrames();
- that._trigger( "dragStop", event, filteredUi( ui ) );
- }
- });
- },
-
- _makeResizable: function() {
- var that = this,
- options = this.options,
- handles = options.resizable,
- // .ui-resizable has position: relative defined in the stylesheet
- // but dialogs have to use absolute or fixed positioning
- position = this.uiDialog.css("position"),
- resizeHandles = typeof handles === "string" ?
- handles :
- "n,e,s,w,se,sw,ne,nw";
-
- function filteredUi( ui ) {
- return {
- originalPosition: ui.originalPosition,
- originalSize: ui.originalSize,
- position: ui.position,
- size: ui.size
- };
- }
-
- this.uiDialog.resizable({
- cancel: ".ui-dialog-content",
- containment: "document",
- alsoResize: this.element,
- maxWidth: options.maxWidth,
- maxHeight: options.maxHeight,
- minWidth: options.minWidth,
- minHeight: this._minHeight(),
- handles: resizeHandles,
- start: function( event, ui ) {
- $( this ).addClass("ui-dialog-resizing");
- that._blockFrames();
- that._trigger( "resizeStart", event, filteredUi( ui ) );
- },
- resize: function( event, ui ) {
- that._trigger( "resize", event, filteredUi( ui ) );
- },
- stop: function( event, ui ) {
- options.height = $( this ).height();
- options.width = $( this ).width();
- $( this ).removeClass("ui-dialog-resizing");
- that._unblockFrames();
- that._trigger( "resizeStop", event, filteredUi( ui ) );
- }
- })
- .css( "position", position );
- },
-
- _minHeight: function() {
- var options = this.options;
-
- return options.height === "auto" ?
- options.minHeight :
- Math.min( options.minHeight, options.height );
- },
-
- _position: function() {
- // Need to show the dialog to get the actual offset in the position plugin
- var isVisible = this.uiDialog.is(":visible");
- if ( !isVisible ) {
- this.uiDialog.show();
- }
- this.uiDialog.position( this.options.position );
- if ( !isVisible ) {
- this.uiDialog.hide();
- }
- },
-
- _setOptions: function( options ) {
- var that = this,
- resize = false,
- resizableOptions = {};
-
- $.each( options, function( key, value ) {
- that._setOption( key, value );
-
- if ( key in sizeRelatedOptions ) {
- resize = true;
- }
- if ( key in resizableRelatedOptions ) {
- resizableOptions[ key ] = value;
- }
- });
-
- if ( resize ) {
- this._size();
- this._position();
- }
- if ( this.uiDialog.is(":data(ui-resizable)") ) {
- this.uiDialog.resizable( "option", resizableOptions );
- }
- },
-
- _setOption: function( key, value ) {
- /*jshint maxcomplexity:15*/
- var isDraggable, isResizable,
- uiDialog = this.uiDialog;
-
- if ( key === "dialogClass" ) {
- uiDialog
- .removeClass( this.options.dialogClass )
- .addClass( value );
- }
-
- if ( key === "disabled" ) {
- return;
- }
-
- this._super( key, value );
-
- if ( key === "appendTo" ) {
- this.uiDialog.appendTo( this._appendTo() );
- }
-
- if ( key === "buttons" ) {
- this._createButtons();
- }
-
- if ( key === "closeText" ) {
- this.uiDialogTitlebarClose.button({
- // Ensure that we always pass a string
- label: "" + value
- });
- }
-
- if ( key === "draggable" ) {
- isDraggable = uiDialog.is(":data(ui-draggable)");
- if ( isDraggable && !value ) {
- uiDialog.draggable("destroy");
- }
-
- if ( !isDraggable && value ) {
- this._makeDraggable();
- }
- }
-
- if ( key === "position" ) {
- this._position();
- }
-
- if ( key === "resizable" ) {
- // currently resizable, becoming non-resizable
- isResizable = uiDialog.is(":data(ui-resizable)");
- if ( isResizable && !value ) {
- uiDialog.resizable("destroy");
- }
-
- // currently resizable, changing handles
- if ( isResizable && typeof value === "string" ) {
- uiDialog.resizable( "option", "handles", value );
- }
-
- // currently non-resizable, becoming resizable
- if ( !isResizable && value !== false ) {
- this._makeResizable();
- }
- }
-
- if ( key === "title" ) {
- this._title( this.uiDialogTitlebar.find(".ui-dialog-title") );
- }
- },
-
- _size: function() {
- // If the user has resized the dialog, the .ui-dialog and .ui-dialog-content
- // divs will both have width and height set, so we need to reset them
- var nonContentHeight, minContentHeight, maxContentHeight,
- options = this.options;
-
- // Reset content sizing
- this.element.show().css({
- width: "auto",
- minHeight: 0,
- maxHeight: "none",
- height: 0
- });
-
- if ( options.minWidth > options.width ) {
- options.width = options.minWidth;
- }
-
- // reset wrapper sizing
- // determine the height of all the non-content elements
- nonContentHeight = this.uiDialog.css({
- height: "auto",
- width: options.width
- })
- .outerHeight();
- minContentHeight = Math.max( 0, options.minHeight - nonContentHeight );
- maxContentHeight = typeof options.maxHeight === "number" ?
- Math.max( 0, options.maxHeight - nonContentHeight ) :
- "none";
-
- if ( options.height === "auto" ) {
- this.element.css({
- minHeight: minContentHeight,
- maxHeight: maxContentHeight,
- height: "auto"
- });
- } else {
- this.element.height( Math.max( 0, options.height - nonContentHeight ) );
- }
-
- if (this.uiDialog.is(":data(ui-resizable)") ) {
- this.uiDialog.resizable( "option", "minHeight", this._minHeight() );
- }
- },
-
- _blockFrames: function() {
- this.iframeBlocks = this.document.find( "iframe" ).map(function() {
- var iframe = $( this );
-
- return $( "<div>" )
- .css({
- position: "absolute",
- width: iframe.outerWidth(),
- height: iframe.outerHeight()
- })
- .appendTo( iframe.parent() )
- .offset( iframe.offset() )[0];
- });
- },
-
- _unblockFrames: function() {
- if ( this.iframeBlocks ) {
- this.iframeBlocks.remove();
- delete this.iframeBlocks;
- }
- },
-
- _createOverlay: function() {
- if ( !this.options.modal ) {
- return;
- }
-
- if ( !$.ui.dialog.overlayInstances ) {
- // Prevent use of anchors and inputs.
- // We use a delay in case the overlay is created from an
- // event that we're going to be cancelling. (#2804)
- this._delay(function() {
- // Handle .dialog().dialog("close") (#4065)
- if ( $.ui.dialog.overlayInstances ) {
- this.document.bind( "focusin.dialog", function( event ) {
- if ( !$( event.target ).closest(".ui-dialog").length &&
- // TODO: Remove hack when datepicker implements
- // the .ui-front logic (#8989)
- !$( event.target ).closest(".ui-datepicker").length ) {
- event.preventDefault();
- $(".ui-dialog:visible:last .ui-dialog-content")
- .data("ui-dialog")._focusTabbable();
- }
- });
- }
- });
- }
-
- this.overlay = $("<div>")
- .addClass("ui-widget-overlay ui-front")
- .appendTo( this._appendTo() );
- this._on( this.overlay, {
- mousedown: "_keepFocus"
- });
- $.ui.dialog.overlayInstances++;
- },
-
- _destroyOverlay: function() {
- if ( !this.options.modal ) {
- return;
- }
-
- if ( this.overlay ) {
- $.ui.dialog.overlayInstances--;
-
- if ( !$.ui.dialog.overlayInstances ) {
- this.document.unbind( "focusin.dialog" );
- }
- this.overlay.remove();
- this.overlay = null;
- }
- }
-});
-
-$.ui.dialog.overlayInstances = 0;
-
-// DEPRECATED
-if ( $.uiBackCompat !== false ) {
- // position option with array notation
- // just override with old implementation
- $.widget( "ui.dialog", $.ui.dialog, {
- _position: function() {
- var position = this.options.position,
- myAt = [],
- offset = [ 0, 0 ],
- isVisible;
-
- if ( position ) {
- if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
- myAt = position.split ? position.split(" ") : [ position[0], position[1] ];
- if ( myAt.length === 1 ) {
- myAt[1] = myAt[0];
- }
-
- $.each( [ "left", "top" ], function( i, offsetPosition ) {
- if ( +myAt[ i ] === myAt[ i ] ) {
- offset[ i ] = myAt[ i ];
- myAt[ i ] = offsetPosition;
- }
- });
-
- position = {
- my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
- myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
- at: myAt.join(" ")
- };
- }
-
- position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
- } else {
- position = $.ui.dialog.prototype.options.position;
- }
-
- // need to show the dialog to get the actual offset in the position plugin
- isVisible = this.uiDialog.is(":visible");
- if ( !isVisible ) {
- this.uiDialog.show();
- }
- this.uiDialog.position( position );
- if ( !isVisible ) {
- this.uiDialog.hide();
- }
- }
- });
-}
-
-}( jQuery ) );
-
-(function( $, undefined ) {
-
-var rvertical = /up|down|vertical/,
- rpositivemotion = /up|left|vertical|horizontal/;
-
-$.effects.effect.blind = function( o, done ) {
- // Create element
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "hide" ),
- direction = o.direction || "up",
- vertical = rvertical.test( direction ),
- ref = vertical ? "height" : "width",
- ref2 = vertical ? "top" : "left",
- motion = rpositivemotion.test( direction ),
- animation = {},
- show = mode === "show",
- wrapper, distance, margin;
-
- // if already wrapped, the wrapper's properties are my property. #6245
- if ( el.parent().is( ".ui-effects-wrapper" ) ) {
- $.effects.save( el.parent(), props );
- } else {
- $.effects.save( el, props );
- }
- el.show();
- wrapper = $.effects.createWrapper( el ).css({
- overflow: "hidden"
- });
-
- distance = wrapper[ ref ]();
- margin = parseFloat( wrapper.css( ref2 ) ) || 0;
-
- animation[ ref ] = show ? distance : 0;
- if ( !motion ) {
- el
- .css( vertical ? "bottom" : "right", 0 )
- .css( vertical ? "top" : "left", "auto" )
- .css({ position: "absolute" });
-
- animation[ ref2 ] = show ? margin : distance + margin;
- }
-
- // start at 0 if we are showing
- if ( show ) {
- wrapper.css( ref, 0 );
- if ( ! motion ) {
- wrapper.css( ref2, margin + distance );
- }
- }
-
- // Animate
- wrapper.animate( animation, {
- duration: o.duration,
- easing: o.easing,
- queue: false,
- complete: function() {
- if ( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- }
- });
-
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.bounce = function( o, done ) {
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
-
- // defaults:
- mode = $.effects.setMode( el, o.mode || "effect" ),
- hide = mode === "hide",
- show = mode === "show",
- direction = o.direction || "up",
- distance = o.distance,
- times = o.times || 5,
-
- // number of internal animations
- anims = times * 2 + ( show || hide ? 1 : 0 ),
- speed = o.duration / anims,
- easing = o.easing,
-
- // utility:
- ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
- motion = ( direction === "up" || direction === "left" ),
- i,
- upAnim,
- downAnim,
-
- // we will need to re-assemble the queue to stack our animations in place
- queue = el.queue(),
- queuelen = queue.length;
-
- // Avoid touching opacity to prevent clearType and PNG issues in IE
- if ( show || hide ) {
- props.push( "opacity" );
- }
-
- $.effects.save( el, props );
- el.show();
- $.effects.createWrapper( el ); // Create Wrapper
-
- // default distance for the BIGGEST bounce is the outer Distance / 3
- if ( !distance ) {
- distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
- }
-
- if ( show ) {
- downAnim = { opacity: 1 };
- downAnim[ ref ] = 0;
-
- // if we are showing, force opacity 0 and set the initial position
- // then do the "first" animation
- el.css( "opacity", 0 )
- .css( ref, motion ? -distance * 2 : distance * 2 )
- .animate( downAnim, speed, easing );
- }
-
- // start at the smallest distance if we are hiding
- if ( hide ) {
- distance = distance / Math.pow( 2, times - 1 );
- }
-
- downAnim = {};
- downAnim[ ref ] = 0;
- // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
- for ( i = 0; i < times; i++ ) {
- upAnim = {};
- upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
-
- el.animate( upAnim, speed, easing )
- .animate( downAnim, speed, easing );
-
- distance = hide ? distance * 2 : distance / 2;
- }
-
- // Last Bounce when Hiding
- if ( hide ) {
- upAnim = { opacity: 0 };
- upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
-
- el.animate( upAnim, speed, easing );
- }
-
- el.queue(function() {
- if ( hide ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- });
-
- // inject all the animations we just queued to be first in line (after "inprogress")
- if ( queuelen > 1) {
- queue.splice.apply( queue,
- [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
- }
- el.dequeue();
-
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.clip = function( o, done ) {
- // Create element
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "hide" ),
- show = mode === "show",
- direction = o.direction || "vertical",
- vert = direction === "vertical",
- size = vert ? "height" : "width",
- position = vert ? "top" : "left",
- animation = {},
- wrapper, animate, distance;
-
- // Save & Show
- $.effects.save( el, props );
- el.show();
-
- // Create Wrapper
- wrapper = $.effects.createWrapper( el ).css({
- overflow: "hidden"
- });
- animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
- distance = animate[ size ]();
-
- // Shift
- if ( show ) {
- animate.css( size, 0 );
- animate.css( position, distance / 2 );
- }
-
- // Create Animation Object:
- animation[ size ] = show ? distance : 0;
- animation[ position ] = show ? 0 : distance / 2;
-
- // Animate
- animate.animate( animation, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( !show ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- }
- });
-
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.drop = function( o, done ) {
-
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "hide" ),
- show = mode === "show",
- direction = o.direction || "left",
- ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
- motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
- animation = {
- opacity: show ? 1 : 0
- },
- distance;
-
- // Adjust
- $.effects.save( el, props );
- el.show();
- $.effects.createWrapper( el );
-
- distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
-
- if ( show ) {
- el
- .css( "opacity", 0 )
- .css( ref, motion === "pos" ? -distance : distance );
- }
-
- // Animation
- animation[ ref ] = ( show ?
- ( motion === "pos" ? "+=" : "-=" ) :
- ( motion === "pos" ? "-=" : "+=" ) ) +
- distance;
-
- // Animate
- el.animate( animation, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- }
- });
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.explode = function( o, done ) {
-
- var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
- cells = rows,
- el = $( this ),
- mode = $.effects.setMode( el, o.mode || "hide" ),
- show = mode === "show",
-
- // show and then visibility:hidden the element before calculating offset
- offset = el.show().css( "visibility", "hidden" ).offset(),
-
- // width and height of a piece
- width = Math.ceil( el.outerWidth() / cells ),
- height = Math.ceil( el.outerHeight() / rows ),
- pieces = [],
-
- // loop
- i, j, left, top, mx, my;
-
- // children animate complete:
- function childComplete() {
- pieces.push( this );
- if ( pieces.length === rows * cells ) {
- animComplete();
- }
- }
-
- // clone the element for each row and cell.
- for( i = 0; i < rows ; i++ ) { // ===>
- top = offset.top + i * height;
- my = i - ( rows - 1 ) / 2 ;
-
- for( j = 0; j < cells ; j++ ) { // |||
- left = offset.left + j * width;
- mx = j - ( cells - 1 ) / 2 ;
-
- // Create a clone of the now hidden main element that will be absolute positioned
- // within a wrapper div off the -left and -top equal to size of our pieces
- el
- .clone()
- .appendTo( "body" )
- .wrap( "<div></div>" )
- .css({
- position: "absolute",
- visibility: "visible",
- left: -j * width,
- top: -i * height
- })
-
- // select the wrapper - make it overflow: hidden and absolute positioned based on
- // where the original was located +left and +top equal to the size of pieces
- .parent()
- .addClass( "ui-effects-explode" )
- .css({
- position: "absolute",
- overflow: "hidden",
- width: width,
- height: height,
- left: left + ( show ? mx * width : 0 ),
- top: top + ( show ? my * height : 0 ),
- opacity: show ? 0 : 1
- }).animate({
- left: left + ( show ? 0 : mx * width ),
- top: top + ( show ? 0 : my * height ),
- opacity: show ? 1 : 0
- }, o.duration || 500, o.easing, childComplete );
- }
- }
-
- function animComplete() {
- el.css({
- visibility: "visible"
- });
- $( pieces ).remove();
- if ( !show ) {
- el.hide();
- }
- done();
- }
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.fade = function( o, done ) {
- var el = $( this ),
- mode = $.effects.setMode( el, o.mode || "toggle" );
-
- el.animate({
- opacity: mode
- }, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: done
- });
-};
-
-})( jQuery );
-
-(function( $, undefined ) {
-
-$.effects.effect.fold = function( o, done ) {
-
- // Create element
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "hide" ),
- show = mode === "show",
- hide = mode === "hide",
- size = o.size || 15,
- percent = /([0-9]+)%/.exec( size ),
- horizFirst = !!o.horizFirst,
- widthFirst = show !== horizFirst,
- ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
- duration = o.duration / 2,
- wrapper, distance,
- animation1 = {},
- animation2 = {};
-
- $.effects.save( el, props );
- el.show();
-
- // Create Wrapper
- wrapper = $.effects.createWrapper( el ).css({
- overflow: "hidden"
- });
- distance = widthFirst ?
- [ wrapper.width(), wrapper.height() ] :
- [ wrapper.height(), wrapper.width() ];
-
- if ( percent ) {
- size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
- }
- if ( show ) {
- wrapper.css( horizFirst ? {
- height: 0,
- width: size
- } : {
- height: size,
- width: 0
- });
- }
-
- // Animation
- animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
- animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
-
- // Animate
- wrapper
- .animate( animation1, duration, o.easing )
- .animate( animation2, duration, o.easing, function() {
- if ( hide ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- });
-
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.highlight = function( o, done ) {
- var elem = $( this ),
- props = [ "backgroundImage", "backgroundColor", "opacity" ],
- mode = $.effects.setMode( elem, o.mode || "show" ),
- animation = {
- backgroundColor: elem.css( "backgroundColor" )
- };
-
- if (mode === "hide") {
- animation.opacity = 0;
- }
-
- $.effects.save( elem, props );
-
- elem
- .show()
- .css({
- backgroundImage: "none",
- backgroundColor: o.color || "#ffff99"
- })
- .animate( animation, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( mode === "hide" ) {
- elem.hide();
- }
- $.effects.restore( elem, props );
- done();
- }
- });
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.pulsate = function( o, done ) {
- var elem = $( this ),
- mode = $.effects.setMode( elem, o.mode || "show" ),
- show = mode === "show",
- hide = mode === "hide",
- showhide = ( show || mode === "hide" ),
-
- // showing or hiding leaves of the "last" animation
- anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
- duration = o.duration / anims,
- animateTo = 0,
- queue = elem.queue(),
- queuelen = queue.length,
- i;
-
- if ( show || !elem.is(":visible")) {
- elem.css( "opacity", 0 ).show();
- animateTo = 1;
- }
-
- // anims - 1 opacity "toggles"
- for ( i = 1; i < anims; i++ ) {
- elem.animate({
- opacity: animateTo
- }, duration, o.easing );
- animateTo = 1 - animateTo;
- }
-
- elem.animate({
- opacity: animateTo
- }, duration, o.easing);
-
- elem.queue(function() {
- if ( hide ) {
- elem.hide();
- }
- done();
- });
-
- // We just queued up "anims" animations, we need to put them next in the queue
- if ( queuelen > 1 ) {
- queue.splice.apply( queue,
- [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
- }
- elem.dequeue();
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.puff = function( o, done ) {
- var elem = $( this ),
- mode = $.effects.setMode( elem, o.mode || "hide" ),
- hide = mode === "hide",
- percent = parseInt( o.percent, 10 ) || 150,
- factor = percent / 100,
- original = {
- height: elem.height(),
- width: elem.width(),
- outerHeight: elem.outerHeight(),
- outerWidth: elem.outerWidth()
- };
-
- $.extend( o, {
- effect: "scale",
- queue: false,
- fade: true,
- mode: mode,
- complete: done,
- percent: hide ? percent : 100,
- from: hide ?
- original :
- {
- height: original.height * factor,
- width: original.width * factor,
- outerHeight: original.outerHeight * factor,
- outerWidth: original.outerWidth * factor
- }
- });
-
- elem.effect( o );
-};
-
-$.effects.effect.scale = function( o, done ) {
-
- // Create element
- var el = $( this ),
- options = $.extend( true, {}, o ),
- mode = $.effects.setMode( el, o.mode || "effect" ),
- percent = parseInt( o.percent, 10 ) ||
- ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
- direction = o.direction || "both",
- origin = o.origin,
- original = {
- height: el.height(),
- width: el.width(),
- outerHeight: el.outerHeight(),
- outerWidth: el.outerWidth()
- },
- factor = {
- y: direction !== "horizontal" ? (percent / 100) : 1,
- x: direction !== "vertical" ? (percent / 100) : 1
- };
-
- // We are going to pass this effect to the size effect:
- options.effect = "size";
- options.queue = false;
- options.complete = done;
-
- // Set default origin and restore for show/hide
- if ( mode !== "effect" ) {
- options.origin = origin || ["middle","center"];
- options.restore = true;
- }
-
- options.from = o.from || ( mode === "show" ? {
- height: 0,
- width: 0,
- outerHeight: 0,
- outerWidth: 0
- } : original );
- options.to = {
- height: original.height * factor.y,
- width: original.width * factor.x,
- outerHeight: original.outerHeight * factor.y,
- outerWidth: original.outerWidth * factor.x
- };
-
- // Fade option to support puff
- if ( options.fade ) {
- if ( mode === "show" ) {
- options.from.opacity = 0;
- options.to.opacity = 1;
- }
- if ( mode === "hide" ) {
- options.from.opacity = 1;
- options.to.opacity = 0;
- }
- }
-
- // Animate
- el.effect( options );
-
-};
-
-$.effects.effect.size = function( o, done ) {
-
- // Create element
- var original, baseline, factor,
- el = $( this ),
- props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
-
- // Always restore
- props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
-
- // Copy for children
- props2 = [ "width", "height", "overflow" ],
- cProps = [ "fontSize" ],
- vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
- hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
-
- // Set options
- mode = $.effects.setMode( el, o.mode || "effect" ),
- restore = o.restore || mode !== "effect",
- scale = o.scale || "both",
- origin = o.origin || [ "middle", "center" ],
- position = el.css( "position" ),
- props = restore ? props0 : props1,
- zero = {
- height: 0,
- width: 0,
- outerHeight: 0,
- outerWidth: 0
- };
-
- if ( mode === "show" ) {
- el.show();
- }
- original = {
- height: el.height(),
- width: el.width(),
- outerHeight: el.outerHeight(),
- outerWidth: el.outerWidth()
- };
-
- if ( o.mode === "toggle" && mode === "show" ) {
- el.from = o.to || zero;
- el.to = o.from || original;
- } else {
- el.from = o.from || ( mode === "show" ? zero : original );
- el.to = o.to || ( mode === "hide" ? zero : original );
- }
-
- // Set scaling factor
- factor = {
- from: {
- y: el.from.height / original.height,
- x: el.from.width / original.width
- },
- to: {
- y: el.to.height / original.height,
- x: el.to.width / original.width
- }
- };
-
- // Scale the css box
- if ( scale === "box" || scale === "both" ) {
-
- // Vertical props scaling
- if ( factor.from.y !== factor.to.y ) {
- props = props.concat( vProps );
- el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
- el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
- }
-
- // Horizontal props scaling
- if ( factor.from.x !== factor.to.x ) {
- props = props.concat( hProps );
- el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
- el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
- }
- }
-
- // Scale the content
- if ( scale === "content" || scale === "both" ) {
-
- // Vertical props scaling
- if ( factor.from.y !== factor.to.y ) {
- props = props.concat( cProps ).concat( props2 );
- el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
- el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
- }
- }
-
- $.effects.save( el, props );
- el.show();
- $.effects.createWrapper( el );
- el.css( "overflow", "hidden" ).css( el.from );
-
- // Adjust
- if (origin) { // Calculate baseline shifts
- baseline = $.effects.getBaseline( origin, original );
- el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
- el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
- el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
- el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
- }
- el.css( el.from ); // set top & left
-
- // Animate
- if ( scale === "content" || scale === "both" ) { // Scale the children
-
- // Add margins/font-size
- vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
- hProps = hProps.concat([ "marginLeft", "marginRight" ]);
- props2 = props0.concat(vProps).concat(hProps);
-
- el.find( "*[width]" ).each( function(){
- var child = $( this ),
- c_original = {
- height: child.height(),
- width: child.width(),
- outerHeight: child.outerHeight(),
- outerWidth: child.outerWidth()
- };
- if (restore) {
- $.effects.save(child, props2);
- }
-
- child.from = {
- height: c_original.height * factor.from.y,
- width: c_original.width * factor.from.x,
- outerHeight: c_original.outerHeight * factor.from.y,
- outerWidth: c_original.outerWidth * factor.from.x
- };
- child.to = {
- height: c_original.height * factor.to.y,
- width: c_original.width * factor.to.x,
- outerHeight: c_original.height * factor.to.y,
- outerWidth: c_original.width * factor.to.x
- };
-
- // Vertical props scaling
- if ( factor.from.y !== factor.to.y ) {
- child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
- child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
- }
-
- // Horizontal props scaling
- if ( factor.from.x !== factor.to.x ) {
- child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
- child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
- }
-
- // Animate children
- child.css( child.from );
- child.animate( child.to, o.duration, o.easing, function() {
-
- // Restore children
- if ( restore ) {
- $.effects.restore( child, props2 );
- }
- });
- });
- }
-
- // Animate
- el.animate( el.to, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( el.to.opacity === 0 ) {
- el.css( "opacity", el.from.opacity );
- }
- if( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- if ( !restore ) {
-
- // we need to calculate our new positioning based on the scaling
- if ( position === "static" ) {
- el.css({
- position: "relative",
- top: el.to.top,
- left: el.to.left
- });
- } else {
- $.each([ "top", "left" ], function( idx, pos ) {
- el.css( pos, function( _, str ) {
- var val = parseInt( str, 10 ),
- toRef = idx ? el.to.left : el.to.top;
-
- // if original was "auto", recalculate the new value from wrapper
- if ( str === "auto" ) {
- return toRef + "px";
- }
-
- return val + toRef + "px";
- });
- });
- }
- }
-
- $.effects.removeWrapper( el );
- done();
- }
- });
-
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.shake = function( o, done ) {
-
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
- mode = $.effects.setMode( el, o.mode || "effect" ),
- direction = o.direction || "left",
- distance = o.distance || 20,
- times = o.times || 3,
- anims = times * 2 + 1,
- speed = Math.round(o.duration/anims),
- ref = (direction === "up" || direction === "down") ? "top" : "left",
- positiveMotion = (direction === "up" || direction === "left"),
- animation = {},
- animation1 = {},
- animation2 = {},
- i,
-
- // we will need to re-assemble the queue to stack our animations in place
- queue = el.queue(),
- queuelen = queue.length;
-
- $.effects.save( el, props );
- el.show();
- $.effects.createWrapper( el );
-
- // Animation
- animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
- animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
- animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
-
- // Animate
- el.animate( animation, speed, o.easing );
-
- // Shakes
- for ( i = 1; i < times; i++ ) {
- el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
- }
- el
- .animate( animation1, speed, o.easing )
- .animate( animation, speed / 2, o.easing )
- .queue(function() {
- if ( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- });
-
- // inject all the animations we just queued to be first in line (after "inprogress")
- if ( queuelen > 1) {
- queue.splice.apply( queue,
- [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
- }
- el.dequeue();
-
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.slide = function( o, done ) {
-
- // Create element
- var el = $( this ),
- props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
- mode = $.effects.setMode( el, o.mode || "show" ),
- show = mode === "show",
- direction = o.direction || "left",
- ref = (direction === "up" || direction === "down") ? "top" : "left",
- positiveMotion = (direction === "up" || direction === "left"),
- distance,
- animation = {};
-
- // Adjust
- $.effects.save( el, props );
- el.show();
- distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
-
- $.effects.createWrapper( el ).css({
- overflow: "hidden"
- });
-
- if ( show ) {
- el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
- }
-
- // Animation
- animation[ ref ] = ( show ?
- ( positiveMotion ? "+=" : "-=") :
- ( positiveMotion ? "-=" : "+=")) +
- distance;
-
- // Animate
- el.animate( animation, {
- queue: false,
- duration: o.duration,
- easing: o.easing,
- complete: function() {
- if ( mode === "hide" ) {
- el.hide();
- }
- $.effects.restore( el, props );
- $.effects.removeWrapper( el );
- done();
- }
- });
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.effects.effect.transfer = function( o, done ) {
- var elem = $( this ),
- target = $( o.to ),
- targetFixed = target.css( "position" ) === "fixed",
- body = $("body"),
- fixTop = targetFixed ? body.scrollTop() : 0,
- fixLeft = targetFixed ? body.scrollLeft() : 0,
- endPosition = target.offset(),
- animation = {
- top: endPosition.top - fixTop ,
- left: endPosition.left - fixLeft ,
- height: target.innerHeight(),
- width: target.innerWidth()
- },
- startPosition = elem.offset(),
- transfer = $( "<div class='ui-effects-transfer'></div>" )
- .appendTo( document.body )
- .addClass( o.className )
- .css({
- top: startPosition.top - fixTop ,
- left: startPosition.left - fixLeft ,
- height: elem.innerHeight(),
- width: elem.innerWidth(),
- position: targetFixed ? "fixed" : "absolute"
- })
- .animate( animation, o.duration, o.easing, function() {
- transfer.remove();
- done();
- });
-};
-
-})(jQuery);
-
-(function( $, undefined ) {
-
-$.widget( "ui.menu", {
- version: "1.10.1",
- defaultElement: "<ul>",
- delay: 300,
- options: {
- icons: {
- submenu: "ui-icon-carat-1-e"
- },
- menus: "ul",
- position: {
- my: "left top",
- at: "right top"
- },
- role: "menu",
-
- // callbacks
- blur: null,
- focus: null,
- select: null
- },
-
- _create: function() {
- this.activeMenu = this.element;
- // flag used to prevent firing of the click handler
- // as the event bubbles up through nested menus
- this.mouseHandled = false;
- this.element
- .uniqueId()
- .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
- .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
- .attr({
- role: this.options.role,
- tabIndex: 0
- })
- // need to catch all clicks on disabled menu
- // not possible through _on
- .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
- if ( this.options.disabled ) {
- event.preventDefault();
- }
- }, this ));
-
- if ( this.options.disabled ) {
- this.element
- .addClass( "ui-state-disabled" )
- .attr( "aria-disabled", "true" );
- }
-
- this._on({
- // Prevent focus from sticking to links inside menu after clicking
- // them (focus should always stay on UL during navigation).
- "mousedown .ui-menu-item > a": function( event ) {
- event.preventDefault();
- },
- "click .ui-state-disabled > a": function( event ) {
- event.preventDefault();
- },
- "click .ui-menu-item:has(a)": function( event ) {
- var target = $( event.target ).closest( ".ui-menu-item" );
- if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
- this.mouseHandled = true;
-
- this.select( event );
- // Open submenu on click
- if ( target.has( ".ui-menu" ).length ) {
- this.expand( event );
- } else if ( !this.element.is( ":focus" ) ) {
- // Redirect focus to the menu
- this.element.trigger( "focus", [ true ] );
-
- // If the active item is on the top level, let it stay active.
- // Otherwise, blur the active item since it is no longer visible.
- if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
- clearTimeout( this.timer );
- }
- }
- }
- },
- "mouseenter .ui-menu-item": function( event ) {
- var target = $( event.currentTarget );
- // Remove ui-state-active class from siblings of the newly focused menu item
- // to avoid a jump caused by adjacent elements both having a class with a border
- target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
- this.focus( event, target );
- },
- mouseleave: "collapseAll",
- "mouseleave .ui-menu": "collapseAll",
- focus: function( event, keepActiveItem ) {
- // If there's already an active item, keep it active
- // If not, activate the first item
- var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
-
- if ( !keepActiveItem ) {
- this.focus( event, item );
- }
- },
- blur: function( event ) {
- this._delay(function() {
- if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
- this.collapseAll( event );
- }
- });
- },
- keydown: "_keydown"
- });
-
- this.refresh();
-
- // Clicks outside of a menu collapse any open menus
- this._on( this.document, {
- click: function( event ) {
- if ( !$( event.target ).closest( ".ui-menu" ).length ) {
- this.collapseAll( event );
- }
-
- // Reset the mouseHandled flag
- this.mouseHandled = false;
- }
- });
- },
-
- _destroy: function() {
- // Destroy (sub)menus
- this.element
- .removeAttr( "aria-activedescendant" )
- .find( ".ui-menu" ).addBack()
- .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
- .removeAttr( "role" )
- .removeAttr( "tabIndex" )
- .removeAttr( "aria-labelledby" )
- .removeAttr( "aria-expanded" )
- .removeAttr( "aria-hidden" )
- .removeAttr( "aria-disabled" )
- .removeUniqueId()
- .show();
-
- // Destroy menu items
- this.element.find( ".ui-menu-item" )
- .removeClass( "ui-menu-item" )
- .removeAttr( "role" )
- .removeAttr( "aria-disabled" )
- .children( "a" )
- .removeUniqueId()
- .removeClass( "ui-corner-all ui-state-hover" )
- .removeAttr( "tabIndex" )
- .removeAttr( "role" )
- .removeAttr( "aria-haspopup" )
- .children().each( function() {
- var elem = $( this );
- if ( elem.data( "ui-menu-submenu-carat" ) ) {
- elem.remove();
- }
- });
-
- // Destroy menu dividers
- this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
- },
-
- _keydown: function( event ) {
- /*jshint maxcomplexity:20*/
- var match, prev, character, skip, regex,
- preventDefault = true;
-
- function escape( value ) {
- return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
- }
-
- switch ( event.keyCode ) {
- case $.ui.keyCode.PAGE_UP:
- this.previousPage( event );
- break;
- case $.ui.keyCode.PAGE_DOWN:
- this.nextPage( event );
- break;
- case $.ui.keyCode.HOME:
- this._move( "first", "first", event );
- break;
- case $.ui.keyCode.END:
- this._move( "last", "last", event );
- break;
- case $.ui.keyCode.UP:
- this.previous( event );
- break;
- case $.ui.keyCode.DOWN:
- this.next( event );
- break;
- case $.ui.keyCode.LEFT:
- this.collapse( event );
- break;
- case $.ui.keyCode.RIGHT:
- if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
- this.expand( event );
- }
- break;
- case $.ui.keyCode.ENTER:
- case $.ui.keyCode.SPACE:
- this._activate( event );
- break;
- case $.ui.keyCode.ESCAPE:
- this.collapse( event );
- break;
- default:
- preventDefault = false;
- prev = this.previousFilter || "";
- character = String.fromCharCode( event.keyCode );
- skip = false;
-
- clearTimeout( this.filterTimer );
-
- if ( character === prev ) {
- skip = true;
- } else {
- character = prev + character;
- }
-
- regex = new RegExp( "^" + escape( character ), "i" );
- match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
- return regex.test( $( this ).children( "a" ).text() );
- });
- match = skip && match.index( this.active.next() ) !== -1 ?
- this.active.nextAll( ".ui-menu-item" ) :
- match;
-
- // If no matches on the current filter, reset to the last character pressed
- // to move down the menu to the first item that starts with that character
- if ( !match.length ) {
- character = String.fromCharCode( event.keyCode );
- regex = new RegExp( "^" + escape( character ), "i" );
- match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
- return regex.test( $( this ).children( "a" ).text() );
- });
- }
-
- if ( match.length ) {
- this.focus( event, match );
- if ( match.length > 1 ) {
- this.previousFilter = character;
- this.filterTimer = this._delay(function() {
- delete this.previousFilter;
- }, 1000 );
- } else {
- delete this.previousFilter;
- }
- } else {
- delete this.previousFilter;
- }
- }
-
- if ( preventDefault ) {
- event.preventDefault();
- }
- },
-
- _activate: function( event ) {
- if ( !this.active.is( ".ui-state-disabled" ) ) {
- if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
- this.expand( event );
- } else {
- this.select( event );
- }
- }
- },
-
- refresh: function() {
- var menus,
- icon = this.options.icons.submenu,
- submenus = this.element.find( this.options.menus );
-
- // Initialize nested menus
- submenus.filter( ":not(.ui-menu)" )
- .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
- .hide()
- .attr({
- role: this.options.role,
- "aria-hidden": "true",
- "aria-expanded": "false"
- })
- .each(function() {
- var menu = $( this ),
- item = menu.prev( "a" ),
- submenuCarat = $( "<span>" )
- .addClass( "ui-menu-icon ui-icon " + icon )
- .data( "ui-menu-submenu-carat", true );
-
- item
- .attr( "aria-haspopup", "true" )
- .prepend( submenuCarat );
- menu.attr( "aria-labelledby", item.attr( "id" ) );
- });
-
- menus = submenus.add( this.element );
-
- // Don't refresh list items that are already adapted
- menus.children( ":not(.ui-menu-item):has(a)" )
- .addClass( "ui-menu-item" )
- .attr( "role", "presentation" )
- .children( "a" )
- .uniqueId()
- .addClass( "ui-corner-all" )
- .attr({
- tabIndex: -1,
- role: this._itemRole()
- });
-
- // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
- menus.children( ":not(.ui-menu-item)" ).each(function() {
- var item = $( this );
- // hyphen, em dash, en dash
- if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) {
- item.addClass( "ui-widget-content ui-menu-divider" );
- }
- });
-
- // Add aria-disabled attribute to any disabled menu item
- menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
-
- // If the active item has been removed, blur the menu
- if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
- this.blur();
- }
- },
-
- _itemRole: function() {
- return {
- menu: "menuitem",
- listbox: "option"
- }[ this.options.role ];
- },
-
- _setOption: function( key, value ) {
- if ( key === "icons" ) {
- this.element.find( ".ui-menu-icon" )
- .removeClass( this.options.icons.submenu )
- .addClass( value.submenu );
- }
- this._super( key, value );
- },
-
- focus: function( event, item ) {
- var nested, focused;
- this.blur( event, event && event.type === "focus" );
-
- this._scrollIntoView( item );
-
- this.active = item.first();
- focused = this.active.children( "a" ).addClass( "ui-state-focus" );
- // Only update aria-activedescendant if there's a role
- // otherwise we assume focus is managed elsewhere
- if ( this.options.role ) {
- this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
- }
-
- // Highlight active parent menu item, if any
- this.active
- .parent()
- .closest( ".ui-menu-item" )
- .children( "a:first" )
- .addClass( "ui-state-active" );
-
- if ( event && event.type === "keydown" ) {
- this._close();
- } else {
- this.timer = this._delay(function() {
- this._close();
- }, this.delay );
- }
-
- nested = item.children( ".ui-menu" );
- if ( nested.length && ( /^mouse/.test( event.type ) ) ) {
- this._startOpening(nested);
- }
- this.activeMenu = item.parent();
-
- this._trigger( "focus", event, { item: item } );
- },
-
- _scrollIntoView: function( item ) {
- var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
- if ( this._hasScroll() ) {
- borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
- paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
- offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
- scroll = this.activeMenu.scrollTop();
- elementHeight = this.activeMenu.height();
- itemHeight = item.height();
-
- if ( offset < 0 ) {
- this.activeMenu.scrollTop( scroll + offset );
- } else if ( offset + itemHeight > elementHeight ) {
- this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
- }
- }
- },
-
- blur: function( event, fromFocus ) {
- if ( !fromFocus ) {
- clearTimeout( this.timer );
- }
-
- if ( !this.active ) {
- return;
- }
-
- this.active.children( "a" ).removeClass( "ui-state-focus" );
- this.active = null;
-
- this._trigger( "blur", event, { item: this.active } );
- },
-
- _startOpening: function( submenu ) {
- clearTimeout( this.timer );
-
- // Don't open if already open fixes a Firefox bug that caused a .5 pixel
- // shift in the submenu position when mousing over the carat icon
- if ( submenu.attr( "aria-hidden" ) !== "true" ) {
- return;
- }
-
- this.timer = this._delay(function() {
- this._close();
- this._open( submenu );
- }, this.delay );
- },
-
- _open: function( submenu ) {
- var position = $.extend({
- of: this.active
- }, this.options.position );
-
- clearTimeout( this.timer );
- this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
- .hide()
- .attr( "aria-hidden", "true" );
-
- submenu
- .show()
- .removeAttr( "aria-hidden" )
- .attr( "aria-expanded", "true" )
- .position( position );
- },
-
- collapseAll: function( event, all ) {
- clearTimeout( this.timer );
- this.timer = this._delay(function() {
- // If we were passed an event, look for the submenu that contains the event
- var currentMenu = all ? this.element :
- $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
-
- // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
- if ( !currentMenu.length ) {
- currentMenu = this.element;
- }
-
- this._close( currentMenu );
-
- this.blur( event );
- this.activeMenu = currentMenu;
- }, this.delay );
- },
-
- // With no arguments, closes the currently active menu - if nothing is active
- // it closes all menus. If passed an argument, it will search for menus BELOW
- _close: function( startMenu ) {
- if ( !startMenu ) {
- startMenu = this.active ? this.active.parent() : this.element;
- }
-
- startMenu
- .find( ".ui-menu" )
- .hide()
- .attr( "aria-hidden", "true" )
- .attr( "aria-expanded", "false" )
- .end()
- .find( "a.ui-state-active" )
- .removeClass( "ui-state-active" );
- },
-
- collapse: function( event ) {
- var newItem = this.active &&
- this.active.parent().closest( ".ui-menu-item", this.element );
- if ( newItem && newItem.length ) {
- this._close();
- this.focus( event, newItem );
- }
- },
-
- expand: function( event ) {
- var newItem = this.active &&
- this.active
- .children( ".ui-menu " )
- .children( ".ui-menu-item" )
- .first();
-
- if ( newItem && newItem.length ) {
- this._open( newItem.parent() );
-
- // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
- this._delay(function() {
- this.focus( event, newItem );
- });
- }
- },
-
- next: function( event ) {
- this._move( "next", "first", event );
- },
-
- previous: function( event ) {
- this._move( "prev", "last", event );
- },
-
- isFirstItem: function() {
- return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
- },
-
- isLastItem: function() {
- return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
- },
-
- _move: function( direction, filter, event ) {
- var next;
- if ( this.active ) {
- if ( direction === "first" || direction === "last" ) {
- next = this.active
- [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
- .eq( -1 );
- } else {
- next = this.active
- [ direction + "All" ]( ".ui-menu-item" )
- .eq( 0 );
- }
- }
- if ( !next || !next.length || !this.active ) {
- next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
- }
-
- this.focus( event, next );
- },
-
- nextPage: function( event ) {
- var item, base, height;
-
- if ( !this.active ) {
- this.next( event );
- return;
- }
- if ( this.isLastItem() ) {
- return;
- }
- if ( this._hasScroll() ) {
- base = this.active.offset().top;
- height = this.element.height();
- this.active.nextAll( ".ui-menu-item" ).each(function() {
- item = $( this );
- return item.offset().top - base - height < 0;
- });
-
- this.focus( event, item );
- } else {
- this.focus( event, this.activeMenu.children( ".ui-menu-item" )
- [ !this.active ? "first" : "last" ]() );
- }
- },
-
- previousPage: function( event ) {
- var item, base, height;
- if ( !this.active ) {
- this.next( event );
- return;
- }
- if ( this.isFirstItem() ) {
- return;
- }
- if ( this._hasScroll() ) {
- base = this.active.offset().top;
- height = this.element.height();
- this.active.prevAll( ".ui-menu-item" ).each(function() {
- item = $( this );
- return item.offset().top - base + height > 0;
- });
-
- this.focus( event, item );
- } else {
- this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
- }
- },
-
- _hasScroll: function() {
- return this.element.outerHeight() < this.element.prop( "scrollHeight" );
- },
-
- select: function( event ) {
- // TODO: It should never be possible to not have an active item at this
- // point, but the tests don't trigger mouseenter before click.
- this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
- var ui = { item: this.active };
- if ( !this.active.has( ".ui-menu" ).length ) {
- this.collapseAll( event, true );
- }
- this._trigger( "select", event, ui );
- }
-});
-
-}( jQuery ));
-
-(function( $, undefined ) {
-
-$.ui = $.ui || {};
-
-var cachedScrollbarWidth,
- max = Math.max,
- abs = Math.abs,
- round = Math.round,
- rhorizontal = /left|center|right/,
- rvertical = /top|center|bottom/,
- roffset = /[\+\-]\d+(\.[\d]+)?%?/,
- rposition = /^\w+/,
- rpercent = /%$/,
- _position = $.fn.position;
-
-function getOffsets( offsets, width, height ) {
- return [
- parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
- parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
- ];
-}
-
-function parseCss( element, property ) {
- return parseInt( $.css( element, property ), 10 ) || 0;
-}
-
-function getDimensions( elem ) {
- var raw = elem[0];
- if ( raw.nodeType === 9 ) {
- return {
- width: elem.width(),
- height: elem.height(),
- offset: { top: 0, left: 0 }
- };
- }
- if ( $.isWindow( raw ) ) {
- return {
- width: elem.width(),
- height: elem.height(),
- offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
- };
- }
- if ( raw.preventDefault ) {
- return {
- width: 0,
- height: 0,
- offset: { top: raw.pageY, left: raw.pageX }
- };
- }
- return {
- width: elem.outerWidth(),
- height: elem.outerHeight(),
- offset: elem.offset()
- };
-}
-
-$.position = {
- scrollbarWidth: function() {
- if ( cachedScrollbarWidth !== undefined ) {
- return cachedScrollbarWidth;
- }
- var w1, w2,
- div = $( "<div style='display:block;width:50px;height:50px;overflow:hidden;'><div style='height:100px;width:auto;'></div></div>" ),
- innerDiv = div.children()[0];
-
- $( "body" ).append( div );
- w1 = innerDiv.offsetWidth;
- div.css( "overflow", "scroll" );
-
- w2 = innerDiv.offsetWidth;
-
- if ( w1 === w2 ) {
- w2 = div[0].clientWidth;
- }
-
- div.remove();
-
- return (cachedScrollbarWidth = w1 - w2);
- },
- getScrollInfo: function( within ) {
- var overflowX = within.isWindow ? "" : within.element.css( "overflow-x" ),
- overflowY = within.isWindow ? "" : within.element.css( "overflow-y" ),
- hasOverflowX = overflowX === "scroll" ||
- ( overflowX === "auto" && within.width < within.element[0].scrollWidth ),
- hasOverflowY = overflowY === "scroll" ||
- ( overflowY === "auto" && within.height < within.element[0].scrollHeight );
- return {
- width: hasOverflowX ? $.position.scrollbarWidth() : 0,
- height: hasOverflowY ? $.position.scrollbarWidth() : 0
- };
- },
- getWithinInfo: function( element ) {
- var withinElement = $( element || window ),
- isWindow = $.isWindow( withinElement[0] );
- return {
- element: withinElement,
- isWindow: isWindow,
- offset: withinElement.offset() || { left: 0, top: 0 },
- scrollLeft: withinElement.scrollLeft(),
- scrollTop: withinElement.scrollTop(),
- width: isWindow ? withinElement.width() : withinElement.outerWidth(),
- height: isWindow ? withinElement.height() : withinElement.outerHeight()
- };
- }
-};
-
-$.fn.position = function( options ) {
- if ( !options || !options.of ) {
- return _position.apply( this, arguments );
- }
-
- // make a copy, we don't want to modify arguments
- options = $.extend( {}, options );
-
- var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
- target = $( options.of ),
- within = $.position.getWithinInfo( options.within ),
- scrollInfo = $.position.getScrollInfo( within ),
- collision = ( options.collision || "flip" ).split( " " ),
- offsets = {};
-
- dimensions = getDimensions( target );
- if ( target[0].preventDefault ) {
- // force left top to allow flipping
- options.at = "left top";
- }
- targetWidth = dimensions.width;
- targetHeight = dimensions.height;
- targetOffset = dimensions.offset;
- // clone to reuse original targetOffset later
- basePosition = $.extend( {}, targetOffset );
-
- // force my and at to have valid horizontal and vertical positions
- // if a value is missing or invalid, it will be converted to center
- $.each( [ "my", "at" ], function() {
- var pos = ( options[ this ] || "" ).split( " " ),
- horizontalOffset,
- verticalOffset;
-
- if ( pos.length === 1) {
- pos = rhorizontal.test( pos[ 0 ] ) ?
- pos.concat( [ "center" ] ) :
- rvertical.test( pos[ 0 ] ) ?
- [ "center" ].concat( pos ) :
- [ "center", "center" ];
- }
- pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
- pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
-
- // calculate offsets
- horizontalOffset = roffset.exec( pos[ 0 ] );
- verticalOffset = roffset.exec( pos[ 1 ] );
- offsets[ this ] = [
- horizontalOffset ? horizontalOffset[ 0 ] : 0,
- verticalOffset ? verticalOffset[ 0 ] : 0
- ];
-
- // reduce to just the positions without the offsets
- options[ this ] = [
- rposition.exec( pos[ 0 ] )[ 0 ],
- rposition.exec( pos[ 1 ] )[ 0 ]
- ];
- });
-
- // normalize collision option
- if ( collision.length === 1 ) {
- collision[ 1 ] = collision[ 0 ];
- }
-
- if ( options.at[ 0 ] === "right" ) {
- basePosition.left += targetWidth;
- } else if ( options.at[ 0 ] === "center" ) {
- basePosition.left += targetWidth / 2;
- }
-
- if ( options.at[ 1 ] === "bottom" ) {
- basePosition.top += targetHeight;
- } else if ( options.at[ 1 ] === "center" ) {
- basePosition.top += targetHeight / 2;
- }
-
- atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
- basePosition.left += atOffset[ 0 ];
- basePosition.top += atOffset[ 1 ];
-
- return this.each(function() {
- var collisionPosition, using,
- elem = $( this ),
- elemWidth = elem.outerWidth(),
- elemHeight = elem.outerHeight(),
- marginLeft = parseCss( this, "marginLeft" ),
- marginTop = parseCss( this, "marginTop" ),
- collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width,
- collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height,
- position = $.extend( {}, basePosition ),
- myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
-
- if ( options.my[ 0 ] === "right" ) {
- position.left -= elemWidth;
- } else if ( options.my[ 0 ] === "center" ) {
- position.left -= elemWidth / 2;
- }
-
- if ( options.my[ 1 ] === "bottom" ) {
- position.top -= elemHeight;
- } else if ( options.my[ 1 ] === "center" ) {
- position.top -= elemHeight / 2;
- }
-
- position.left += myOffset[ 0 ];
- position.top += myOffset[ 1 ];
-
- // if the browser doesn't support fractions, then round for consistent results
- if ( !$.support.offsetFractions ) {
- position.left = round( position.left );
- position.top = round( position.top );
- }
-
- collisionPosition = {
- marginLeft: marginLeft,
- marginTop: marginTop
- };
-
- $.each( [ "left", "top" ], function( i, dir ) {
- if ( $.ui.position[ collision[ i ] ] ) {
- $.ui.position[ collision[ i ] ][ dir ]( position, {
- targetWidth: targetWidth,
- targetHeight: targetHeight,
- elemWidth: elemWidth,
- elemHeight: elemHeight,
- collisionPosition: collisionPosition,
- collisionWidth: collisionWidth,
- collisionHeight: collisionHeight,
- offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
- my: options.my,
- at: options.at,
- within: within,
- elem : elem
- });
- }
- });
-
- if ( options.using ) {
- // adds feedback as second argument to using callback, if present
- using = function( props ) {
- var left = targetOffset.left - position.left,
- right = left + targetWidth - elemWidth,
- top = targetOffset.top - position.top,
- bottom = top + targetHeight - elemHeight,
- feedback = {
- target: {
- element: target,
- left: targetOffset.left,
- top: targetOffset.top,
- width: targetWidth,
- height: targetHeight
- },
- element: {
- element: elem,
- left: position.left,
- top: position.top,
- width: elemWidth,
- height: elemHeight
- },
- horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
- vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
- };
- if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
- feedback.horizontal = "center";
- }
- if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
- feedback.vertical = "middle";
- }
- if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
- feedback.important = "horizontal";
- } else {
- feedback.important = "vertical";
- }
- options.using.call( this, props, feedback );
- };
- }
-
- elem.offset( $.extend( position, { using: using } ) );
- });
-};
-
-$.ui.position = {
- fit: {
- left: function( position, data ) {
- var within = data.within,
- withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
- outerWidth = within.width,
- collisionPosLeft = position.left - data.collisionPosition.marginLeft,
- overLeft = withinOffset - collisionPosLeft,
- overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
- newOverRight;
-
- // element is wider than within
- if ( data.collisionWidth > outerWidth ) {
- // element is initially over the left side of within
- if ( overLeft > 0 && overRight <= 0 ) {
- newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset;
- position.left += overLeft - newOverRight;
- // element is initially over right side of within
- } else if ( overRight > 0 && overLeft <= 0 ) {
- position.left = withinOffset;
- // element is initially over both left and right sides of within
- } else {
- if ( overLeft > overRight ) {
- position.left = withinOffset + outerWidth - data.collisionWidth;
- } else {
- position.left = withinOffset;
- }
- }
- // too far left -> align with left edge
- } else if ( overLeft > 0 ) {
- position.left += overLeft;
- // too far right -> align with right edge
- } else if ( overRight > 0 ) {
- position.left -= overRight;
- // adjust based on position and margin
- } else {
- position.left = max( position.left - collisionPosLeft, position.left );
- }
- },
- top: function( position, data ) {
- var within = data.within,
- withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
- outerHeight = data.within.height,
- collisionPosTop = position.top - data.collisionPosition.marginTop,
- overTop = withinOffset - collisionPosTop,
- overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
- newOverBottom;
-
- // element is taller than within
- if ( data.collisionHeight > outerHeight ) {
- // element is initially over the top of within
- if ( overTop > 0 && overBottom <= 0 ) {
- newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset;
- position.top += overTop - newOverBottom;
- // element is initially over bottom of within
- } else if ( overBottom > 0 && overTop <= 0 ) {
- position.top = withinOffset;
- // element is initially over both top and bottom of within
- } else {
- if ( overTop > overBottom ) {
- position.top = withinOffset + outerHeight - data.collisionHeight;
- } else {
- position.top = withinOffset;
- }
- }
- // too far up -> align with top
- } else if ( overTop > 0 ) {
- position.top += overTop;
- // too far down -> align with bottom edge
- } else if ( overBottom > 0 ) {
- position.top -= overBottom;
- // adjust based on position and margin
- } else {
- position.top = max( position.top - collisionPosTop, position.top );
- }
- }
- },
- flip: {
- left: function( position, data ) {
- var within = data.within,
- withinOffset = within.offset.left + within.scrollLeft,
- outerWidth = within.width,
- offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
- collisionPosLeft = position.left - data.collisionPosition.marginLeft,
- overLeft = collisionPosLeft - offsetLeft,
- overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
- myOffset = data.my[ 0 ] === "left" ?
- -data.elemWidth :
- data.my[ 0 ] === "right" ?
- data.elemWidth :
- 0,
- atOffset = data.at[ 0 ] === "left" ?
- data.targetWidth :
- data.at[ 0 ] === "right" ?
- -data.targetWidth :
- 0,
- offset = -2 * data.offset[ 0 ],
- newOverRight,
- newOverLeft;
-
- if ( overLeft < 0 ) {
- newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset;
- if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
- position.left += myOffset + atOffset + offset;
- }
- }
- else if ( overRight > 0 ) {
- newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft;
- if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
- position.left += myOffset + atOffset + offset;
- }
- }
- },
- top: function( position, data ) {
- var within = data.within,
- withinOffset = within.offset.top + within.scrollTop,
- outerHeight = within.height,
- offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
- collisionPosTop = position.top - data.collisionPosition.marginTop,
- overTop = collisionPosTop - offsetTop,
- overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
- top = data.my[ 1 ] === "top",
- myOffset = top ?
- -data.elemHeight :
- data.my[ 1 ] === "bottom" ?
- data.elemHeight :
- 0,
- atOffset = data.at[ 1 ] === "top" ?
- data.targetHeight :
- data.at[ 1 ] === "bottom" ?
- -data.targetHeight :
- 0,
- offset = -2 * data.offset[ 1 ],
- newOverTop,
- newOverBottom;
- if ( overTop < 0 ) {
- newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset;
- if ( ( position.top + myOffset + atOffset + offset) > overTop && ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) ) {
- position.top += myOffset + atOffset + offset;
- }
- }
- else if ( overBottom > 0 ) {
- newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop;
- if ( ( position.top + myOffset + atOffset + offset) > overBottom && ( newOverTop > 0 || abs( newOverTop ) < overBottom ) ) {
- position.top += myOffset + atOffset + offset;
- }
- }
- }
- },
- flipfit: {
- left: function() {
- $.ui.position.flip.left.apply( this, arguments );
- $.ui.position.fit.left.apply( this, arguments );
- },
- top: function() {
- $.ui.position.flip.top.apply( this, arguments );
- $.ui.position.fit.top.apply( this, arguments );
- }
- }
-};
-
-// fraction support test
-(function () {
- var testElement, testElementParent, testElementStyle, offsetLeft, i,
- body = document.getElementsByTagName( "body" )[ 0 ],
- div = document.createElement( "div" );
-
- //Create a "fake body" for testing based on method used in jQuery.support
- testElement = document.createElement( body ? "div" : "body" );
- testElementStyle = {
- visibility: "hidden",
- width: 0,
- height: 0,
- border: 0,
- margin: 0,
- background: "none"
- };
- if ( body ) {
- $.extend( testElementStyle, {
- position: "absolute",
- left: "-1000px",
- top: "-1000px"
- });
- }
- for ( i in testElementStyle ) {
- testElement.style[ i ] = testElementStyle[ i ];
- }
- testElement.appendChild( div );
- testElementParent = body || document.documentElement;
- testElementParent.insertBefore( testElement, testElementParent.firstChild );
-
- div.style.cssText = "position: absolute; left: 10.7432222px;";
-
- offsetLeft = $( div ).offset().left;
- $.support.offsetFractions = offsetLeft > 10 && offsetLeft < 11;
-
- testElement.innerHTML = "";
- testElementParent.removeChild( testElement );
-})();
-
-}( jQuery ) );
-
-(function( $, undefined ) {
-
-$.widget( "ui.progressbar", {
- version: "1.10.1",
- options: {
- max: 100,
- value: 0,
-
- change: null,
- complete: null
- },
-
- min: 0,
-
- _create: function() {
- // Constrain initial value
- this.oldValue = this.options.value = this._constrainedValue();
-
- this.element
- .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
- .attr({
- // Only set static values, aria-valuenow and aria-valuemax are
- // set inside _refreshValue()
- role: "progressbar",
- "aria-valuemin": this.min
- });
-
- this.valueDiv = $( "<div class='ui-progressbar-value ui-widget-header ui-corner-left'></div>" )
- .appendTo( this.element );
-
- this._refreshValue();
- },
-
- _destroy: function() {
- this.element
- .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
- .removeAttr( "role" )
- .removeAttr( "aria-valuemin" )
- .removeAttr( "aria-valuemax" )
- .removeAttr( "aria-valuenow" );
-
- this.valueDiv.remove();
- },
-
- value: function( newValue ) {
- if ( newValue === undefined ) {
- return this.options.value;
- }
-
- this.options.value = this._constrainedValue( newValue );
- this._refreshValue();
- },
-
- _constrainedValue: function( newValue ) {
- if ( newValue === undefined ) {
- newValue = this.options.value;
- }
-
- this.indeterminate = newValue === false;
-
- // sanitize value
- if ( typeof newValue !== "number" ) {
- newValue = 0;
- }
-
- return this.indeterminate ? false :
- Math.min( this.options.max, Math.max( this.min, newValue ) );
- },
-
- _setOptions: function( options ) {
- // Ensure "value" option is set after other values (like max)
- var value = options.value;
- delete options.value;
-
- this._super( options );
-
- this.options.value = this._constrainedValue( value );
- this._refreshValue();
- },
-
- _setOption: function( key, value ) {
- if ( key === "max" ) {
- // Don't allow a max less than min
- value = Math.max( this.min, value );
- }
-
- this._super( key, value );
- },
-
- _percentage: function() {
- return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
- },
-
- _refreshValue: function() {
- var value = this.options.value,
- percentage = this._percentage();
-
- this.valueDiv
- .toggle( this.indeterminate || value > this.min )
- .toggleClass( "ui-corner-right", value === this.options.max )
- .width( percentage.toFixed(0) + "%" );
-
- this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate );
-
- if ( this.indeterminate ) {
- this.element.removeAttr( "aria-valuenow" );
- if ( !this.overlayDiv ) {
- this.overlayDiv = $( "<div class='ui-progressbar-overlay'></div>" ).appendTo( this.valueDiv );
- }
- } else {
- this.element.attr({
- "aria-valuemax": this.options.max,
- "aria-valuenow": value
- });
- if ( this.overlayDiv ) {
- this.overlayDiv.remove();
- this.overlayDiv = null;
- }
- }
-
- if ( this.oldValue !== value ) {
- this.oldValue = value;
- this._trigger( "change" );
- }
- if ( value === this.options.max ) {
- this._trigger( "complete" );
- }
- }
-});
-
-})( jQuery );
-
-(function( $, undefined ) {
-
-// number of pages in a slider
-// (how many times can you page up/down to go through the whole range)
-var numPages = 5;
-
-$.widget( "ui.slider", $.ui.mouse, {
- version: "1.10.1",
- widgetEventPrefix: "slide",
-
- options: {
- animate: false,
- distance: 0,
- max: 100,
- min: 0,
- orientation: "horizontal",
- range: false,
- step: 1,
- value: 0,
- values: null,
-
- // callbacks
- change: null,
- slide: null,
- start: null,
- stop: null
- },
-
- _create: function() {
- this._keySliding = false;
- this._mouseSliding = false;
- this._animateOff = true;
- this._handleIndex = null;
- this._detectOrientation();
- this._mouseInit();
-
- this.element
- .addClass( "ui-slider" +
- " ui-slider-" + this.orientation +
- " ui-widget" +
- " ui-widget-content" +
- " ui-corner-all");
-
- this._refresh();
- this._setOption( "disabled", this.options.disabled );
-
- this._animateOff = false;
- },
-
- _refresh: function() {
- this._createRange();
- this._createHandles();
- this._setupEvents();
- this._refreshValue();
- },
-
- _createHandles: function() {
- var i, handleCount,
- options = this.options,
- existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
- handle = "<a class='ui-slider-handle ui-state-default ui-corner-all' href='#'></a>",
- handles = [];
-
- handleCount = ( options.values && options.values.length ) || 1;
-
- if ( existingHandles.length > handleCount ) {
- existingHandles.slice( handleCount ).remove();
- existingHandles = existingHandles.slice( 0, handleCount );
- }
-
- for ( i = existingHandles.length; i < handleCount; i++ ) {
- handles.push( handle );
- }
-
- this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
-
- this.handle = this.handles.eq( 0 );
-
- this.handles.each(function( i ) {
- $( this ).data( "ui-slider-handle-index", i );
- });
- },
-
- _createRange: function() {
- var options = this.options,
- classes = "";
-
- if ( options.range ) {
- if ( options.range === true ) {
- if ( !options.values ) {
- options.values = [ this._valueMin(), this._valueMin() ];
- } else if ( options.values.length && options.values.length !== 2 ) {
- options.values = [ options.values[0], options.values[0] ];
- } else if ( $.isArray( options.values ) ) {
- options.values = options.values.slice(0);
- }
- }
-
- if ( !this.range || !this.range.length ) {
- this.range = $( "<div></div>" )
- .appendTo( this.element );
-
- classes = "ui-slider-range" +
- // note: this isn't the most fittingly semantic framework class for this element,
- // but worked best visually with a variety of themes
- " ui-widget-header ui-corner-all";
- } else {
- this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
- // Handle range switching from true to min/max
- .css({
- "left": "",
- "bottom": ""
- });
- }
-
- this.range.addClass( classes +
- ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
- } else {
- this.range = $([]);
- }
- },
-
- _setupEvents: function() {
- var elements = this.handles.add( this.range ).filter( "a" );
- this._off( elements );
- this._on( elements, this._handleEvents );
- this._hoverable( elements );
- this._focusable( elements );
- },
-
- _destroy: function() {
- this.handles.remove();
- this.range.remove();
-
- this.element
- .removeClass( "ui-slider" +
- " ui-slider-horizontal" +
- " ui-slider-vertical" +
- " ui-widget" +
- " ui-widget-content" +
- " ui-corner-all" );
-
- this._mouseDestroy();
- },
-
- _mouseCapture: function( event ) {
- var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
- that = this,
- o = this.options;
-
- if ( o.disabled ) {
- return false;
- }
-
- this.elementSize = {
- width: this.element.outerWidth(),
- height: this.element.outerHeight()
- };
- this.elementOffset = this.element.offset();
-
- position = { x: event.pageX, y: event.pageY };
- normValue = this._normValueFromMouse( position );
- distance = this._valueMax() - this._valueMin() + 1;
- this.handles.each(function( i ) {
- var thisDistance = Math.abs( normValue - that.values(i) );
- if (( distance > thisDistance ) ||
- ( distance === thisDistance &&
- (i === that._lastChangedValue || that.values(i) === o.min ))) {
- distance = thisDistance;
- closestHandle = $( this );
- index = i;
- }
- });
-
- allowed = this._start( event, index );
- if ( allowed === false ) {
- return false;
- }
- this._mouseSliding = true;
-
- this._handleIndex = index;
-
- closestHandle
- .addClass( "ui-state-active" )
- .focus();
-
- offset = closestHandle.offset();
- mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
- this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
- left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
- top: event.pageY - offset.top -
- ( closestHandle.height() / 2 ) -
- ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
- ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
- ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
- };
-
- if ( !this.handles.hasClass( "ui-state-hover" ) ) {
- this._slide( event, index, normValue );
- }
- this._animateOff = true;
- return true;
- },
-
- _mouseStart: function() {
- return true;
- },
-
- _mouseDrag: function( event ) {
- var position = { x: event.pageX, y: event.pageY },
- normValue = this._normValueFromMouse( position );
-
- this._slide( event, this._handleIndex, normValue );
-
- return false;
- },
-
- _mouseStop: function( event ) {
- this.handles.removeClass( "ui-state-active" );
- this._mouseSliding = false;
-
- this._stop( event, this._handleIndex );
- this._change( event, this._handleIndex );
-
- this._handleIndex = null;
- this._clickOffset = null;
- this._animateOff = false;
-
- return false;
- },
-
- _detectOrientation: function() {
- this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
- },
-
- _normValueFromMouse: function( position ) {
- var pixelTotal,
- pixelMouse,
- percentMouse,
- valueTotal,
- valueMouse;
-
- if ( this.orientation === "horizontal" ) {
- pixelTotal = this.elementSize.width;
- pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
- } else {
- pixelTotal = this.elementSize.height;
- pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
- }
-
- percentMouse = ( pixelMouse / pixelTotal );
- if ( percentMouse > 1 ) {
- percentMouse = 1;
- }
- if ( percentMouse < 0 ) {
- percentMouse = 0;
- }
- if ( this.orientation === "vertical" ) {
- percentMouse = 1 - percentMouse;
- }
-
- valueTotal = this._valueMax() - this._valueMin();
- valueMouse = this._valueMin() + percentMouse * valueTotal;
-
- return this._trimAlignValue( valueMouse );
- },
-
- _start: function( event, index ) {
- var uiHash = {
- handle: this.handles[ index ],
- value: this.value()
- };
- if ( this.options.values && this.options.values.length ) {
- uiHash.value = this.values( index );
- uiHash.values = this.values();
- }
- return this._trigger( "start", event, uiHash );
- },
-
- _slide: function( event, index, newVal ) {
- var otherVal,
- newValues,
- allowed;
-
- if ( this.options.values && this.options.values.length ) {
- otherVal = this.values( index ? 0 : 1 );
-
- if ( ( this.options.values.length === 2 && this.options.range === true ) &&
- ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
- ) {
- newVal = otherVal;
- }
-
- if ( newVal !== this.values( index ) ) {
- newValues = this.values();
- newValues[ index ] = newVal;
- // A slide can be canceled by returning false from the slide callback
- allowed = this._trigger( "slide", event, {
- handle: this.handles[ index ],
- value: newVal,
- values: newValues
- } );
- otherVal = this.values( index ? 0 : 1 );
- if ( allowed !== false ) {
- this.values( index, newVal, true );
- }
- }
- } else {
- if ( newVal !== this.value() ) {
- // A slide can be canceled by returning false from the slide callback
- allowed = this._trigger( "slide", event, {
- handle: this.handles[ index ],
- value: newVal
- } );
- if ( allowed !== false ) {
- this.value( newVal );
- }
- }
- }
- },
-
- _stop: function( event, index ) {
- var uiHash = {
- handle: this.handles[ index ],
- value: this.value()
- };
- if ( this.options.values && this.options.values.length ) {
- uiHash.value = this.values( index );
- uiHash.values = this.values();
- }
-
- this._trigger( "stop", event, uiHash );
- },
-
- _change: function( event, index ) {
- if ( !this._keySliding && !this._mouseSliding ) {
- var uiHash = {
- handle: this.handles[ index ],
- value: this.value()
- };
- if ( this.options.values && this.options.values.length ) {
- uiHash.value = this.values( index );
- uiHash.values = this.values();
- }
-
- //store the last changed value index for reference when handles overlap
- this._lastChangedValue = index;
-
- this._trigger( "change", event, uiHash );
- }
- },
-
- value: function( newValue ) {
- if ( arguments.length ) {
- this.options.value = this._trimAlignValue( newValue );
- this._refreshValue();
- this._change( null, 0 );
- return;
- }
-
- return this._value();
- },
-
- values: function( index, newValue ) {
- var vals,
- newValues,
- i;
-
- if ( arguments.length > 1 ) {
- this.options.values[ index ] = this._trimAlignValue( newValue );
- this._refreshValue();
- this._change( null, index );
- return;
- }
-
- if ( arguments.length ) {
- if ( $.isArray( arguments[ 0 ] ) ) {
- vals = this.options.values;
- newValues = arguments[ 0 ];
- for ( i = 0; i < vals.length; i += 1 ) {
- vals[ i ] = this._trimAlignValue( newValues[ i ] );
- this._change( null, i );
- }
- this._refreshValue();
- } else {
- if ( this.options.values && this.options.values.length ) {
- return this._values( index );
- } else {
- return this.value();
- }
- }
- } else {
- return this._values();
- }
- },
-
- _setOption: function( key, value ) {
- var i,
- valsLength = 0;
-
- if ( key === "range" && this.options.range === true ) {
- if ( value === "min" ) {
- this.options.value = this._values( 0 );
- this.options.values = null;
- } else if ( value === "max" ) {
- this.options.value = this._values( this.options.values.length-1 );
- this.options.values = null;
- }
- }
-
- if ( $.isArray( this.options.values ) ) {
- valsLength = this.options.values.length;
- }
-
- $.Widget.prototype._setOption.apply( this, arguments );
-
- switch ( key ) {
- case "orientation":
- this._detectOrientation();
- this.element
- .removeClass( "ui-slider-horizontal ui-slider-vertical" )
- .addClass( "ui-slider-" + this.orientation );
- this._refreshValue();
- break;
- case "value":
- this._animateOff = true;
- this._refreshValue();
- this._change( null, 0 );
- this._animateOff = false;
- break;
- case "values":
- this._animateOff = true;
- this._refreshValue();
- for ( i = 0; i < valsLength; i += 1 ) {
- this._change( null, i );
- }
- this._animateOff = false;
- break;
- case "min":
- case "max":
- this._animateOff = true;
- this._refreshValue();
- this._animateOff = false;
- break;
- case "range":
- this._animateOff = true;
- this._refresh();
- this._animateOff = false;
- break;
- }
- },
-
- //internal value getter
- // _value() returns value trimmed by min and max, aligned by step
- _value: function() {
- var val = this.options.value;
- val = this._trimAlignValue( val );
-
- return val;
- },
-
- //internal values getter
- // _values() returns array of values trimmed by min and max, aligned by step
- // _values( index ) returns single value trimmed by min and max, aligned by step
- _values: function( index ) {
- var val,
- vals,
- i;
-
- if ( arguments.length ) {
- val = this.options.values[ index ];
- val = this._trimAlignValue( val );
-
- return val;
- } else if ( this.options.values && this.options.values.length ) {
- // .slice() creates a copy of the array
- // this copy gets trimmed by min and max and then returned
- vals = this.options.values.slice();
- for ( i = 0; i < vals.length; i+= 1) {
- vals[ i ] = this._trimAlignValue( vals[ i ] );
- }
-
- return vals;
- } else {
- return [];
- }
- },
-
- // returns the step-aligned value that val is closest to, between (inclusive) min and max
- _trimAlignValue: function( val ) {
- if ( val <= this._valueMin() ) {
- return this._valueMin();
- }
- if ( val >= this._valueMax() ) {
- return this._valueMax();
- }
- var step = ( this.options.step > 0 ) ? this.options.step : 1,
- valModStep = (val - this._valueMin()) % step,
- alignValue = val - valModStep;
-
- if ( Math.abs(valModStep) * 2 >= step ) {
- alignValue += ( valModStep > 0 ) ? step : ( -step );
- }
-
- // Since JavaScript has problems with large floats, round
- // the final value to 5 digits after the decimal point (see #4124)
- return parseFloat( alignValue.toFixed(5) );
- },
-
- _valueMin: function() {
- return this.options.min;
- },
-
- _valueMax: function() {
- return this.options.max;
- },
-
- _refreshValue: function() {
- var lastValPercent, valPercent, value, valueMin, valueMax,
- oRange = this.options.range,
- o = this.options,
- that = this,
- animate = ( !this._animateOff ) ? o.animate : false,
- _set = {};
-
- if ( this.options.values && this.options.values.length ) {
- this.handles.each(function( i ) {
- valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
- _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
- $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
- if ( that.options.range === true ) {
- if ( that.orientation === "horizontal" ) {
- if ( i === 0 ) {
- that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
- }
- if ( i === 1 ) {
- that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
- }
- } else {
- if ( i === 0 ) {
- that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
- }
- if ( i === 1 ) {
- that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
- }
- }
- }
- lastValPercent = valPercent;
- });
- } else {
- value = this.value();
- valueMin = this._valueMin();
- valueMax = this._valueMax();
- valPercent = ( valueMax !== valueMin ) ?
- ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
- 0;
- _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
- this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
-
- if ( oRange === "min" && this.orientation === "horizontal" ) {
- this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
- }
- if ( oRange === "max" && this.orientation === "horizontal" ) {
- this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
- }
- if ( oRange === "min" && this.orientation === "vertical" ) {
- this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
- }
- if ( oRange === "max" && this.orientation === "vertical" ) {
- this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
- }
- }
- },
-
- _handleEvents: {
- keydown: function( event ) {
- /*jshint maxcomplexity:25*/
- var allowed, curVal, newVal, step,
- index = $( event.target ).data( "ui-slider-handle-index" );
-
- switch ( event.keyCode ) {
- case $.ui.keyCode.HOME:
- case $.ui.keyCode.END:
- case $.ui.keyCode.PAGE_UP:
- case $.ui.keyCode.PAGE_DOWN:
- case $.ui.keyCode.UP:
- case $.ui.keyCode.RIGHT:
- case $.ui.keyCode.DOWN:
- case $.ui.keyCode.LEFT:
- event.preventDefault();
- if ( !this._keySliding ) {
- this._keySliding = true;
- $( event.target ).addClass( "ui-state-active" );
- allowed = this._start( event, index );
- if ( allowed === false ) {
- return;
- }
- }
- break;
- }
-
- step = this.options.step;
- if ( this.options.values && this.options.values.length ) {
- curVal = newVal = this.values( index );
- } else {
- curVal = newVal = this.value();
- }
-
- switch ( event.keyCode ) {
- case $.ui.keyCode.HOME:
- newVal = this._valueMin();
- break;
- case $.ui.keyCode.END:
- newVal = this._valueMax();
- break;
- case $.ui.keyCode.PAGE_UP:
- newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
- break;
- case $.ui.keyCode.PAGE_DOWN:
- newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
- break;
- case $.ui.keyCode.UP:
- case $.ui.keyCode.RIGHT:
- if ( curVal === this._valueMax() ) {
- return;
- }
- newVal = this._trimAlignValue( curVal + step );
- break;
- case $.ui.keyCode.DOWN:
- case $.ui.keyCode.LEFT:
- if ( curVal === this._valueMin() ) {
- return;
- }
- newVal = this._trimAlignValue( curVal - step );
- break;
- }
-
- this._slide( event, index, newVal );
- },
- click: function( event ) {
- event.preventDefault();
- },
- keyup: function( event ) {
- var index = $( event.target ).data( "ui-slider-handle-index" );
-
- if ( this._keySliding ) {
- this._keySliding = false;
- this._stop( event, index );
- this._change( event, index );
- $( event.target ).removeClass( "ui-state-active" );
- }
- }
- }
-
-});
-
-}(jQuery));
-
-(function( $ ) {
-
-function modifier( fn ) {
- return function() {
- var previous = this.element.val();
- fn.apply( this, arguments );
- this._refresh();
- if ( previous !== this.element.val() ) {
- this._trigger( "change" );
- }
- };
-}
-
-$.widget( "ui.spinner", {
- version: "1.10.1",
- defaultElement: "<input>",
- widgetEventPrefix: "spin",
- options: {
- culture: null,
- icons: {
- down: "ui-icon-triangle-1-s",
- up: "ui-icon-triangle-1-n"
- },
- incremental: true,
- max: null,
- min: null,
- numberFormat: null,
- page: 10,
- step: 1,
-
- change: null,
- spin: null,
- start: null,
- stop: null
- },
-
- _create: function() {
- // handle string values that need to be parsed
- this._setOption( "max", this.options.max );
- this._setOption( "min", this.options.min );
- this._setOption( "step", this.options.step );
-
- // format the value, but don't constrain
- this._value( this.element.val(), true );
-
- this._draw();
- this._on( this._events );
- this._refresh();
-
- // turning off autocomplete prevents the browser from remembering the
- // value when navigating through history, so we re-enable autocomplete
- // if the page is unloaded before the widget is destroyed. #7790
- this._on( this.window, {
- beforeunload: function() {
- this.element.removeAttr( "autocomplete" );
- }
- });
- },
-
- _getCreateOptions: function() {
- var options = {},
- element = this.element;
-
- $.each( [ "min", "max", "step" ], function( i, option ) {
- var value = element.attr( option );
- if ( value !== undefined && value.length ) {
- options[ option ] = value;
- }
- });
-
- return options;
- },
-
- _events: {
- keydown: function( event ) {
- if ( this._start( event ) && this._keydown( event ) ) {
- event.preventDefault();
- }
- },
- keyup: "_stop",
- focus: function() {
- this.previous = this.element.val();
- },
- blur: function( event ) {
- if ( this.cancelBlur ) {
- delete this.cancelBlur;
- return;
- }
-
- this._refresh();
- if ( this.previous !== this.element.val() ) {
- this._trigger( "change", event );
- }
- },
- mousewheel: function( event, delta ) {
- if ( !delta ) {
- return;
- }
- if ( !this.spinning && !this._start( event ) ) {
- return false;
- }
-
- this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
- clearTimeout( this.mousewheelTimer );
- this.mousewheelTimer = this._delay(function() {
- if ( this.spinning ) {
- this._stop( event );
- }
- }, 100 );
- event.preventDefault();
- },
- "mousedown .ui-spinner-button": function( event ) {
- var previous;
-
- // We never want the buttons to have focus; whenever the user is
- // interacting with the spinner, the focus should be on the input.
- // If the input is focused then this.previous is properly set from
- // when the input first received focus. If the input is not focused
- // then we need to set this.previous based on the value before spinning.
- previous = this.element[0] === this.document[0].activeElement ?
- this.previous : this.element.val();
- function checkFocus() {
- var isActive = this.element[0] === this.document[0].activeElement;
- if ( !isActive ) {
- this.element.focus();
- this.previous = previous;
- // support: IE
- // IE sets focus asynchronously, so we need to check if focus
- // moved off of the input because the user clicked on the button.
- this._delay(function() {
- this.previous = previous;
- });
- }
- }
-
- // ensure focus is on (or stays on) the text field
- event.preventDefault();
- checkFocus.call( this );
-
- // support: IE
- // IE doesn't prevent moving focus even with event.preventDefault()
- // so we set a flag to know when we should ignore the blur event
- // and check (again) if focus moved off of the input.
- this.cancelBlur = true;
- this._delay(function() {
- delete this.cancelBlur;
- checkFocus.call( this );
- });
-
- if ( this._start( event ) === false ) {
- return;
- }
-
- this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
- },
- "mouseup .ui-spinner-button": "_stop",
- "mouseenter .ui-spinner-button": function( event ) {
- // button will add ui-state-active if mouse was down while mouseleave and kept down
- if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
- return;
- }
-
- if ( this._start( event ) === false ) {
- return false;
- }
- this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
- },
- // TODO: do we really want to consider this a stop?
- // shouldn't we just stop the repeater and wait until mouseup before
- // we trigger the stop event?
- "mouseleave .ui-spinner-button": "_stop"
- },
-
- _draw: function() {
- var uiSpinner = this.uiSpinner = this.element
- .addClass( "ui-spinner-input" )
- .attr( "autocomplete", "off" )
- .wrap( this._uiSpinnerHtml() )
- .parent()
- // add buttons
- .append( this._buttonHtml() );
-
- this.element.attr( "role", "spinbutton" );
-
- // button bindings
- this.buttons = uiSpinner.find( ".ui-spinner-button" )
- .attr( "tabIndex", -1 )
- .button()
- .removeClass( "ui-corner-all" );
-
- // IE 6 doesn't understand height: 50% for the buttons
- // unless the wrapper has an explicit height
- if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
- uiSpinner.height() > 0 ) {
- uiSpinner.height( uiSpinner.height() );
- }
-
- // disable spinner if element was already disabled
- if ( this.options.disabled ) {
- this.disable();
- }
- },
-
- _keydown: function( event ) {
- var options = this.options,
- keyCode = $.ui.keyCode;
-
- switch ( event.keyCode ) {
- case keyCode.UP:
- this._repeat( null, 1, event );
- return true;
- case keyCode.DOWN:
- this._repeat( null, -1, event );
- return true;
- case keyCode.PAGE_UP:
- this._repeat( null, options.page, event );
- return true;
- case keyCode.PAGE_DOWN:
- this._repeat( null, -options.page, event );
- return true;
- }
-
- return false;
- },
-
- _uiSpinnerHtml: function() {
- return "<span class='ui-spinner ui-widget ui-widget-content ui-corner-all'></span>";
- },
-
- _buttonHtml: function() {
- return "" +
- "<a class='ui-spinner-button ui-spinner-up ui-corner-tr'>" +
- "<span class='ui-icon " + this.options.icons.up + "'>▲</span>" +
- "</a>" +
- "<a class='ui-spinner-button ui-spinner-down ui-corner-br'>" +
- "<span class='ui-icon " + this.options.icons.down + "'>▼</span>" +
- "</a>";
- },
-
- _start: function( event ) {
- if ( !this.spinning && this._trigger( "start", event ) === false ) {
- return false;
- }
-
- if ( !this.counter ) {
- this.counter = 1;
- }
- this.spinning = true;
- return true;
- },
-
- _repeat: function( i, steps, event ) {
- i = i || 500;
-
- clearTimeout( this.timer );
- this.timer = this._delay(function() {
- this._repeat( 40, steps, event );
- }, i );
-
- this._spin( steps * this.options.step, event );
- },
-
- _spin: function( step, event ) {
- var value = this.value() || 0;
-
- if ( !this.counter ) {
- this.counter = 1;
- }
-
- value = this._adjustValue( value + step * this._increment( this.counter ) );
-
- if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
- this._value( value );
- this.counter++;
- }
- },
-
- _increment: function( i ) {
- var incremental = this.options.incremental;
-
- if ( incremental ) {
- return $.isFunction( incremental ) ?
- incremental( i ) :
- Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
- }
-
- return 1;
- },
-
- _precision: function() {
- var precision = this._precisionOf( this.options.step );
- if ( this.options.min !== null ) {
- precision = Math.max( precision, this._precisionOf( this.options.min ) );
- }
- return precision;
- },
-
- _precisionOf: function( num ) {
- var str = num.toString(),
- decimal = str.indexOf( "." );
- return decimal === -1 ? 0 : str.length - decimal - 1;
- },
-
- _adjustValue: function( value ) {
- var base, aboveMin,
- options = this.options;
-
- // make sure we're at a valid step
- // - find out where we are relative to the base (min or 0)
- base = options.min !== null ? options.min : 0;
- aboveMin = value - base;
- // - round to the nearest step
- aboveMin = Math.round(aboveMin / options.step) * options.step;
- // - rounding is based on 0, so adjust back to our base
- value = base + aboveMin;
-
- // fix precision from bad JS floating point math
- value = parseFloat( value.toFixed( this._precision() ) );
-
- // clamp the value
- if ( options.max !== null && value > options.max) {
- return options.max;
- }
- if ( options.min !== null && value < options.min ) {
- return options.min;
- }
-
- return value;
- },
-
- _stop: function( event ) {
- if ( !this.spinning ) {
- return;
- }
-
- clearTimeout( this.timer );
- clearTimeout( this.mousewheelTimer );
- this.counter = 0;
- this.spinning = false;
- this._trigger( "stop", event );
- },
-
- _setOption: function( key, value ) {
- if ( key === "culture" || key === "numberFormat" ) {
- var prevValue = this._parse( this.element.val() );
- this.options[ key ] = value;
- this.element.val( this._format( prevValue ) );
- return;
- }
-
- if ( key === "max" || key === "min" || key === "step" ) {
- if ( typeof value === "string" ) {
- value = this._parse( value );
- }
- }
- if ( key === "icons" ) {
- this.buttons.first().find( ".ui-icon" )
- .removeClass( this.options.icons.up )
- .addClass( value.up );
- this.buttons.last().find( ".ui-icon" )
- .removeClass( this.options.icons.down )
- .addClass( value.down );
- }
-
- this._super( key, value );
-
- if ( key === "disabled" ) {
- if ( value ) {
- this.element.prop( "disabled", true );
- this.buttons.button( "disable" );
- } else {
- this.element.prop( "disabled", false );
- this.buttons.button( "enable" );
- }
- }
- },
-
- _setOptions: modifier(function( options ) {
- this._super( options );
- this._value( this.element.val() );
- }),
-
- _parse: function( val ) {
- if ( typeof val === "string" && val !== "" ) {
- val = window.Globalize && this.options.numberFormat ?
- Globalize.parseFloat( val, 10, this.options.culture ) : +val;
- }
- return val === "" || isNaN( val ) ? null : val;
- },
-
- _format: function( value ) {
- if ( value === "" ) {
- return "";
- }
- return window.Globalize && this.options.numberFormat ?
- Globalize.format( value, this.options.numberFormat, this.options.culture ) :
- value;
- },
-
- _refresh: function() {
- this.element.attr({
- "aria-valuemin": this.options.min,
- "aria-valuemax": this.options.max,
- // TODO: what should we do with values that can't be parsed?
- "aria-valuenow": this._parse( this.element.val() )
- });
- },
-
- // update the value without triggering change
- _value: function( value, allowAny ) {
- var parsed;
- if ( value !== "" ) {
- parsed = this._parse( value );
- if ( parsed !== null ) {
- if ( !allowAny ) {
- parsed = this._adjustValue( parsed );
- }
- value = this._format( parsed );
- }
- }
- this.element.val( value );
- this._refresh();
- },
-
- _destroy: function() {
- this.element
- .removeClass( "ui-spinner-input" )
- .prop( "disabled", false )
- .removeAttr( "autocomplete" )
- .removeAttr( "role" )
- .removeAttr( "aria-valuemin" )
- .removeAttr( "aria-valuemax" )
- .removeAttr( "aria-valuenow" );
- this.uiSpinner.replaceWith( this.element );
- },
-
- stepUp: modifier(function( steps ) {
- this._stepUp( steps );
- }),
- _stepUp: function( steps ) {
- if ( this._start() ) {
- this._spin( (steps || 1) * this.options.step );
- this._stop();
- }
- },
-
- stepDown: modifier(function( steps ) {
- this._stepDown( steps );
- }),
- _stepDown: function( steps ) {
- if ( this._start() ) {
- this._spin( (steps || 1) * -this.options.step );
- this._stop();
- }
- },
-
- pageUp: modifier(function( pages ) {
- this._stepUp( (pages || 1) * this.options.page );
- }),
-
- pageDown: modifier(function( pages ) {
- this._stepDown( (pages || 1) * this.options.page );
- }),
-
- value: function( newVal ) {
- if ( !arguments.length ) {
- return this._parse( this.element.val() );
- }
- modifier( this._value ).call( this, newVal );
- },
-
- widget: function() {
- return this.uiSpinner;
- }
-});
-
-}( jQuery ) );
-
-(function( $, undefined ) {
-
-var tabId = 0,
- rhash = /#.*$/;
-
-function getNextTabId() {
- return ++tabId;
-}
-
-function isLocal( anchor ) {
- return anchor.hash.length > 1 &&
- decodeURIComponent( anchor.href.replace( rhash, "" ) ) ===
- decodeURIComponent( location.href.replace( rhash, "" ) );
-}
-
-$.widget( "ui.tabs", {
- version: "1.10.1",
- delay: 300,
- options: {
- active: null,
- collapsible: false,
- event: "click",
- heightStyle: "content",
- hide: null,
- show: null,
-
- // callbacks
- activate: null,
- beforeActivate: null,
- beforeLoad: null,
- load: null
- },
-
- _create: function() {
- var that = this,
- options = this.options;
-
- this.running = false;
-
- this.element
- .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
- .toggleClass( "ui-tabs-collapsible", options.collapsible )
- // Prevent users from focusing disabled tabs via click
- .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
- if ( $( this ).is( ".ui-state-disabled" ) ) {
- event.preventDefault();
- }
- })
- // support: IE <9
- // Preventing the default action in mousedown doesn't prevent IE
- // from focusing the element, so if the anchor gets focused, blur.
- // We don't have to worry about focusing the previously focused
- // element since clicking on a non-focusable element should focus
- // the body anyway.
- .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
- if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
- this.blur();
- }
- });
-
- this._processTabs();
- options.active = this._initialActive();
-
- // Take disabling tabs via class attribute from HTML
- // into account and update option properly.
- if ( $.isArray( options.disabled ) ) {
- options.disabled = $.unique( options.disabled.concat(
- $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
- return that.tabs.index( li );
- })
- ) ).sort();
- }
-
- // check for length avoids error when initializing empty list
- if ( this.options.active !== false && this.anchors.length ) {
- this.active = this._findActive( options.active );
- } else {
- this.active = $();
- }
-
- this._refresh();
-
- if ( this.active.length ) {
- this.load( options.active );
- }
- },
-
- _initialActive: function() {
- var active = this.options.active,
- collapsible = this.options.collapsible,
- locationHash = location.hash.substring( 1 );
-
- if ( active === null ) {
- // check the fragment identifier in the URL
- if ( locationHash ) {
- this.tabs.each(function( i, tab ) {
- if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
- active = i;
- return false;
- }
- });
- }
-
- // check for a tab marked active via a class
- if ( active === null ) {
- active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
- }
-
- // no active tab, set to false
- if ( active === null || active === -1 ) {
- active = this.tabs.length ? 0 : false;
- }
- }
-
- // handle numbers: negative, out of range
- if ( active !== false ) {
- active = this.tabs.index( this.tabs.eq( active ) );
- if ( active === -1 ) {
- active = collapsible ? false : 0;
- }
- }
-
- // don't allow collapsible: false and active: false
- if ( !collapsible && active === false && this.anchors.length ) {
- active = 0;
- }
-
- return active;
- },
-
- _getCreateEventData: function() {
- return {
- tab: this.active,
- panel: !this.active.length ? $() : this._getPanelForTab( this.active )
- };
- },
-
- _tabKeydown: function( event ) {
- /*jshint maxcomplexity:15*/
- var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
- selectedIndex = this.tabs.index( focusedTab ),
- goingForward = true;
-
- if ( this._handlePageNav( event ) ) {
- return;
- }
-
- switch ( event.keyCode ) {
- case $.ui.keyCode.RIGHT:
- case $.ui.keyCode.DOWN:
- selectedIndex++;
- break;
- case $.ui.keyCode.UP:
- case $.ui.keyCode.LEFT:
- goingForward = false;
- selectedIndex--;
- break;
- case $.ui.keyCode.END:
- selectedIndex = this.anchors.length - 1;
- break;
- case $.ui.keyCode.HOME:
- selectedIndex = 0;
- break;
- case $.ui.keyCode.SPACE:
- // Activate only, no collapsing
- event.preventDefault();
- clearTimeout( this.activating );
- this._activate( selectedIndex );
- return;
- case $.ui.keyCode.ENTER:
- // Toggle (cancel delayed activation, allow collapsing)
- event.preventDefault();
- clearTimeout( this.activating );
- // Determine if we should collapse or activate
- this._activate( selectedIndex === this.options.active ? false : selectedIndex );
- return;
- default:
- return;
- }
-
- // Focus the appropriate tab, based on which key was pressed
- event.preventDefault();
- clearTimeout( this.activating );
- selectedIndex = this._focusNextTab( selectedIndex, goingForward );
-
- // Navigating with control key will prevent automatic activation
- if ( !event.ctrlKey ) {
- // Update aria-selected immediately so that AT think the tab is already selected.
- // Otherwise AT may confuse the user by stating that they need to activate the tab,
- // but the tab will already be activated by the time the announcement finishes.
- focusedTab.attr( "aria-selected", "false" );
- this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
-
- this.activating = this._delay(function() {
- this.option( "active", selectedIndex );
- }, this.delay );
- }
- },
-
- _panelKeydown: function( event ) {
- if ( this._handlePageNav( event ) ) {
- return;
- }
-
- // Ctrl+up moves focus to the current tab
- if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
- event.preventDefault();
- this.active.focus();
- }
- },
-
- // Alt+page up/down moves focus to the previous/next tab (and activates)
- _handlePageNav: function( event ) {
- if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
- this._activate( this._focusNextTab( this.options.active - 1, false ) );
- return true;
- }
- if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
- this._activate( this._focusNextTab( this.options.active + 1, true ) );
- return true;
- }
- },
-
- _findNextTab: function( index, goingForward ) {
- var lastTabIndex = this.tabs.length - 1;
-
- function constrain() {
- if ( index > lastTabIndex ) {
- index = 0;
- }
- if ( index < 0 ) {
- index = lastTabIndex;
- }
- return index;
- }
-
- while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
- index = goingForward ? index + 1 : index - 1;
- }
-
- return index;
- },
-
- _focusNextTab: function( index, goingForward ) {
- index = this._findNextTab( index, goingForward );
- this.tabs.eq( index ).focus();
- return index;
- },
-
- _setOption: function( key, value ) {
- if ( key === "active" ) {
- // _activate() will handle invalid values and update this.options
- this._activate( value );
- return;
- }
-
- if ( key === "disabled" ) {
- // don't use the widget factory's disabled handling
- this._setupDisabled( value );
- return;
- }
-
- this._super( key, value);
-
- if ( key === "collapsible" ) {
- this.element.toggleClass( "ui-tabs-collapsible", value );
- // Setting collapsible: false while collapsed; open first panel
- if ( !value && this.options.active === false ) {
- this._activate( 0 );
- }
- }
-
- if ( key === "event" ) {
- this._setupEvents( value );
- }
-
- if ( key === "heightStyle" ) {
- this._setupHeightStyle( value );
- }
- },
-
- _tabId: function( tab ) {
- return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
- },
-
- _sanitizeSelector: function( hash ) {
- return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
- },
-
- refresh: function() {
- var options = this.options,
- lis = this.tablist.children( ":has(a[href])" );
-
- // get disabled tabs from class attribute from HTML
- // this will get converted to a boolean if needed in _refresh()
- options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
- return lis.index( tab );
- });
-
- this._processTabs();
-
- // was collapsed or no tabs
- if ( options.active === false || !this.anchors.length ) {
- options.active = false;
- this.active = $();
- // was active, but active tab is gone
- } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
- // all remaining tabs are disabled
- if ( this.tabs.length === options.disabled.length ) {
- options.active = false;
- this.active = $();
- // activate previous tab
- } else {
- this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
- }
- // was active, active tab still exists
- } else {
- // make sure active index is correct
- options.active = this.tabs.index( this.active );
- }
-
- this._refresh();
- },
-
- _refresh: function() {
- this._setupDisabled( this.options.disabled );
- this._setupEvents( this.options.event );
- this._setupHeightStyle( this.options.heightStyle );
-
- this.tabs.not( this.active ).attr({
- "aria-selected": "false",
- tabIndex: -1
- });
- this.panels.not( this._getPanelForTab( this.active ) )
- .hide()
- .attr({
- "aria-expanded": "false",
- "aria-hidden": "true"
- });
-
- // Make sure one tab is in the tab order
- if ( !this.active.length ) {
- this.tabs.eq( 0 ).attr( "tabIndex", 0 );
- } else {
- this.active
- .addClass( "ui-tabs-active ui-state-active" )
- .attr({
- "aria-selected": "true",
- tabIndex: 0
- });
- this._getPanelForTab( this.active )
- .show()
- .attr({
- "aria-expanded": "true",
- "aria-hidden": "false"
- });
- }
- },
-
- _processTabs: function() {
- var that = this;
-
- this.tablist = this._getList()
- .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
- .attr( "role", "tablist" );
-
- this.tabs = this.tablist.find( "> li:has(a[href])" )
- .addClass( "ui-state-default ui-corner-top" )
- .attr({
- role: "tab",
- tabIndex: -1
- });
-
- this.anchors = this.tabs.map(function() {
- return $( "a", this )[ 0 ];
- })
- .addClass( "ui-tabs-anchor" )
- .attr({
- role: "presentation",
- tabIndex: -1
- });
-
- this.panels = $();
-
- this.anchors.each(function( i, anchor ) {
- var selector, panel, panelId,
- anchorId = $( anchor ).uniqueId().attr( "id" ),
- tab = $( anchor ).closest( "li" ),
- originalAriaControls = tab.attr( "aria-controls" );
-
- // inline tab
- if ( isLocal( anchor ) ) {
- selector = anchor.hash;
- panel = that.element.find( that._sanitizeSelector( selector ) );
- // remote tab
- } else {
- panelId = that._tabId( tab );
- selector = "#" + panelId;
- panel = that.element.find( selector );
- if ( !panel.length ) {
- panel = that._createPanel( panelId );
- panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
- }
- panel.attr( "aria-live", "polite" );
- }
-
- if ( panel.length) {
- that.panels = that.panels.add( panel );
- }
- if ( originalAriaControls ) {
- tab.data( "ui-tabs-aria-controls", originalAriaControls );
- }
- tab.attr({
- "aria-controls": selector.substring( 1 ),
- "aria-labelledby": anchorId
- });
- panel.attr( "aria-labelledby", anchorId );
- });
-
- this.panels
- .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
- .attr( "role", "tabpanel" );
- },
-
- // allow overriding how to find the list for rare usage scenarios (#7715)
- _getList: function() {
- return this.element.find( "ol,ul" ).eq( 0 );
- },
-
- _createPanel: function( id ) {
- return $( "<div>" )
- .attr( "id", id )
- .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
- .data( "ui-tabs-destroy", true );
- },
-
- _setupDisabled: function( disabled ) {
- if ( $.isArray( disabled ) ) {
- if ( !disabled.length ) {
- disabled = false;
- } else if ( disabled.length === this.anchors.length ) {
- disabled = true;
- }
- }
-
- // disable tabs
- for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
- if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
- $( li )
- .addClass( "ui-state-disabled" )
- .attr( "aria-disabled", "true" );
- } else {
- $( li )
- .removeClass( "ui-state-disabled" )
- .removeAttr( "aria-disabled" );
- }
- }
-
- this.options.disabled = disabled;
- },
-
- _setupEvents: function( event ) {
- var events = {
- click: function( event ) {
- event.preventDefault();
- }
- };
- if ( event ) {
- $.each( event.split(" "), function( index, eventName ) {
- events[ eventName ] = "_eventHandler";
- });
- }
-
- this._off( this.anchors.add( this.tabs ).add( this.panels ) );
- this._on( this.anchors, events );
- this._on( this.tabs, { keydown: "_tabKeydown" } );
- this._on( this.panels, { keydown: "_panelKeydown" } );
-
- this._focusable( this.tabs );
- this._hoverable( this.tabs );
- },
-
- _setupHeightStyle: function( heightStyle ) {
- var maxHeight,
- parent = this.element.parent();
-
- if ( heightStyle === "fill" ) {
- maxHeight = parent.height();
- maxHeight -= this.element.outerHeight() - this.element.height();
-
- this.element.siblings( ":visible" ).each(function() {
- var elem = $( this ),
- position = elem.css( "position" );
-
- if ( position === "absolute" || position === "fixed" ) {
- return;
- }
- maxHeight -= elem.outerHeight( true );
- });
-
- this.element.children().not( this.panels ).each(function() {
- maxHeight -= $( this ).outerHeight( true );
- });
-
- this.panels.each(function() {
- $( this ).height( Math.max( 0, maxHeight -
- $( this ).innerHeight() + $( this ).height() ) );
- })
- .css( "overflow", "auto" );
- } else if ( heightStyle === "auto" ) {
- maxHeight = 0;
- this.panels.each(function() {
- maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
- }).height( maxHeight );
- }
- },
-
- _eventHandler: function( event ) {
- var options = this.options,
- active = this.active,
- anchor = $( event.currentTarget ),
- tab = anchor.closest( "li" ),
- clickedIsActive = tab[ 0 ] === active[ 0 ],
- collapsing = clickedIsActive && options.collapsible,
- toShow = collapsing ? $() : this._getPanelForTab( tab ),
- toHide = !active.length ? $() : this._getPanelForTab( active ),
- eventData = {
- oldTab: active,
- oldPanel: toHide,
- newTab: collapsing ? $() : tab,
- newPanel: toShow
- };
-
- event.preventDefault();
-
- if ( tab.hasClass( "ui-state-disabled" ) ||
- // tab is already loading
- tab.hasClass( "ui-tabs-loading" ) ||
- // can't switch durning an animation
- this.running ||
- // click on active header, but not collapsible
- ( clickedIsActive && !options.collapsible ) ||
- // allow canceling activation
- ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
- return;
- }
-
- options.active = collapsing ? false : this.tabs.index( tab );
-
- this.active = clickedIsActive ? $() : tab;
- if ( this.xhr ) {
- this.xhr.abort();
- }
-
- if ( !toHide.length && !toShow.length ) {
- $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
- }
-
- if ( toShow.length ) {
- this.load( this.tabs.index( tab ), event );
- }
- this._toggle( event, eventData );
- },
-
- // handles show/hide for selecting tabs
- _toggle: function( event, eventData ) {
- var that = this,
- toShow = eventData.newPanel,
- toHide = eventData.oldPanel;
-
- this.running = true;
-
- function complete() {
- that.running = false;
- that._trigger( "activate", event, eventData );
- }
-
- function show() {
- eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
-
- if ( toShow.length && that.options.show ) {
- that._show( toShow, that.options.show, complete );
- } else {
- toShow.show();
- complete();
- }
- }
-
- // start out by hiding, then showing, then completing
- if ( toHide.length && this.options.hide ) {
- this._hide( toHide, this.options.hide, function() {
- eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
- show();
- });
- } else {
- eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
- toHide.hide();
- show();
- }
-
- toHide.attr({
- "aria-expanded": "false",
- "aria-hidden": "true"
- });
- eventData.oldTab.attr( "aria-selected", "false" );
- // If we're switching tabs, remove the old tab from the tab order.
- // If we're opening from collapsed state, remove the previous tab from the tab order.
- // If we're collapsing, then keep the collapsing tab in the tab order.
- if ( toShow.length && toHide.length ) {
- eventData.oldTab.attr( "tabIndex", -1 );
- } else if ( toShow.length ) {
- this.tabs.filter(function() {
- return $( this ).attr( "tabIndex" ) === 0;
- })
- .attr( "tabIndex", -1 );
- }
-
- toShow.attr({
- "aria-expanded": "true",
- "aria-hidden": "false"
- });
- eventData.newTab.attr({
- "aria-selected": "true",
- tabIndex: 0
- });
- },
-
- _activate: function( index ) {
- var anchor,
- active = this._findActive( index );
-
- // trying to activate the already active panel
- if ( active[ 0 ] === this.active[ 0 ] ) {
- return;
- }
-
- // trying to collapse, simulate a click on the current active header
- if ( !active.length ) {
- active = this.active;
- }
-
- anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
- this._eventHandler({
- target: anchor,
- currentTarget: anchor,
- preventDefault: $.noop
- });
- },
-
- _findActive: function( index ) {
- return index === false ? $() : this.tabs.eq( index );
- },
-
- _getIndex: function( index ) {
- // meta-function to give users option to provide a href string instead of a numerical index.
- if ( typeof index === "string" ) {
- index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
- }
-
- return index;
- },
-
- _destroy: function() {
- if ( this.xhr ) {
- this.xhr.abort();
- }
-
- this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
-
- this.tablist
- .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
- .removeAttr( "role" );
-
- this.anchors
- .removeClass( "ui-tabs-anchor" )
- .removeAttr( "role" )
- .removeAttr( "tabIndex" )
- .removeUniqueId();
-
- this.tabs.add( this.panels ).each(function() {
- if ( $.data( this, "ui-tabs-destroy" ) ) {
- $( this ).remove();
- } else {
- $( this )
- .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
- "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
- .removeAttr( "tabIndex" )
- .removeAttr( "aria-live" )
- .removeAttr( "aria-busy" )
- .removeAttr( "aria-selected" )
- .removeAttr( "aria-labelledby" )
- .removeAttr( "aria-hidden" )
- .removeAttr( "aria-expanded" )
- .removeAttr( "role" );
- }
- });
-
- this.tabs.each(function() {
- var li = $( this ),
- prev = li.data( "ui-tabs-aria-controls" );
- if ( prev ) {
- li
- .attr( "aria-controls", prev )
- .removeData( "ui-tabs-aria-controls" );
- } else {
- li.removeAttr( "aria-controls" );
- }
- });
-
- this.panels.show();
-
- if ( this.options.heightStyle !== "content" ) {
- this.panels.css( "height", "" );
- }
- },
-
- enable: function( index ) {
- var disabled = this.options.disabled;
- if ( disabled === false ) {
- return;
- }
-
- if ( index === undefined ) {
- disabled = false;
- } else {
- index = this._getIndex( index );
- if ( $.isArray( disabled ) ) {
- disabled = $.map( disabled, function( num ) {
- return num !== index ? num : null;
- });
- } else {
- disabled = $.map( this.tabs, function( li, num ) {
- return num !== index ? num : null;
- });
- }
- }
- this._setupDisabled( disabled );
- },
-
- disable: function( index ) {
- var disabled = this.options.disabled;
- if ( disabled === true ) {
- return;
- }
-
- if ( index === undefined ) {
- disabled = true;
- } else {
- index = this._getIndex( index );
- if ( $.inArray( index, disabled ) !== -1 ) {
- return;
- }
- if ( $.isArray( disabled ) ) {
- disabled = $.merge( [ index ], disabled ).sort();
- } else {
- disabled = [ index ];
- }
- }
- this._setupDisabled( disabled );
- },
-
- load: function( index, event ) {
- index = this._getIndex( index );
- var that = this,
- tab = this.tabs.eq( index ),
- anchor = tab.find( ".ui-tabs-anchor" ),
- panel = this._getPanelForTab( tab ),
- eventData = {
- tab: tab,
- panel: panel
- };
-
- // not remote
- if ( isLocal( anchor[ 0 ] ) ) {
- return;
- }
-
- this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
-
- // support: jQuery <1.8
- // jQuery <1.8 returns false if the request is canceled in beforeSend,
- // but as of 1.8, $.ajax() always returns a jqXHR object.
- if ( this.xhr && this.xhr.statusText !== "canceled" ) {
- tab.addClass( "ui-tabs-loading" );
- panel.attr( "aria-busy", "true" );
-
- this.xhr
- .success(function( response ) {
- // support: jQuery <1.8
- // http://bugs.jquery.com/ticket/11778
- setTimeout(function() {
- panel.html( response );
- that._trigger( "load", event, eventData );
- }, 1 );
- })
- .complete(function( jqXHR, status ) {
- // support: jQuery <1.8
- // http://bugs.jquery.com/ticket/11778
- setTimeout(function() {
- if ( status === "abort" ) {
- that.panels.stop( false, true );
- }
-
- tab.removeClass( "ui-tabs-loading" );
- panel.removeAttr( "aria-busy" );
-
- if ( jqXHR === that.xhr ) {
- delete that.xhr;
- }
- }, 1 );
- });
- }
- },
-
- _ajaxSettings: function( anchor, event, eventData ) {
- var that = this;
- return {
- url: anchor.attr( "href" ),
- beforeSend: function( jqXHR, settings ) {
- return that._trigger( "beforeLoad", event,
- $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
- }
- };
- },
-
- _getPanelForTab: function( tab ) {
- var id = $( tab ).attr( "aria-controls" );
- return this.element.find( this._sanitizeSelector( "#" + id ) );
- }
-});
-
-})( jQuery );
-
-(function( $ ) {
-
-var increments = 0;
-
-function addDescribedBy( elem, id ) {
- var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
- describedby.push( id );
- elem
- .data( "ui-tooltip-id", id )
- .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
-}
-
-function removeDescribedBy( elem ) {
- var id = elem.data( "ui-tooltip-id" ),
- describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
- index = $.inArray( id, describedby );
- if ( index !== -1 ) {
- describedby.splice( index, 1 );
- }
-
- elem.removeData( "ui-tooltip-id" );
- describedby = $.trim( describedby.join( " " ) );
- if ( describedby ) {
- elem.attr( "aria-describedby", describedby );
- } else {
- elem.removeAttr( "aria-describedby" );
- }
-}
-
-$.widget( "ui.tooltip", {
- version: "1.10.1",
- options: {
- content: function() {
- // support: IE<9, Opera in jQuery <1.7
- // .text() can't accept undefined, so coerce to a string
- var title = $( this ).attr( "title" ) || "";
- // Escape title, since we're going from an attribute to raw HTML
- return $( "<a>" ).text( title ).html();
- },
- hide: true,
- // Disabled elements have inconsistent behavior across browsers (#8661)
- items: "[title]:not([disabled])",
- position: {
- my: "left top+15",
- at: "left bottom",
- collision: "flipfit flip"
- },
- show: true,
- tooltipClass: null,
- track: false,
-
- // callbacks
- close: null,
- open: null
- },
-
- _create: function() {
- this._on({
- mouseover: "open",
- focusin: "open"
- });
-
- // IDs of generated tooltips, needed for destroy
- this.tooltips = {};
- // IDs of parent tooltips where we removed the title attribute
- this.parents = {};
-
- if ( this.options.disabled ) {
- this._disable();
- }
- },
-
- _setOption: function( key, value ) {
- var that = this;
-
- if ( key === "disabled" ) {
- this[ value ? "_disable" : "_enable" ]();
- this.options[ key ] = value;
- // disable element style changes
- return;
- }
-
- this._super( key, value );
-
- if ( key === "content" ) {
- $.each( this.tooltips, function( id, element ) {
- that._updateContent( element );
- });
- }
- },
-
- _disable: function() {
- var that = this;
-
- // close open tooltips
- $.each( this.tooltips, function( id, element ) {
- var event = $.Event( "blur" );
- event.target = event.currentTarget = element[0];
- that.close( event, true );
- });
-
- // remove title attributes to prevent native tooltips
- this.element.find( this.options.items ).addBack().each(function() {
- var element = $( this );
- if ( element.is( "[title]" ) ) {
- element
- .data( "ui-tooltip-title", element.attr( "title" ) )
- .attr( "title", "" );
- }
- });
- },
-
- _enable: function() {
- // restore title attributes
- this.element.find( this.options.items ).addBack().each(function() {
- var element = $( this );
- if ( element.data( "ui-tooltip-title" ) ) {
- element.attr( "title", element.data( "ui-tooltip-title" ) );
- }
- });
- },
-
- open: function( event ) {
- var that = this,
- target = $( event ? event.target : this.element )
- // we need closest here due to mouseover bubbling,
- // but always pointing at the same event target
- .closest( this.options.items );
-
- // No element to show a tooltip for or the tooltip is already open
- if ( !target.length || target.data( "ui-tooltip-id" ) ) {
- return;
- }
-
- if ( target.attr( "title" ) ) {
- target.data( "ui-tooltip-title", target.attr( "title" ) );
- }
-
- target.data( "ui-tooltip-open", true );
-
- // kill parent tooltips, custom or native, for hover
- if ( event && event.type === "mouseover" ) {
- target.parents().each(function() {
- var parent = $( this ),
- blurEvent;
- if ( parent.data( "ui-tooltip-open" ) ) {
- blurEvent = $.Event( "blur" );
- blurEvent.target = blurEvent.currentTarget = this;
- that.close( blurEvent, true );
- }
- if ( parent.attr( "title" ) ) {
- parent.uniqueId();
- that.parents[ this.id ] = {
- element: this,
- title: parent.attr( "title" )
- };
- parent.attr( "title", "" );
- }
- });
- }
-
- this._updateContent( target, event );
- },
-
- _updateContent: function( target, event ) {
- var content,
- contentOption = this.options.content,
- that = this,
- eventType = event ? event.type : null;
-
- if ( typeof contentOption === "string" ) {
- return this._open( event, target, contentOption );
- }
-
- content = contentOption.call( target[0], function( response ) {
- // ignore async response if tooltip was closed already
- if ( !target.data( "ui-tooltip-open" ) ) {
- return;
- }
- // IE may instantly serve a cached response for ajax requests
- // delay this call to _open so the other call to _open runs first
- that._delay(function() {
- // jQuery creates a special event for focusin when it doesn't
- // exist natively. To improve performance, the native event
- // object is reused and the type is changed. Therefore, we can't
- // rely on the type being correct after the event finished
- // bubbling, so we set it back to the previous value. (#8740)
- if ( event ) {
- event.type = eventType;
- }
- this._open( event, target, response );
- });
- });
- if ( content ) {
- this._open( event, target, content );
- }
- },
-
- _open: function( event, target, content ) {
- var tooltip, events, delayedShow,
- positionOption = $.extend( {}, this.options.position );
-
- if ( !content ) {
- return;
- }
-
- // Content can be updated multiple times. If the tooltip already
- // exists, then just update the content and bail.
- tooltip = this._find( target );
- if ( tooltip.length ) {
- tooltip.find( ".ui-tooltip-content" ).html( content );
- return;
- }
-
- // if we have a title, clear it to prevent the native tooltip
- // we have to check first to avoid defining a title if none exists
- // (we don't want to cause an element to start matching [title])
- //
- // We use removeAttr only for key events, to allow IE to export the correct
- // accessible attributes. For mouse events, set to empty string to avoid
- // native tooltip showing up (happens only when removing inside mouseover).
- if ( target.is( "[title]" ) ) {
- if ( event && event.type === "mouseover" ) {
- target.attr( "title", "" );
- } else {
- target.removeAttr( "title" );
- }
- }
-
- tooltip = this._tooltip( target );
- addDescribedBy( target, tooltip.attr( "id" ) );
- tooltip.find( ".ui-tooltip-content" ).html( content );
-
- function position( event ) {
- positionOption.of = event;
- if ( tooltip.is( ":hidden" ) ) {
- return;
- }
- tooltip.position( positionOption );
- }
- if ( this.options.track && event && /^mouse/.test( event.type ) ) {
- this._on( this.document, {
- mousemove: position
- });
- // trigger once to override element-relative positioning
- position( event );
- } else {
- tooltip.position( $.extend({
- of: target
- }, this.options.position ) );
- }
-
- tooltip.hide();
-
- this._show( tooltip, this.options.show );
- // Handle tracking tooltips that are shown with a delay (#8644). As soon
- // as the tooltip is visible, position the tooltip using the most recent
- // event.
- if ( this.options.show && this.options.show.delay ) {
- delayedShow = this.delayedShow = setInterval(function() {
- if ( tooltip.is( ":visible" ) ) {
- position( positionOption.of );
- clearInterval( delayedShow );
- }
- }, $.fx.interval );
- }
-
- this._trigger( "open", event, { tooltip: tooltip } );
-
- events = {
- keyup: function( event ) {
- if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
- var fakeEvent = $.Event(event);
- fakeEvent.currentTarget = target[0];
- this.close( fakeEvent, true );
- }
- },
- remove: function() {
- this._removeTooltip( tooltip );
- }
- };
- if ( !event || event.type === "mouseover" ) {
- events.mouseleave = "close";
- }
- if ( !event || event.type === "focusin" ) {
- events.focusout = "close";
- }
- this._on( true, target, events );
- },
-
- close: function( event ) {
- var that = this,
- target = $( event ? event.currentTarget : this.element ),
- tooltip = this._find( target );
-
- // disabling closes the tooltip, so we need to track when we're closing
- // to avoid an infinite loop in case the tooltip becomes disabled on close
- if ( this.closing ) {
- return;
- }
-
- // Clear the interval for delayed tracking tooltips
- clearInterval( this.delayedShow );
-
- // only set title if we had one before (see comment in _open())
- if ( target.data( "ui-tooltip-title" ) ) {
- target.attr( "title", target.data( "ui-tooltip-title" ) );
- }
-
- removeDescribedBy( target );
-
- tooltip.stop( true );
- this._hide( tooltip, this.options.hide, function() {
- that._removeTooltip( $( this ) );
- });
-
- target.removeData( "ui-tooltip-open" );
- this._off( target, "mouseleave focusout keyup" );
- // Remove 'remove' binding only on delegated targets
- if ( target[0] !== this.element[0] ) {
- this._off( target, "remove" );
- }
- this._off( this.document, "mousemove" );
-
- if ( event && event.type === "mouseleave" ) {
- $.each( this.parents, function( id, parent ) {
- $( parent.element ).attr( "title", parent.title );
- delete that.parents[ id ];
- });
- }
-
- this.closing = true;
- this._trigger( "close", event, { tooltip: tooltip } );
- this.closing = false;
- },
-
- _tooltip: function( element ) {
- var id = "ui-tooltip-" + increments++,
- tooltip = $( "<div>" )
- .attr({
- id: id,
- role: "tooltip"
- })
- .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
- ( this.options.tooltipClass || "" ) );
- $( "<div>" )
- .addClass( "ui-tooltip-content" )
- .appendTo( tooltip );
- tooltip.appendTo( this.document[0].body );
- this.tooltips[ id ] = element;
- return tooltip;
- },
-
- _find: function( target ) {
- var id = target.data( "ui-tooltip-id" );
- return id ? $( "#" + id ) : $();
- },
-
- _removeTooltip: function( tooltip ) {
- tooltip.remove();
- delete this.tooltips[ tooltip.attr( "id" ) ];
- },
-
- _destroy: function() {
- var that = this;
-
- // close open tooltips
- $.each( this.tooltips, function( id, element ) {
- // Delegate to close method to handle common cleanup
- var event = $.Event( "blur" );
- event.target = event.currentTarget = element[0];
- that.close( event, true );
-
- // Remove immediately; destroying an open tooltip doesn't use the
- // hide animation
- $( "#" + id ).remove();
-
- // Restore the title
- if ( element.data( "ui-tooltip-title" ) ) {
- element.attr( "title", element.data( "ui-tooltip-title" ) );
- element.removeData( "ui-tooltip-title" );
- }
- });
- }
-});
-
-}( jQuery ) );
diff --git a/ui/js/jquery.min.js b/ui/js/jquery.min.js
deleted file mode 100644
index 006e953..0000000
--- a/ui/js/jquery.min.js
+++ /dev/null
@@ -1,5 +0,0 @@
-/*! jQuery v1.9.1 | (c) 2005, 2012 jQuery Foundation, Inc. | jquery.org/license
-//@ sourceMappingURL=jquery.min.map
-*/(function(e,t){var n,r,i=typeof t,o=e.document,a=e.location,s=e.jQuery,u=e.$,l={},c=[],p="1.9.1",f=c.concat,d=c.push,h=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,b=function(e,t){return new b.fn.init(e,t,r)},x=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,N=/^(?:(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^[\],:{}\s]*$/,E=/(?:^|:|,)(?:\s*\[)+/g,S=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,A=/"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g,j=/^-ms-/,D=/-([\da-z])/gi,L=function(e,t){return t.toUpperCase()},H=function(e){(o.addEventListener||"load"===e.type||"complete"===o.readyState)&&(q(),b.ready())},q=function(){o.addEventListener?(o.removeEventListener("DOMContentLoaded",H,!1),e.removeEventListener("load",H,!1)):(o.detachEvent("onreadystatechange",H),e.detachEvent("onload",H))};b.fn=b.prototype={jquery:p,constructor:b,init:function(e,n,r){var i,a;if(!e)return this;if("string"==!
typeof e){if(i="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:N.exec(e),!i||!i[1]&&n)return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e);if(i[1]){if(n=n instanceof b?n[0]:n,b.merge(this,b.parseHTML(i[1],n&&n.nodeType?n.ownerDocument||n:o,!0)),C.test(i[1])&&b.isPlainObject(n))for(i in n)b.isFunction(this[i])?this[i](n[i]):this.attr(i,n[i]);return this}if(a=o.getElementById(i[2]),a&&a.parentNode){if(a.id!==i[2])return r.find(e);this.length=1,this[0]=a}return this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):b.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),b.makeArray(e,this))},selector:"",length:0,size:function(){return this.length},toArray:function(){return h.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=b.merge(this.constructor(),e);return t.prevObject=this,t.context=this.con!
text,t},each:function(e,t){return b.each(this,e,t)},ready:function(e){return b.ready.promise().done(e),this},slice:function(){return this.pushStack(h.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(b.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:d,sort:[].sort,splice:[].splice},b.fn.init.prototype=b.fn,b.extend=b.fn.extend=function(){var e,n,r,i,o,a,s=arguments[0]||{},u=1,l=arguments.length,c=!1;for("boolean"==typeof s&&(c=s,s=arguments[1]||{},u=2),"object"==typeof s||b.isFunction(s)||(s={}),l===u&&(s=this,--u);l>u;u++)if(null!=(o=arguments[u]))for(i in o)e=s[i],r=o[i],s!==r&&(c&&r&&(b.isPlainObject(r)||(n=b.isArray(r)))?(n?(n=!1,a=e&&b.isArray(e)?e:[]):a=e&&b.isPlainObject(e)?e:{},s[i]=b.extend(c,a,r)):r!==t&&(s[i]=r));return !
s},b.extend({noConflict:function(t){return e.$===b&&(e.$=u),t&&e.jQuery!
===b&&(e.jQuery=s),b},isReady:!1,readyWait:1,holdReady:function(e){e?b.readyWait++:b.ready(!0)},ready:function(e){if(e===!0?!--b.readyWait:!b.isReady){if(!o.body)return setTimeout(b.ready);b.isReady=!0,e!==!0&&--b.readyWait>0||(n.resolveWith(o,[b]),b.fn.trigger&&b(o).trigger("ready").off("ready"))}},isFunction:function(e){return"function"===b.type(e)},isArray:Array.isArray||function(e){return"array"===b.type(e)},isWindow:function(e){return null!=e&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if(!e||"object"!==b.type(e)||e.nodeType||b.isWindow(e))return!1;try{if(e.constructor&&!y.call(e,"constructor")&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||y.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Er!
ror(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=b.buildFragment([e],t,i),i&&b(i).remove(),b.merge([],r.childNodes))},parseJSON:function(n){return e.JSON&&e.JSON.parse?e.JSON.parse(n):null===n?n:"string"==typeof n&&(n=b.trim(n),n&&k.test(n.replace(S,"@").replace(A,"]").replace(E,"")))?Function("return "+n)():(b.error("Invalid JSON: "+n),t)},parseXML:function(n){var r,i;if(!n||"string"!=typeof n)return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(o){r=t}return r&&r.documentElement&&!r.getElementsByTagName("parsererror").length||b.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&b.trim(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(j,"ms-").replace(D,L)},nodeName:function(e,t){return !
e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e!
,t,n){var r,i=0,o=e.length,a=M(e);if(n){if(a){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(a){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:v&&!v.call("\ufeff\u00a0")?function(e){return null==e?"":v.call(e)}:function(e){return null==e?"":(e+"").replace(T,"")},makeArray:function(e,t){var n=t||[];return null!=e&&(M(Object(e))?b.merge(n,"string"==typeof e?[e]:e):d.call(n,e)),n},inArray:function(e,t,n){var r;if(t){if(g)return g.call(t,e,n);for(r=t.length,n=n?0>n?Math.max(0,r+n):n:0;r>n;n++)if(n in t&&t[n]===e)return n}return-1},merge:function(e,n){var r=n.length,i=e.length,o=0;if("number"==typeof r)for(;r>o;o++)e[i++]=n[o];else while(n[o]!==t)e[i++]=n[o++];return e.length=i,e},grep:function(e,t,n){var r,i=[],o=0,a=e.length;for(n=!!n;a>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,a=M(e),s=[];if(a)for(;!
o>i;i++)r=t(e[i],i,n),null!=r&&(s[s.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(s[s.length]=r);return f.apply([],s)},guid:1,proxy:function(e,n){var r,i,o;return"string"==typeof n&&(o=e[n],n=e,e=o),b.isFunction(e)?(r=h.call(arguments,2),i=function(){return e.apply(n||this,r.concat(h.call(arguments)))},i.guid=e.guid=e.guid||b.guid++,i):t},access:function(e,n,r,i,o,a,s){var u=0,l=e.length,c=null==r;if("object"===b.type(r)){o=!0;for(u in r)b.access(e,n,u,r[u],!0,a,s)}else if(i!==t&&(o=!0,b.isFunction(i)||(s=!0),c&&(s?(n.call(e,i),n=null):(c=n,n=function(e,t,n){return c.call(b(e),n)})),n))for(;l>u;u++)n(e[u],r,s?i:i.call(e[u],u,n(e[u],r)));return o?e:c?n.call(e):l?n(e[0],r):a},now:function(){return(new Date).getTime()}}),b.ready.promise=function(t){if(!n)if(n=b.Deferred(),"complete"===o.readyState)setTimeout(b.ready);else if(o.addEventListener)o.addEventListener("DOMContentLoaded",H,!1),e.addEventListener("load",H,!1);else{o.attachEvent("onreadystatechange",H),e.attachEve!
nt("onload",H);var r=!1;try{r=null==e.frameElement&&o.documentElement}c!
atch(i){}r&&r.doScroll&&function a(){if(!b.isReady){try{r.doScroll("left")}catch(e){return setTimeout(a,50)}q(),b.ready()}}()}return n.promise(t)},b.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function M(e){var t=e.length,n=b.type(e);return b.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}r=b(o);var _={};function F(e){var t=_[e]={};return b.each(e.match(w)||[],function(e,n){t[n]=!0}),t}b.Callbacks=function(e){e="string"==typeof e?_[e]||F(e):b.extend({},e);var n,r,i,o,a,s,u=[],l=!e.once&&[],c=function(t){for(r=e.memory&&t,i=!0,a=s||0,s=0,o=u.length,n=!0;u&&o>a;a++)if(u[a].apply(t[0],t[1])===!1&&e.stopOnFalse){r=!1;break}n=!1,u&&(l?l.length&&c(l.shift()):r?u=[]:p.disable())},p={add:function(){if(u){var t=u.length;(function i(t){b.each(t,function(t,n){var r=b.type(n);"function"===r?e.unique&&p.has(n)||u.push(n):n&&n.length&&"strin!
g"!==r&&i(n)})})(arguments),n?o=u.length:r&&(s=t,c(r))}return this},remove:function(){return u&&b.each(arguments,function(e,t){var r;while((r=b.inArray(t,u,r))>-1)u.splice(r,1),n&&(o>=r&&o--,a>=r&&a--)}),this},has:function(e){return e?b.inArray(e,u)>-1:!(!u||!u.length)},empty:function(){return u=[],this},disable:function(){return u=l=r=t,this},disabled:function(){return!u},lock:function(){return l=t,r||p.disable(),this},locked:function(){return!l},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],!u||i&&!l||(n?l.push(t):c(t)),this},fire:function(){return p.fireWith(this,arguments),this},fired:function(){return!!i}};return p},b.extend({Deferred:function(e){var t=[["resolve","done",b.Callbacks("once memory"),"resolved"],["reject","fail",b.Callbacks("once memory"),"rejected"],["notify","progress",b.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return!
b.Deferred(function(n){b.each(t,function(t,o){var a=o[0],s=b.isFunctio!
n(e[t])&&e[t];i[o[1]](function(){var e=s&&s.apply(this,arguments);e&&b.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[a+"With"](this===r?n.promise():this,s?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?b.extend(e,r):r}},i={};return r.pipe=r.then,b.each(t,function(e,o){var a=o[2],s=o[3];r[o[1]]=a.add,s&&a.add(function(){n=s},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=a.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=h.call(arguments),r=n.length,i=1!==r||e&&b.isFunction(e.promise)?r:0,o=1===i?e:b.Deferred(),a=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?h.call(arguments):r,n===s?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},s,u,l;if(r>1)for(s=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&b.isFunction(n[t].promise)?n[t].promise().done(a(t,l,n)).fail(o.reject).progress(a(t,u,s)):--i;return i||o!
.resolveWith(l,n),o.promise()}}),b.support=function(){var t,n,r,a,s,u,l,c,p,f,d=o.createElement("div");if(d.setAttribute("className","t"),d.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",n=d.getElementsByTagName("*"),r=d.getElementsByTagName("a")[0],!n||!r||!n.length)return{};s=o.createElement("select"),l=s.appendChild(o.createElement("option")),a=d.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={getSetAttribute:"t"!==d.className,leadingWhitespace:3===d.firstChild.nodeType,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:"/a"===r.getAttribute("href"),opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:!!a.value,optSelected:l.selected,enctype:!!o.createElement("form").enctype,html5Clone:"<:nav></:nav>"!==o.createElement("nav").cloneNode(!0).outerHTML,boxModel:"CSS1Compat"===o.compa!
tMode,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrink!
WrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},a.checked=!0,t.noCloneChecked=a.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!l.disabled;try{delete d.test}catch(h){t.deleteExpando=!1}a=o.createElement("input"),a.setAttribute("value",""),t.input=""===a.getAttribute("value"),a.value="t",a.setAttribute("type","radio"),t.radioValue="t"===a.value,a.setAttribute("checked","t"),a.setAttribute("name","t"),u=o.createDocumentFragment(),u.appendChild(a),t.appendChecked=a.checked,t.checkClone=u.cloneNode(!0).cloneNode(!0).lastChild.checked,d.attachEvent&&(d.attachEvent("onclick",function(){t.noCloneEvent=!1}),d.cloneNode(!0).click());for(f in{submit:!0,change:!0,focusin:!0})d.setAttribute(c="on"+f,"t"),t[f+"Bubbles"]=c in e||d.attributes[c].expando===!1;return d.style.backgroundClip="content-box",d.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===d.style.backgroundClip,b(function(){var n,r,a,s="padding:0;margin:0;border:0;display:!
block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",u=o.getElementsByTagName("body")[0];u&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",u.appendChild(n).appendChild(d),d.innerHTML="<table><tr><td></td><td>t</td></tr></table>",a=d.getElementsByTagName("td"),a[0].style.cssText="padding:0;margin:0;border:0;display:none",p=0===a[0].offsetHeight,a[0].style.display="",a[1].style.display="none",t.reliableHiddenOffsets=p&&0===a[0].offsetHeight,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=4===d.offsetWidth,t.doesNotIncludeMarginInBodyOffset=1!==u.offsetTop,e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(d,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(d,null)||{width:"4px"}).!
width,r=d.appendChild(o.createElement("div")),r.style.cssText=d.style.c!
ssText=s,r.style.marginRight=r.style.width="0",d.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),typeof d.style.zoom!==i&&(d.innerHTML="",d.style.cssText=s+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=3===d.offsetWidth,d.style.display="block",d.innerHTML="<div></div>",d.firstChild.style.width="5px",t.shrinkWrapBlocks=3!==d.offsetWidth,t.inlineBlockNeedsLayout&&(u.style.zoom=1)),u.removeChild(n),n=d=a=r=null)}),n=s=u=l=r=a=null,t}();var O=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,B=/([A-Z])/g;function P(e,n,r,i){if(b.acceptData(e)){var o,a,s=b.expando,u="string"==typeof n,l=e.nodeType,p=l?b.cache:e,f=l?e[s]:e[s]&&s;if(f&&p[f]&&(i||p[f].data)||!u||r!==t)return f||(l?e[s]=f=c.pop()||b.guid++:f=s),p[f]||(p[f]={},l||(p[f].toJSON=b.noop)),("object"==typeof n||"function"==typeof n)&&(i?p[f]=b.extend(p[f],n):p[f].data=b.extend(p[f].data,n)),o=p[f],i||(o.data||(o.data={}),o=o.data),r!==t&&(o[b.camelCase(n)]=r),u?(a=o[!
n],null==a&&(a=o[b.camelCase(n)])):a=o,a}}function R(e,t,n){if(b.acceptData(e)){var r,i,o,a=e.nodeType,s=a?b.cache:e,u=a?e[b.expando]:b.expando;if(s[u]){if(t&&(o=n?s[u]:s[u].data)){b.isArray(t)?t=t.concat(b.map(t,b.camelCase)):t in o?t=[t]:(t=b.camelCase(t),t=t in o?[t]:t.split(" "));for(r=0,i=t.length;i>r;r++)delete o[t[r]];if(!(n?$:b.isEmptyObject)(o))return}(n||(delete s[u].data,$(s[u])))&&(a?b.cleanData([e],!0):b.support.deleteExpando||s!=s.window?delete s[u]:s[u]=null)}}}b.extend({cache:{},expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?b.cache[e[b.expando]]:e[b.expando],!!e&&!$(e)},data:function(e,t,n){return P(e,t,n)},removeData:function(e,t){return R(e,t)},_data:function(e,t,n){return P(e,t,n,!0)},_removeData:function(e,t){return R(e,t,!0)},acceptData:function(e){if(e.nodeType&&1!==e.nodeType&&9!==e.nodeType)return!1;var t=e.nodeName&&b.noData!
[e.nodeName.toLowerCase()];return!t||t!==!0&&e.getAttribute("classid")=!
==t}}),b.fn.extend({data:function(e,n){var r,i,o=this[0],a=0,s=null;if(e===t){if(this.length&&(s=b.data(o),1===o.nodeType&&!b._data(o,"parsedAttrs"))){for(r=o.attributes;r.length>a;a++)i=r[a].name,i.indexOf("data-")||(i=b.camelCase(i.slice(5)),W(o,i,s[i]));b._data(o,"parsedAttrs",!0)}return s}return"object"==typeof e?this.each(function(){b.data(this,e)}):b.access(this,function(n){return n===t?o?W(o,e,b.data(o,e)):null:(this.each(function(){b.data(this,e,n)}),t)},null,n,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){b.removeData(this,e)})}});function W(e,n,r){if(r===t&&1===e.nodeType){var i="data-"+n.replace(B,"-$1").toLowerCase();if(r=e.getAttribute(i),"string"==typeof r){try{r="true"===r?!0:"false"===r?!1:"null"===r?null:+r+""===r?+r:O.test(r)?b.parseJSON(r):r}catch(o){}b.data(e,n,r)}else r=t}return r}function $(e){var t;for(t in e)if(("data"!==t||!b.isEmptyObject(e[t]))&&"toJSON"!==t)return!1;return!0}b.extend({queue:function(e,n,r){var i;!
return e?(n=(n||"fx")+"queue",i=b._data(e,n),r&&(!i||b.isArray(r)?i=b._data(e,n,b.makeArray(r)):i.push(r)),i||[]):t},dequeue:function(e,t){t=t||"fx";var n=b.queue(e,t),r=n.length,i=n.shift(),o=b._queueHooks(e,t),a=function(){b.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),o.cur=i,i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return b._data(e,n)||b._data(e,n,{empty:b.Callbacks("once memory").add(function(){b._removeData(e,t+"queue"),b._removeData(e,n)})})}}),b.fn.extend({queue:function(e,n){var r=2;return"string"!=typeof e&&(n=e,e="fx",r--),r>arguments.length?b.queue(this[0],e):n===t?this:this.each(function(){var t=b.queue(this,e,n);b._queueHooks(this,e),"fx"===e&&"inprogress"!==t[0]&&b.dequeue(this,e)})},dequeue:function(e){return this.each(function(){b.dequeue(this,e)})},delay:function(e,t){return e=b.fx?b.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,!
e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return !
this.queue(e||"fx",[])},promise:function(e,n){var r,i=1,o=b.Deferred(),a=this,s=this.length,u=function(){--i||o.resolveWith(a,[a])};"string"!=typeof e&&(n=e,e=t),e=e||"fx";while(s--)r=b._data(a[s],e+"queueHooks"),r&&r.empty&&(i++,r.empty.add(u));return u(),o.promise(n)}});var I,z,X=/[\t\r\n]/g,U=/\r/g,V=/^(?:input|select|textarea|button|object)$/i,Y=/^(?:a|area)$/i,J=/^(?:checked|selected|autofocus|autoplay|async|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped)$/i,G=/^(?:checked|selected)$/i,Q=b.support.getSetAttribute,K=b.support.input;b.fn.extend({attr:function(e,t){return b.access(this,b.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){b.removeAttr(this,e)})},prop:function(e,t){return b.access(this,b.prop,e,t,arguments.length>1)},removeProp:function(e){return e=b.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,o,a=0,s=this.length,u="string"==typeof e&!
&e;if(b.isFunction(e))return this.each(function(t){b(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=b.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,a=0,s=this.length,u=0===arguments.length||"string"==typeof e&&e;if(b.isFunction(e))return this.each(function(t){b(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];s>a;a++)if(n=this[a],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(X," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?b.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e,r="boolean"==typeof t;return b.isFunction(e)?this.each(function(n){b(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var o,a=0,s=b(this),u=t!
,l=e.match(w)||[];while(o=l[a++])u=r?u:!s.hasClass(o),s[u?"addClass":"r!
emoveClass"](o)}else(n===i||"boolean"===n)&&(this.className&&b._data(this,"__className__",this.className),this.className=this.className||e===!1?"":b._data(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(X," ").indexOf(t)>=0)return!0;return!1},val:function(e){var n,r,i,o=this[0];{if(arguments.length)return i=b.isFunction(e),this.each(function(n){var o,a=b(this);1===this.nodeType&&(o=i?e.call(this,n,a.val()):e,null==o?o="":"number"==typeof o?o+="":b.isArray(o)&&(o=b.map(o,function(e){return null==e?"":e+""})),r=b.valHooks[this.type]||b.valHooks[this.nodeName.toLowerCase()],r&&"set"in r&&r.set(this,o,"value")!==t||(this.value=o))});if(o)return r=b.valHooks[o.type]||b.valHooks[o.nodeName.toLowerCase()],r&&"get"in r&&(n=r.get(o,"value"))!==t?n:(n=o.value,"string"==typeof n?n.replace(U,""):null==n?"":n)}}}),b.extend({valHooks:{option:{get:function(e){var t=e.attributes.v!
alue;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,a=o?null:[],s=o?i+1:r.length,u=0>i?s:o?i:0;for(;s>u;u++)if(n=r[u],!(!n.selected&&u!==i||(b.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&b.nodeName(n.parentNode,"optgroup"))){if(t=b(n).val(),o)return t;a.push(t)}return a},set:function(e,t){var n=b.makeArray(t);return b(e).find("option").each(function(){this.selected=b.inArray(b(this).val(),n)>=0}),n.length||(e.selectedIndex=-1),n}}},attr:function(e,n,r){var o,a,s,u=e.nodeType;if(e&&3!==u&&8!==u&&2!==u)return typeof e.getAttribute===i?b.prop(e,n,r):(a=1!==u||!b.isXMLDoc(e),a&&(n=n.toLowerCase(),o=b.attrHooks[n]||(J.test(n)?z:I)),r===t?o&&a&&"get"in o&&null!==(s=o.get(e,n))?s:(typeof e.getAttribute!==i&&(s=e.getAttribute(n)),null==s?t:s):null!==r?o&&a&&"set"in o&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r):(b.removeAttr(e,n),t))},removeAttr:fun!
ction(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i!
++])r=b.propFix[n]||n,J.test(n)?!Q&&G.test(n)?e[b.camelCase("default-"+n)]=e[r]=!1:e[r]=!1:b.attr(e,n,""),e.removeAttribute(Q?n:r)},attrHooks:{type:{set:function(e,t){if(!b.support.radioValue&&"radio"===t&&b.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(e,n,r){var i,o,a,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return a=1!==s||!b.isXMLDoc(e),a&&(n=b.propFix[n]||n,o=b.propHooks[n]),r!==t?o&&"set"in o&&(i=o.set(e,r,n))!==t?i:e[n]=r:o&&"get"in o&&null!==(i=o.get(e,n))?i:e[n]},propHooks:{tabIndex:{get:function(e){var n=e.getAttributeNode("tabindex");return n&&n.specified?parseInt(n.value,10):V.test(e.nodeName)||Y.test(e.nodeName)&&e.href?0:t}}}}),z={get:funct!
ion(e,n){var r=b.prop(e,n),i="boolean"==typeof r&&e.getAttribute(n),o="boolean"==typeof r?K&&Q?null!=i:G.test(n)?e[b.camelCase("default-"+n)]:!!i:e.getAttributeNode(n);return o&&o.value!==!1?n.toLowerCase():t},set:function(e,t,n){return t===!1?b.removeAttr(e,n):K&&Q||!G.test(n)?e.setAttribute(!Q&&b.propFix[n]||n,n):e[b.camelCase("default-"+n)]=e[n]=!0,n}},K&&Q||(b.attrHooks.value={get:function(e,n){var r=e.getAttributeNode(n);return b.nodeName(e,"input")?e.defaultValue:r&&r.specified?r.value:t},set:function(e,n,r){return b.nodeName(e,"input")?(e.defaultValue=n,t):I&&I.set(e,n,r)}}),Q||(I=b.valHooks.button={get:function(e,n){var r=e.getAttributeNode(n);return r&&("id"===n||"name"===n||"coords"===n?""!==r.value:r.specified)?r.value:t},set:function(e,n,r){var i=e.getAttributeNode(r);return i||e.setAttributeNode(i=e.ownerDocument.createAttribute(r)),i.value=n+="","value"===r||n===e.getAttribute(r)?n:t}},b.attrHooks.contenteditable={get:I.get,set:function(e,t,n){I.set(e,""===t?!!
1:t,n)}},b.each(["width","height"],function(e,n){b.attrHooks[n]=b.exten!
d(b.attrHooks[n],{set:function(e,r){return""===r?(e.setAttribute(n,"auto"),r):t}})})),b.support.hrefNormalized||(b.each(["href","src","width","height"],function(e,n){b.attrHooks[n]=b.extend(b.attrHooks[n],{get:function(e){var r=e.getAttribute(n,2);return null==r?t:r}})}),b.each(["href","src"],function(e,t){b.propHooks[t]={get:function(e){return e.getAttribute(t,4)}}})),b.support.style||(b.attrHooks.style={get:function(e){return e.style.cssText||t},set:function(e,t){return e.style.cssText=t+""}}),b.support.optSelected||(b.propHooks.selected=b.extend(b.propHooks.selected,{get:function(e){var t=e.parentNode;return t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex),null}})),b.support.enctype||(b.propFix.enctype="encoding"),b.support.checkOn||b.each(["radio","checkbox"],function(){b.valHooks[this]={get:function(e){return null===e.getAttribute("value")?"on":e.value}}}),b.each(["radio","checkbox"],function(){b.valHooks[this]=b.extend(b.valHooks[this],{set:function(e,n){!
return b.isArray(n)?e.checked=b.inArray(b(e).val(),n)>=0:t}})});var Z=/^(?:input|select|textarea)$/i,et=/^key/,tt=/^(?:mouse|contextmenu)|click/,nt=/^(?:focusinfocus|focusoutblur)$/,rt=/^([^.]*)(?:\.(.+)|)$/;function it(){return!0}function ot(){return!1}b.event={global:{},add:function(e,n,r,o,a){var s,u,l,c,p,f,d,h,g,m,y,v=b._data(e);if(v){r.handler&&(c=r,r=c.handler,a=c.selector),r.guid||(r.guid=b.guid++),(u=v.events)||(u=v.events={}),(f=v.handle)||(f=v.handle=function(e){return typeof b===i||e&&b.event.triggered===e.type?t:b.event.dispatch.apply(f.elem,arguments)},f.elem=e),n=(n||"").match(w)||[""],l=n.length;while(l--)s=rt.exec(n[l])||[],g=y=s[1],m=(s[2]||"").split(".").sort(),p=b.event.special[g]||{},g=(a?p.delegateType:p.bindType)||g,p=b.event.special[g]||{},d=b.extend({type:g,origType:y,data:o,handler:r,guid:r.guid,selector:a,needsContext:a&&b.expr.match.needsContext.test(a),namespace:m.join(".")},c),(h=u[g])||(h=u[g]=[],h.delegateCount=0,p.setup&&p.setup.call(e,o,m,f!
)!==!1||(e.addEventListener?e.addEventListener(g,f,!1):e.attachEvent&&e!
.attachEvent("on"+g,f))),p.add&&(p.add.call(e,d),d.handler.guid||(d.handler.guid=r.guid)),a?h.splice(h.delegateCount++,0,d):h.push(d),b.event.global[g]=!0;e=null}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,p,f,d,h,g,m=b.hasData(e)&&b._data(e);if(m&&(c=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(s=rt.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){p=b.event.special[d]||{},d=(r?p.delegateType:p.bindType)||d,f=c[d]||[],s=s[2]&&RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),u=o=f.length;while(o--)a=f[o],!i&&g!==a.origType||n&&n.guid!==a.guid||s&&!s.test(a.namespace)||r&&r!==a.selector&&("**"!==r||!a.selector)||(f.splice(o,1),a.selector&&f.delegateCount--,p.remove&&p.remove.call(e,a));u&&!f.length&&(p.teardown&&p.teardown.call(e,h,m.handle)!==!1||b.removeEvent(e,d,m.handle),delete c[d])}else for(d in c)b.event.remove(e,d+t[l],n,r,!0);b.isEmptyObject(c)&&(delete m.handle,b._removeData(e,"events"))}},trigger:function(n,r,i,a){var s,u,l,c,p,f,d,h=[i|!
|o],g=y.call(n,"type")?n.type:n,m=y.call(n,"namespace")?n.namespace.split("."):[];if(l=f=i=i||o,3!==i.nodeType&&8!==i.nodeType&&!nt.test(g+b.event.triggered)&&(g.indexOf(".")>=0&&(m=g.split("."),g=m.shift(),m.sort()),u=0>g.indexOf(":")&&"on"+g,n=n[b.expando]?n:new b.Event(g,"object"==typeof n&&n),n.isTrigger=!0,n.namespace=m.join("."),n.namespace_re=n.namespace?RegExp("(^|\\.)"+m.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,n.result=t,n.target||(n.target=i),r=null==r?[n]:b.makeArray(r,[n]),p=b.event.special[g]||{},a||!p.trigger||p.trigger.apply(i,r)!==!1)){if(!a&&!p.noBubble&&!b.isWindow(i)){for(c=p.delegateType||g,nt.test(c+g)||(l=l.parentNode);l;l=l.parentNode)h.push(l),f=l;f===(i.ownerDocument||o)&&h.push(f.defaultView||f.parentWindow||e)}d=0;while((l=h[d++])&&!n.isPropagationStopped())n.type=d>1?c:p.bindType||g,s=(b._data(l,"events")||{})[n.type]&&b._data(l,"handle"),s&&s.apply(l,r),s=u&&l[u],s&&b.acceptData(l)&&s.apply&&s.apply(l,r)===!1&&n.preventDefault();if(n.type=g,!(a||n!
.isDefaultPrevented()||p._default&&p._default.apply(i.ownerDocument,r)!!
==!1||"click"===g&&b.nodeName(i,"a")||!b.acceptData(i)||!u||!i[g]||b.isWindow(i))){f=i[u],f&&(i[u]=null),b.event.triggered=g;try{i[g]()}catch(v){}b.event.triggered=t,f&&(i[u]=f)}return n.result}},dispatch:function(e){e=b.event.fix(e);var n,r,i,o,a,s=[],u=h.call(arguments),l=(b._data(this,"events")||{})[e.type]||[],c=b.event.special[e.type]||{};if(u[0]=e,e.delegateTarget=this,!c.preDispatch||c.preDispatch.call(this,e)!==!1){s=b.event.handlers.call(this,e,l),n=0;while((o=s[n++])&&!e.isPropagationStopped()){e.currentTarget=o.elem,a=0;while((i=o.handlers[a++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(i.namespace))&&(e.handleObj=i,e.data=i.data,r=((b.event.special[i.origType]||{}).handle||i.handler).apply(o.elem,u),r!==t&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return c.postDispatch&&c.postDispatch.call(this,e),e.result}},handlers:function(e,n){var r,i,o,a,s=[],u=n.delegateCount,l=e.target;if(u&&l.nodeType&&(!e.button||"click!
"!==e.type))for(;l!=this;l=l.parentNode||this)if(1===l.nodeType&&(l.disabled!==!0||"click"!==e.type)){for(o=[],a=0;u>a;a++)i=n[a],r=i.selector+" ",o[r]===t&&(o[r]=i.needsContext?b(r,this).index(l)>=0:b.find(r,this,null,[l]).length),o[r]&&o.push(i);o.length&&s.push({elem:l,handlers:o})}return n.length>u&&s.push({elem:this,handlers:n.slice(u)}),s},fix:function(e){if(e[b.expando])return e;var t,n,r,i=e.type,a=e,s=this.fixHooks[i];s||(this.fixHooks[i]=s=tt.test(i)?this.mouseHooks:et.test(i)?this.keyHooks:{}),r=s.props?this.props.concat(s.props):this.props,e=new b.Event(a),t=r.length;while(t--)n=r[t],e[n]=a[n];return e.target||(e.target=a.srcElement||o),3===e.target.nodeType&&(e.target=e.target.parentNode),e.metaKey=!!e.metaKey,s.filter?s.filter(e,a):e},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){retu!
rn null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},m!
ouseHooks:{props:"button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,n){var r,i,a,s=n.button,u=n.fromElement;return null==e.pageX&&null!=n.clientX&&(i=e.target.ownerDocument||o,a=i.documentElement,r=i.body,e.pageX=n.clientX+(a&&a.scrollLeft||r&&r.scrollLeft||0)-(a&&a.clientLeft||r&&r.clientLeft||0),e.pageY=n.clientY+(a&&a.scrollTop||r&&r.scrollTop||0)-(a&&a.clientTop||r&&r.clientTop||0)),!e.relatedTarget&&u&&(e.relatedTarget=u===e.target?n.toElement:u),e.which||s===t||(e.which=1&s?1:2&s?3:4&s?2:0),e}},special:{load:{noBubble:!0},click:{trigger:function(){return b.nodeName(this,"input")&&"checkbox"===this.type&&this.click?(this.click(),!1):t}},focus:{trigger:function(){if(this!==o.activeElement&&this.focus)try{return this.focus(),!1}catch(e){}},delegateType:"focusin"},blur:{trigger:function(){return this===o.activeElement&&this.blur?(this.blur(),!1):t},delegateType:"focusout"},beforeunload:{postDispa!
tch:function(e){e.result!==t&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=b.extend(new b.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?b.event.trigger(i,null,t):b.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},b.removeEvent=o.removeEventListener?function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)}:function(e,t,n){var r="on"+t;e.detachEvent&&(typeof e[r]===i&&(e[r]=null),e.detachEvent(r,n))},b.Event=function(e,n){return this instanceof b.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.returnValue===!1||e.getPreventDefault&&e.getPreventDefault()?it:ot):this.type=e,n&&b.extend(this,n),this.timeStamp=e&&e.timeStamp||b.now(),this[b.expando]=!0,t):new b.Event(e,n)},b.Event.prototype={isDefaultPrevented:ot,isPropagationStopped:ot,isImmediatePropagationStopped:ot,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=it,e&&(e.prevent!
Default?e.preventDefault():e.returnValue=!1)},stopPropagation:function(!
){var e=this.originalEvent;this.isPropagationStopped=it,e&&(e.stopPropagation&&e.stopPropagation(),e.cancelBubble=!0)},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=it,this.stopPropagation()}},b.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){b.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;
-return(!i||i!==r&&!b.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),b.support.submitBubbles||(b.event.special.submit={setup:function(){return b.nodeName(this,"form")?!1:(b.event.add(this,"click._submit keypress._submit",function(e){var n=e.target,r=b.nodeName(n,"input")||b.nodeName(n,"button")?n.form:t;r&&!b._data(r,"submitBubbles")&&(b.event.add(r,"submit._submit",function(e){e._submit_bubble=!0}),b._data(r,"submitBubbles",!0))}),t)},postDispatch:function(e){e._submit_bubble&&(delete e._submit_bubble,this.parentNode&&!e.isTrigger&&b.event.simulate("submit",this.parentNode,e,!0))},teardown:function(){return b.nodeName(this,"form")?!1:(b.event.remove(this,"._submit"),t)}}),b.support.changeBubbles||(b.event.special.change={setup:function(){return Z.test(this.nodeName)?(("checkbox"===this.type||"radio"===this.type)&&(b.event.add(this,"propertychange._change",function(e){"checked"===e.originalEvent.propertyName&&(this._just_changed=!0)}),b.!
event.add(this,"click._change",function(e){this._just_changed&&!e.isTrigger&&(this._just_changed=!1),b.event.simulate("change",this,e,!0)})),!1):(b.event.add(this,"beforeactivate._change",function(e){var t=e.target;Z.test(t.nodeName)&&!b._data(t,"changeBubbles")&&(b.event.add(t,"change._change",function(e){!this.parentNode||e.isSimulated||e.isTrigger||b.event.simulate("change",this.parentNode,e,!0)}),b._data(t,"changeBubbles",!0))}),t)},handle:function(e){var n=e.target;return this!==n||e.isSimulated||e.isTrigger||"radio"!==n.type&&"checkbox"!==n.type?e.handleObj.handler.apply(this,arguments):t},teardown:function(){return b.event.remove(this,"._change"),!Z.test(this.nodeName)}}),b.support.focusinBubbles||b.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){b.event.simulate(t,e.target,b.event.fix(e),!0)};b.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),b.fn.extend(!
{on:function(e,n,r,i,o){var a,s;if("object"==typeof e){"string"!=typeof n&&(r=r||n,n=t);for(a in e)this.on(a,n,r,e[a],o);return this}if(null==r&&null==i?(i=n,r=n=t):null==i&&("string"==typeof n?(i=r,r=t):(i=r,r=n,n=t)),i===!1)i=ot;else if(!i)return this;return 1===o&&(s=i,i=function(e){return b().off(e),s.apply(this,arguments)},i.guid=s.guid||(s.guid=b.guid++)),this.each(function(){b.event.add(this,e,i,r,n)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,n,r){var i,o;if(e&&e.preventDefault&&e.handleObj)return i=e.handleObj,b(e.delegateTarget).off(i.namespace?i.origType+"."+i.namespace:i.origType,i.selector,i.handler),this;if("object"==typeof e){for(o in e)this.off(o,n,e[o]);return this}return(n===!1||"function"==typeof n)&&(r=n,n=t),r===!1&&(r=ot),this.each(function(){b.event.remove(this,e,r,n)})},bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:!
function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t!
,e||"**",n)},trigger:function(e,t){return this.each(function(){b.event.trigger(e,t,this)})},triggerHandler:function(e,n){var r=this[0];return r?b.event.trigger(e,n,r,!0):t}}),function(e,t){var n,r,i,o,a,s,u,l,c,p,f,d,h,g,m,y,v,x="sizzle"+-new Date,w=e.document,T={},N=0,C=0,k=it(),E=it(),S=it(),A=typeof t,j=1<<31,D=[],L=D.pop,H=D.push,q=D.slice,M=D.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},_="[\\x20\\t\\r\\n\\f]",F="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=F.replace("w","w#"),B="([*^$|!~]?=)",P="\\["+_+"*("+F+")"+_+"*(?:"+B+_+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+O+")|)|)"+_+"*\\]",R=":("+F+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+P.replace(3,8)+")*)|.*)\\)|)",W=RegExp("^"+_+"+|((?:^|[^\\\\])(?:\\\\.)*)"+_+"+$","g"),$=RegExp("^"+_+"*,"+_+"*"),I=RegExp("^"+_+"*([\\x20\\t\\r\\n\\f>+~])"+_+"*"),z=RegExp(R),X=RegExp("^"+O+"$"),U={ID:RegExp("^#("+F+")"),CLASS:RegExp("^\\.("+F+")"),NAME:RegExp("^\\[name=['\"]?(!
"+F+")['\"]?\\]"),TAG:RegExp("^("+F.replace("w","w*")+")"),ATTR:RegExp("^"+P),PSEUDO:RegExp("^"+R),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+_+"*(even|odd|(([+-]|)(\\d*)n|)"+_+"*(?:([+-]|)"+_+"*(\\d+)|))"+_+"*\\)|)","i"),needsContext:RegExp("^"+_+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+_+"*((?:-\\d)?\\d*)"+_+"*\\)|)(?=[^-]|$)","i")},V=/[\x20\t\r\n\f]*[+~]/,Y=/^[^{]+\{\s*\[native code/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,G=/^(?:input|select|textarea|button)$/i,Q=/^h\d$/i,K=/'|\\/g,Z=/\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,et=/\\([\da-fA-F]{1,6}[\x20\t\r\n\f]?|.)/g,tt=function(e,t){var n="0x"+t-65536;return n!==n?t:0>n?String.fromCharCode(n+65536):String.fromCharCode(55296|n>>10,56320|1023&n)};try{q.call(w.documentElement.childNodes,0)[0].nodeType}catch(nt){q=function(e){var t,n=[];while(t=this[e++])n.push(t);return n}}function rt(e){return Y.test(e+"")}function it(){var e,t=[];return e=function(n,r){return t.push(n+=" ")>i.cach!
eLength&&delete e[t.shift()],e[n]=r}}function ot(e){return e[x]=!0,e}fu!
nction at(e){var t=p.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}}function st(e,t,n,r){var i,o,a,s,u,l,f,g,m,v;if((t?t.ownerDocument||t:w)!==p&&c(t),t=t||p,n=n||[],!e||"string"!=typeof e)return n;if(1!==(s=t.nodeType)&&9!==s)return[];if(!d&&!r){if(i=J.exec(e))if(a=i[1]){if(9===s){if(o=t.getElementById(a),!o||!o.parentNode)return n;if(o.id===a)return n.push(o),n}else if(t.ownerDocument&&(o=t.ownerDocument.getElementById(a))&&y(t,o)&&o.id===a)return n.push(o),n}else{if(i[2])return H.apply(n,q.call(t.getElementsByTagName(e),0)),n;if((a=i[3])&&T.getByClassName&&t.getElementsByClassName)return H.apply(n,q.call(t.getElementsByClassName(a),0)),n}if(T.qsa&&!h.test(e)){if(f=!0,g=x,m=t,v=9===s&&e,1===s&&"object"!==t.nodeName.toLowerCase()){l=ft(e),(f=t.getAttribute("id"))?g=f.replace(K,"\\$&"):t.setAttribute("id",g),g="[id='"+g+"'] ",u=l.length;while(u--)l[u]=g+dt(l[u]);m=V.test(e)&&t.parentNode||t,v=l.join(",")}if(v)try{return H.apply(n,q.call(m.querySelect!
orAll(v),0)),n}catch(b){}finally{f||t.removeAttribute("id")}}}return wt(e.replace(W,"$1"),t,n,r)}a=st.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},c=st.setDocument=function(e){var n=e?e.ownerDocument||e:w;return n!==p&&9===n.nodeType&&n.documentElement?(p=n,f=n.documentElement,d=a(n),T.tagNameNoComments=at(function(e){return e.appendChild(n.createComment("")),!e.getElementsByTagName("*").length}),T.attributes=at(function(e){e.innerHTML="<select></select>";var t=typeof e.lastChild.getAttribute("multiple");return"boolean"!==t&&"string"!==t}),T.getByClassName=at(function(e){return e.innerHTML="<div class='hidden e'></div><div class='hidden'></div>",e.getElementsByClassName&&e.getElementsByClassName("e").length?(e.lastChild.className="e",2===e.getElementsByClassName("e").length):!1}),T.getByName=at(function(e){e.id=x+0,e.innerHTML="<a name='"+x+"'></a><div name='"+x+"'></div>",f.insertBefore(e,f.firstChild);var t=n.getElements!
ByName&&n.getElementsByName(x).length===2+n.getElementsByName(x+0).leng!
th;return T.getIdNotName=!n.getElementById(x),f.removeChild(e),t}),i.attrHandle=at(function(e){return e.innerHTML="<a href='#'></a>",e.firstChild&&typeof e.firstChild.getAttribute!==A&&"#"===e.firstChild.getAttribute("href")})?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},T.getIdNotName?(i.find.ID=function(e,t){if(typeof t.getElementById!==A&&!d){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){return e.getAttribute("id")===t}}):(i.find.ID=function(e,n){if(typeof n.getElementById!==A&&!d){var r=n.getElementById(e);return r?r.id===e||typeof r.getAttributeNode!==A&&r.getAttributeNode("id").value===e?[r]:t:[]}},i.filter.ID=function(e){var t=e.replace(et,tt);return function(e){var n=typeof e.getAttributeNode!==A&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=T.tagNameNoComments?function(e,n){return typeof n.getElementsByTagName!==A?n!
.getElementsByTagName(e):t}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.NAME=T.getByName&&function(e,n){return typeof n.getElementsByName!==A?n.getElementsByName(name):t},i.find.CLASS=T.getByClassName&&function(e,n){return typeof n.getElementsByClassName===A||d?t:n.getElementsByClassName(e)},g=[],h=[":focus"],(T.qsa=rt(n.querySelectorAll))&&(at(function(e){e.innerHTML="<select><option selected=''></option></select>",e.querySelectorAll("[selected]").length||h.push("\\["+_+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||h.push(":checked")}),at(function(e){e.innerHTML="<input type='hidden' i=''/>",e.querySelectorAll("[i^='']").length&&h.push("[*^$]="+_+"*(?:\"\"|'')"),e.querySelectorAll(":enabled").length||h.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),h.push(",.*:")})),(T.matchesSelector=rt(m=f.matchesSelector||f.mozMatc!
hesSelector||f.webkitMatchesSelector||f.oMatchesSelector||f.msMatchesSe!
lector))&&at(function(e){T.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",R)}),h=RegExp(h.join("|")),g=RegExp(g.join("|")),y=rt(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},v=f.compareDocumentPosition?function(e,t){var r;return e===t?(u=!0,0):(r=t.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(t))?1&r||e.parentNode&&11===e.parentNode.nodeType?e===n||y(w,e)?-1:t===n||y(w,t)?1:0:4&r?-1:1:e.compareDocumentPosition?-1:1}:function(e,t){var r,i=0,o=e.parentNode,a=t.parentNode,s=[e],l=[t];if(e===t)return u=!0,0;if(!o||!a)return e===n?-1:t===n?1:o?-1:a?1:0;if(o===a)return ut(e,t);r=e;while(r=r.parentNode)s.unshift(r);r=t;while(r=r.parentNode)l.unshift(r);while(s[i]===l[i])i++!
;return i?ut(s[i],l[i]):s[i]===w?-1:l[i]===w?1:0},u=!1,[0,0].sort(v),T..detectDuplicates=u,p):p},st.matches=function(e,t){return st(e,null,null,t)},st.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Z,"='$1']"),!(!T.matchesSelector||d||g&&g.test(t)||h.test(t)))try{var n=m.call(e,t);if(n||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(r){}return st(t,p,null,[e]).length>0},st.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},st.attr=function(e,t){var n;return(e.ownerDocument||e)!==p&&c(e),d||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):d||T.attributes?e.getAttribute(t):((n=e.getAttributeNode(t))||e.getAttribute(t))&&e[t]===!0?t:n&&n.specified?n.value:null},st.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},st.uniqueSort=function(e){var t,n=[],r=1,i=0;if(u=!T.detectDuplicates,e.sort(v),u){for(;t=e[r];r++)t===e[r-1]&&(i=n.push(r));while(i--)e.splice(n[i],1)}return e};function ut(!
e,t){var n=t&&e,r=n&&(~t.sourceIndex||j)-(~e.sourceIndex||j);if(r)return!
r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function lt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ct(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function pt(e){return ot(function(t){return t=+t,ot(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}o=st.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=st.selectors={cacheLength:50,createPseudo:ot,match:U,find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(et,tt),e[3]=(e[4]||e[5]||"").replace(et,tt),"~="===e[2]&&(e[3!
]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||st.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&st.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return U.CHILD.test(e[0])?null:(e[4]?e[2]=e[4]:n&&z.test(n)&&(t=ft(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){return"*"===e?function(){return!0}:(e=e.replace(et,tt).toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[e+" "];return t||(t=RegExp("(^|"+_+")"+e+"("+_+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==A&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=st.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.sli!
ce(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.!
slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,d,h,g=o!==a?"nextSibling":"previousSibling",m=t.parentNode,y=s&&t.nodeName.toLowerCase(),v=!u&&!s;if(m){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?m.firstChild:m.lastChild],a&&v){c=m[x]||(m[x]={}),l=c[e]||[],d=l[0]===N&&l[1],f=l[0]===N&&l[2],p=d&&m.childNodes[d];while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[N,d,f];break}}else if(v&&(l=(t[x]||(t[x]={}))[e])&&l[0]===N)f=l[1];else while(p=++d&&p&&p[g]||(f=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(v&&((p[x]||(p[x]={}))[e]=[N,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||st.error("unsu!
pported pseudo: "+e);return r[x]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?ot(function(e,n){var i,o=r(e,t),a=o.length;while(a--)i=M.call(e,o[a]),e[i]=!(n[i]=o[a])}):function(e){return r(e,0,n)}):r}},pseudos:{not:ot(function(e){var t=[],n=[],r=s(e.replace(W,"$1"));return r[x]?ot(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:ot(function(e){return function(t){return st(e,t).length>0}}),contains:ot(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:ot(function(e){return X.test(e||"")||st.error("unsupported lang: "+e),e=e.replace(et,tt).toLowerCase(),function(t){var n;do if(n=d?t.getAttribute("xml:lang")||t.getAttribute("lang"):t.lang)return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n!
.slice(1)===t.id},root:function(e){return e===f},focus:function(e){retu!
rn e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return Q.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:pt(function(){return[0]}),last:pt(function(e,t){return[t-1]}),eq:pt(function(e,t,n){return[0>n?n+t:n]})!
,even:pt(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:pt(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:pt(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:pt(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}};for(n in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[n]=lt(n);for(n in{submit:!0,reset:!0})i.pseudos[n]=ct(n);function ft(e,t){var n,r,o,a,s,u,l,c=E[e+" "];if(c)return t?0:c.slice(0);s=e,u=[],l=i.preFilter;while(s){(!n||(r=$.exec(s)))&&(r&&(s=s.slice(r[0].length)||s),u.push(o=[])),n=!1,(r=I.exec(s))&&(n=r.shift(),o.push({value:n,type:r[0].replace(W," ")}),s=s.slice(n.length));for(a in i.filter)!(r=U[a].exec(s))||l[a]&&!(r=l[a](r))||(n=r.shift(),o.push({value:n,type:a,matches:r}),s=s.slice(n.length));if(!n)break}return t?s.length:s?st.error(e):E(e,u).slice(0)}function dt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function ht(e,t,n){var i=t.dir,o=n&&"parentNode"===i,a=C!
++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)ret!
urn e(t,n,r)}:function(t,n,s){var u,l,c,p=N+" "+a;if(s){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,s))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[x]||(t[x]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,s)||r,l[1]===!0)return!0}}function gt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function mt(e,t,n,r,i){var o,a=[],s=0,u=e.length,l=null!=t;for(;u>s;s++)(o=e[s])&&(!n||n(o,r,i))&&(a.push(o),l&&t.push(s));return a}function yt(e,t,n,r,i,o){return r&&!r[x]&&(r=yt(r)),i&&!i[x]&&(i=yt(i,o)),ot(function(o,a,s,u){var l,c,p,f=[],d=[],h=a.length,g=o||xt(t||"*",s.nodeType?[s]:s,[]),m=!e||!o&&t?g:mt(g,f,e,s,u),y=n?i||(o?e:h||r)?[]:a:m;if(n&&n(m,y,s,u),r){l=mt(y,d),r(l,[],s,u),c=l.length;while(c--)(p=l[c])&&(y[d[c]]=!(m[d[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?M.call(o,p):f[c])>-1&!
&(o[l]=!(a[l]=p))}}else y=mt(y===a?y.splice(h,y.length):y),i?i(null,a,y,u):H.apply(a,y)})}function vt(e){var t,n,r,o=e.length,a=i.relative[e[0].type],s=a||i.relative[" "],u=a?1:0,c=ht(function(e){return e===t},s,!0),p=ht(function(e){return M.call(t,e)>-1},s,!0),f=[function(e,n,r){return!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>u;u++)if(n=i.relative[e[u].type])f=[ht(gt(f),n)];else{if(n=i.filter[e[u].type].apply(null,e[u].matches),n[x]){for(r=++u;o>r;r++)if(i.relative[e[r].type])break;return yt(u>1&>(f),u>1&&dt(e.slice(0,u-1)).replace(W,"$1"),n,r>u&&vt(e.slice(u,r)),o>r&&vt(e=e.slice(r)),o>r&&dt(e))}f.push(n)}return gt(f)}function bt(e,t){var n=0,o=t.length>0,a=e.length>0,s=function(s,u,c,f,d){var h,g,m,y=[],v=0,b="0",x=s&&[],w=null!=d,T=l,C=s||a&&i.find.TAG("*",d&&u.parentNode||u),k=N+=null==T?1:Math.random()||.1;for(w&&(l=u!==p&&u,r=n);null!=(h=C[b]);b++){if(a&&h){g=0;while(m=e[g++])if(m(h,u,c)){f.push(h);break}w&&(N=k,r=++n)}o&&((h=!m&&h)&&v--,s&&x.push!
(h))}if(v+=b,o&&b!==v){g=0;while(m=t[g++])m(x,y,u,c);if(s){if(v>0)while!
(b--)x[b]||y[b]||(y[b]=L.call(f));y=mt(y)}H.apply(f,y),w&&!s&&y.length>0&&v+t.length>1&&st.uniqueSort(f)}return w&&(N=k,l=T),x};return o?ot(s):s}s=st.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=ft(e)),n=t.length;while(n--)o=vt(t[n]),o[x]?r.push(o):i.push(o);o=S(e,bt(i,r))}return o};function xt(e,t,n){var r=0,i=t.length;for(;i>r;r++)st(e,t[r],n);return n}function wt(e,t,n,r){var o,a,u,l,c,p=ft(e);if(!r&&1===p.length){if(a=p[0]=p[0].slice(0),a.length>2&&"ID"===(u=a[0]).type&&9===t.nodeType&&!d&&i.relative[a[1].type]){if(t=i.find.ID(u.matches[0].replace(et,tt),t)[0],!t)return n;e=e.slice(a.shift().value.length)}o=U.needsContext.test(e)?0:a.length;while(o--){if(u=a[o],i.relative[l=u.type])break;if((c=i.find[l])&&(r=c(u.matches[0].replace(et,tt),V.test(a[0].type)&&t.parentNode||t))){if(a.splice(o,1),e=r.length&&dt(a),!e)return H.apply(n,q.call(r,0)),n;break}}}return s(e,p)(r,t,d,n,V.test(e)),n}i.pseudos.nth=i.pseudos.eq;function Tt(){}i.filters=Tt.prototype=i.p!
seudos,i.setFilters=new Tt,c(),st.attr=b.attr,b.find=st,b.expr=st.selectors,b.expr[":"]=b.expr.pseudos,b.unique=st.uniqueSort,b.text=st.getText,b.isXMLDoc=st.isXML,b.contains=st.contains}(e);var at=/Until$/,st=/^(?:parents|prev(?:Until|All))/,ut=/^.[^:#\[\.,]*$/,lt=b.expr.match.needsContext,ct={children:!0,contents:!0,next:!0,prev:!0};b.fn.extend({find:function(e){var t,n,r,i=this.length;if("string"!=typeof e)return r=this,this.pushStack(b(e).filter(function(){for(t=0;i>t;t++)if(b.contains(r[t],this))return!0}));for(n=[],t=0;i>t;t++)b.find(e,this[t],n);return n=this.pushStack(i>1?b.unique(n):n),n.selector=(this.selector?this.selector+" ":"")+e,n},has:function(e){var t,n=b(e,this),r=n.length;return this.filter(function(){for(t=0;r>t;t++)if(b.contains(this,n[t]))return!0})},not:function(e){return this.pushStack(ft(this,e,!1))},filter:function(e){return this.pushStack(ft(this,e,!0))},is:function(e){return!!e&&("string"==typeof e?lt.test(e)?b(e,this.context).index(this[0])>=0:b!
.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t!
){var n,r=0,i=this.length,o=[],a=lt.test(e)||"string"!=typeof e?b(e,t||this.context):0;for(;i>r;r++){n=this[r];while(n&&n.ownerDocument&&n!==t&&11!==n.nodeType){if(a?a.index(n)>-1:b.find.matchesSelector(n,e)){o.push(n);break}n=n.parentNode}}return this.pushStack(o.length>1?b.unique(o):o)},index:function(e){return e?"string"==typeof e?b.inArray(this[0],b(e)):b.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?b(e,t):b.makeArray(e&&e.nodeType?[e]:e),r=b.merge(this.get(),n);return this.pushStack(b.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),b.fn.andSelf=b.fn.addBack;function pt(e,t){do e=e[t];while(e&&1!==e.nodeType);return e}b.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return b.dir(e,"parentNode")},parentsUntil:function(e,t,n){return b.dir(e,"parentNode",n)},next:function(e){return!
pt(e,"nextSibling")},prev:function(e){return pt(e,"previousSibling")},nextAll:function(e){return b.dir(e,"nextSibling")},prevAll:function(e){return b.dir(e,"previousSibling")},nextUntil:function(e,t,n){return b.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return b.dir(e,"previousSibling",n)},siblings:function(e){return b.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return b.sibling(e.firstChild)},contents:function(e){return b.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:b.merge([],e.childNodes)}},function(e,t){b.fn[e]=function(n,r){var i=b.map(this,t,n);return at.test(e)||(r=n),r&&"string"==typeof r&&(i=b.filter(r,i)),i=this.length>1&&!ct[e]?b.unique(i):i,this.length>1&&st.test(e)&&(i=i.reverse()),this.pushStack(i)}}),b.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),1===t.length?b.find.matchesSelector(t[0],e)?[t[0]]:[]:b.find.matches(e,t)},dir:function(e,n,r){var i=[],o=e[n];while(o&&9!==o.nodeType&&(r===t||1!==o.nodeT!
ype||!b(o).is(r)))1===o.nodeType&&i.push(o),o=o[n];return i},sibling:fu!
nction(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function ft(e,t,n){if(t=t||0,b.isFunction(t))return b.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return b.grep(e,function(e){return e===t===n});if("string"==typeof t){var r=b.grep(e,function(e){return 1===e.nodeType});if(ut.test(t))return b.filter(t,r,!n);t=b.filter(t,r)}return b.grep(e,function(e){return b.inArray(e,t)>=0===n})}function dt(e){var t=ht.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}var ht="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",gt=/ jQuery\d+="(?:null|\d+)"/g,mt=RegExp("<(?:"+ht+")[\\s/>]","i"),yt=/^\s+/,vt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bt=/<([\w:]+)/,xt=/<tbody/i,wt=/<|&#?\w+;/,Tt=/<(?:script|style|link)/i,Nt=/^(?:checkbox!
|radio)$/i,Ct=/checked\s*(?:[^=]|=\s*.checked.)/i,kt=/^$|\/(?:java|ecma)script/i,Et=/^true\/(.*)/,St=/^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g,At={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],area:[1,"<map>","</map>"],param:[1,"<object>","</object>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],_default:b.support.htmlSerialize?[0,"",""]:[1,"X<div>","</div>"]},jt=dt(o),Dt=jt.appendChild(o.createElement("div"));At.optgroup=At.option,At.tbody=At.tfoot=At.colgroup=At.caption=At.thead,At.th=At.td,b.fn.extend({text:function(e){return b.access(this,function(e){return e===t?b.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(b.isFunction(e))return this.each(function(t){b(this).wrapAll(e.call(this,t))});if(t!
his[0]){var t=b(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parent!
Node&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&1===e.firstChild.nodeType)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return b.isFunction(e)?this.each(function(t){b(this).wrapInner(e.call(this,t))}):this.each(function(){var t=b(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=b.isFunction(e);return this.each(function(n){b(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){b.nodeName(this,"body")||b(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(1===this.nodeType||11===this.nodeType||9===this.nodeType)&&this.insertBefore(e,this.firstChild)})},before:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.!
insertBefore(e,this)})},after:function(){return this.domManip(arguments,!1,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=0;for(;null!=(n=this[r]);r++)(!e||b.filter(e,[n]).length>0)&&(t||1!==n.nodeType||b.cleanData(Ot(n)),n.parentNode&&(t&&b.contains(n.ownerDocument,n)&&Mt(Ot(n,"script")),n.parentNode.removeChild(n)));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++){1===e.nodeType&&b.cleanData(Ot(e,!1));while(e.firstChild)e.removeChild(e.firstChild);e.options&&b.nodeName(e,"select")&&(e.options.length=0)}return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return b.clone(this,e,t)})},html:function(e){return b.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return 1===n.nodeType?n.innerHTML.replace(gt,""):t;if(!("string"!=typeof e||Tt.test(e)||!b.support.htmlSerialize&&mt.test(e)||!b.support.leadingWhitespace&&yt.test(e)||At[(bt.exec(e)||!
["",""])[1].toLowerCase()])){e=e.replace(vt,"<$1></$2>");try{for(;i>r;r!
++)n=this[r]||{},1===n.nodeType&&(b.cleanData(Ot(n,!1)),n.innerHTML=e);n=0}catch(o){}}n&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(e){var t=b.isFunction(e);return t||"string"==typeof e||(e=b(e).not(this).detach()),this.domManip([e],!0,function(e){var t=this.nextSibling,n=this.parentNode;n&&(b(this).remove(),n.insertBefore(e,t))})},detach:function(e){return this.remove(e,!0)},domManip:function(e,n,r){e=f.apply([],e);var i,o,a,s,u,l,c=0,p=this.length,d=this,h=p-1,g=e[0],m=b.isFunction(g);if(m||!(1>=p||"string"!=typeof g||b.support.checkClone)&&Ct.test(g))return this.each(function(i){var o=d.eq(i);m&&(e[0]=g.call(this,i,n?o.html():t)),o.domManip(e,n,r)});if(p&&(l=b.buildFragment(e,this[0].ownerDocument,!1,this),i=l.firstChild,1===l.childNodes.length&&(l=i),i)){for(n=n&&b.nodeName(i,"tr"),s=b.map(Ot(l,"script"),Ht),a=s.length;p>c;c++)o=l,c!==h&&(o=b.clone(o,!0,!0),a&&b.merge(s,Ot(o,"script"))),r.call(n&&b.nodeName(this[c],"table")?Lt(this[c],"tbody"!
):this[c],o,c);if(a)for(u=s[s.length-1].ownerDocument,b.map(s,qt),c=0;a>c;c++)o=s[c],kt.test(o.type||"")&&!b._data(o,"globalEval")&&b.contains(u,o)&&(o.src?b.ajax({url:o.src,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0}):b.globalEval((o.text||o.textContent||o.innerHTML||"").replace(St,"")));l=i=null}return this}});function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function Ht(e){var t=e.getAttributeNode("type");return e.type=(t&&t.specified)+"/"+e.type,e}function qt(e){var t=Et.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function Mt(e,t){var n,r=0;for(;null!=(n=e[r]);r++)b._data(n,"globalEval",!t||b._data(t[r],"globalEval"))}function _t(e,t){if(1===t.nodeType&&b.hasData(e)){var n,r,i,o=b._data(e),a=b._data(t,o),s=o.events;if(s){delete a.handle,a.events={};for(n in s)for(r=0,i=s[n].length;i>r;r++)b.event.add(t,n,s[n][r])}a.data&&(a.data=b.extend({},a.data))}}function Ft(e,t){var n,r,i;if(1===!
t.nodeType){if(n=t.nodeName.toLowerCase(),!b.support.noCloneEvent&&t[b.!
expando]){i=b._data(t);for(r in i.events)b.removeEvent(t,r,i.handle);t.removeAttribute(b.expando)}"script"===n&&t.text!==e.text?(Ht(t).text=e.text,qt(t)):"object"===n?(t.parentNode&&(t.outerHTML=e.outerHTML),b.support.html5Clone&&e.innerHTML&&!b.trim(t.innerHTML)&&(t.innerHTML=e.innerHTML)):"input"===n&&Nt.test(e.type)?(t.defaultChecked=t.checked=e.checked,t.value!==e.value&&(t.value=e.value)):"option"===n?t.defaultSelected=t.selected=e.defaultSelected:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}}b.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){b.fn[e]=function(e){var n,r=0,i=[],o=b(e),a=o.length-1;for(;a>=r;r++)n=r===a?this:this.clone(!0),b(o[r])[t](n),d.apply(i,n.get());return this.pushStack(i)}});function Ot(e,n){var r,o,a=0,s=typeof e.getElementsByTagName!==i?e.getElementsByTagName(n||"*"):typeof e.querySelectorAll!==i?e.querySelectorAll(n||"*"):t;if(!s)for(s=[],r=e.childNo!
des||e;null!=(o=r[a]);a++)!n||b.nodeName(o,n)?s.push(o):b.merge(s,Ot(o,n));return n===t||n&&b.nodeName(e,n)?b.merge([e],s):s}function Bt(e){Nt.test(e.type)&&(e.defaultChecked=e.checked)}b.extend({clone:function(e,t,n){var r,i,o,a,s,u=b.contains(e.ownerDocument,e);if(b.support.html5Clone||b.isXMLDoc(e)||!mt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(Dt.innerHTML=e.outerHTML,Dt.removeChild(o=Dt.firstChild)),!(b.support.noCloneEvent&&b.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||b.isXMLDoc(e)))for(r=Ot(o),s=Ot(e),a=0;null!=(i=s[a]);++a)r[a]&&Ft(i,r[a]);if(t)if(n)for(s=s||Ot(e),r=r||Ot(o),a=0;null!=(i=s[a]);a++)_t(i,r[a]);else _t(e,o);return r=Ot(o,"script"),r.length>0&&Mt(r,!u&&Ot(e,"script")),r=s=i=null,o},buildFragment:function(e,t,n,r){var i,o,a,s,u,l,c,p=e.length,f=dt(t),d=[],h=0;for(;p>h;h++)if(o=e[h],o||0===o)if("object"===b.type(o))b.merge(d,o.nodeType?[o]:o);else if(wt.test(o)){s=s||f.appendChild(t.createElement("div")),u=(bt.exec(o)||["",""])[1].toLowerC!
ase(),c=At[u]||At._default,s.innerHTML=c[1]+o.replace(vt,"<$1></$2>")+c!
[2],i=c[0];while(i--)s=s.lastChild;if(!b.support.leadingWhitespace&&yt.test(o)&&d.push(t.createTextNode(yt.exec(o)[0])),!b.support.tbody){o="table"!==u||xt.test(o)?"<table>"!==c[1]||xt.test(o)?0:s:s.firstChild,i=o&&o.childNodes.length;while(i--)b.nodeName(l=o.childNodes[i],"tbody")&&!l.childNodes.length&&o.removeChild(l)
-}b.merge(d,s.childNodes),s.textContent="";while(s.firstChild)s.removeChild(s.firstChild);s=f.lastChild}else d.push(t.createTextNode(o));s&&f.removeChild(s),b.support.appendChecked||b.grep(Ot(d,"input"),Bt),h=0;while(o=d[h++])if((!r||-1===b.inArray(o,r))&&(a=b.contains(o.ownerDocument,o),s=Ot(f.appendChild(o),"script"),a&&Mt(s),n)){i=0;while(o=s[i++])kt.test(o.type||"")&&n.push(o)}return s=null,f},cleanData:function(e,t){var n,r,o,a,s=0,u=b.expando,l=b.cache,p=b.support.deleteExpando,f=b.event.special;for(;null!=(n=e[s]);s++)if((t||b.acceptData(n))&&(o=n[u],a=o&&l[o])){if(a.events)for(r in a.events)f[r]?b.event.remove(n,r):b.removeEvent(n,r,a.handle);l[o]&&(delete l[o],p?delete n[u]:typeof n.removeAttribute!==i?n.removeAttribute(u):n[u]=null,c.push(o))}}});var Pt,Rt,Wt,$t=/alpha\([^)]*\)/i,It=/opacity\s*=\s*([^)]*)/,zt=/^(top|right|bottom|left)$/,Xt=/^(none|table(?!-c[ea]).+)/,Ut=/^margin/,Vt=RegExp("^("+x+")(.*)$","i"),Yt=RegExp("^("+x+")(?!px)[a-z%]+$","i"),Jt=RegExp("^([+!
-])=("+x+")","i"),Gt={BODY:"block"},Qt={position:"absolute",visibility:"hidden",display:"block"},Kt={letterSpacing:0,fontWeight:400},Zt=["Top","Right","Bottom","Left"],en=["Webkit","O","Moz","ms"];function tn(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=en.length;while(i--)if(t=en[i]+n,t in e)return t;return r}function nn(e,t){return e=t||e,"none"===b.css(e,"display")||!b.contains(e.ownerDocument,e)}function rn(e,t){var n,r,i,o=[],a=0,s=e.length;for(;s>a;a++)r=e[a],r.style&&(o[a]=b._data(r,"olddisplay"),n=r.style.display,t?(o[a]||"none"!==n||(r.style.display=""),""===r.style.display&&nn(r)&&(o[a]=b._data(r,"olddisplay",un(r.nodeName)))):o[a]||(i=nn(r),(n&&"none"!==n||!i)&&b._data(r,"olddisplay",i?n:b.css(r,"display"))));for(a=0;s>a;a++)r=e[a],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[a]||"":"none"));return e}b.fn.extend({css:function(e,n){return b.access(this,function(e,n,r){var i,o,a={},s=0;if(b.isArray(n)!
){for(o=Rt(e),i=n.length;i>s;s++)a[n[s]]=b.css(e,n[s],!1,o);return a}return r!==t?b.style(e,n,r):b.css(e,n)},e,n,arguments.length>1)},show:function(){return rn(this,!0)},hide:function(){return rn(this)},toggle:function(e){var t="boolean"==typeof e;return this.each(function(){(t?e:nn(this))?b(this).show():b(this).hide()})}}),b.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Wt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":b.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var o,a,s,u=b.camelCase(n),l=e.style;if(n=b.cssProps[u]||(b.cssProps[u]=tn(l,u)),s=b.cssHooks[n]||b.cssHooks[u],r===t)return s&&"get"in s&&(o=s.get(e,!1,i))!==t?o:l[n];if(a=typeof r,"string"===a&&(o=Jt.exec(r))&&(r=(o[1]+1)*o[2]+parseFloat(b.css(e,n)),a="number"),!(null==r||"number"===a&&isNaN(r)||("number"!==!
a||b.cssNumber[u]||(r+="px"),b.support.clearCloneStyle||""!==r||0!==n.i!
ndexOf("background")||(l[n]="inherit"),s&&"set"in s&&(r=s.set(e,r,i))===t)))try{l[n]=r}catch(c){}}},css:function(e,n,r,i){var o,a,s,u=b.camelCase(n);return n=b.cssProps[u]||(b.cssProps[u]=tn(e.style,u)),s=b.cssHooks[n]||b.cssHooks[u],s&&"get"in s&&(a=s.get(e,!0,r)),a===t&&(a=Wt(e,n,i)),"normal"===a&&n in Kt&&(a=Kt[n]),""===r||r?(o=parseFloat(a),r===!0||b.isNumeric(o)?o||0:a):a},swap:function(e,t,n,r){var i,o,a={};for(o in t)a[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=a[o];return i}}),e.getComputedStyle?(Rt=function(t){return e.getComputedStyle(t,null)},Wt=function(e,n,r){var i,o,a,s=r||Rt(e),u=s?s.getPropertyValue(n)||s[n]:t,l=e.style;return s&&(""!==u||b.contains(e.ownerDocument,e)||(u=b.style(e,n)),Yt.test(u)&&Ut.test(n)&&(i=l.width,o=l.minWidth,a=l.maxWidth,l.minWidth=l.maxWidth=l.width=u,u=s.width,l.width=i,l.minWidth=o,l.maxWidth=a)),u}):o.documentElement.currentStyle&&(Rt=function(e){return e.currentStyle},Wt=function(e,n,r){var i,o,a,s=r|!
|Rt(e),u=s?s[n]:t,l=e.style;return null==u&&l&&l[n]&&(u=l[n]),Yt.test(u)&&!zt.test(n)&&(i=l.left,o=e.runtimeStyle,a=o&&o.left,a&&(o.left=e.currentStyle.left),l.left="fontSize"===n?"1em":u,u=l.pixelLeft+"px",l.left=i,a&&(o.left=a)),""===u?"auto":u});function on(e,t,n){var r=Vt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function an(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,a=0;for(;4>o;o+=2)"margin"===n&&(a+=b.css(e,n+Zt[o],!0,i)),r?("content"===n&&(a-=b.css(e,"padding"+Zt[o],!0,i)),"margin"!==n&&(a-=b.css(e,"border"+Zt[o]+"Width",!0,i))):(a+=b.css(e,"padding"+Zt[o],!0,i),"padding"!==n&&(a+=b.css(e,"border"+Zt[o]+"Width",!0,i)));return a}function sn(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=Rt(e),a=b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=Wt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Yt.test(i))return i;r=a&&(b.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+an(e,!
t,n||(a?"border":"content"),r,o)+"px"}function un(e){var t=o,n=Gt[e];re!
turn n||(n=ln(e,t),"none"!==n&&n||(Pt=(Pt||b("<iframe frameborder='0' width='0' height='0'/>").css("cssText","display:block !important")).appendTo(t.documentElement),t=(Pt[0].contentWindow||Pt[0].contentDocument).document,t.write("<!doctype html><html><body>"),t.close(),n=ln(e,t),Pt.detach()),Gt[e]=n),n}function ln(e,t){var n=b(t.createElement(e)).appendTo(t.body),r=b.css(n[0],"display");return n.remove(),r}b.each(["height","width"],function(e,n){b.cssHooks[n]={get:function(e,r,i){return r?0===e.offsetWidth&&Xt.test(b.css(e,"display"))?b.swap(e,Qt,function(){return sn(e,n,i)}):sn(e,n,i):t},set:function(e,t,r){var i=r&&Rt(e);return on(e,t,r?an(e,n,r,b.support.boxSizing&&"border-box"===b.css(e,"boxSizing",!1,i),i):0)}}}),b.support.opacity||(b.cssHooks.opacity={get:function(e,t){return It.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=b.isNumeric(t)?"alpha(opacity="+10!
0*t+")":"",o=r&&r.filter||n.filter||"";n.zoom=1,(t>=1||""===t)&&""===b..trim(o.replace($t,""))&&n.removeAttribute&&(n.removeAttribute("filter"),""===t||r&&!r.filter)||(n.filter=$t.test(o)?o.replace($t,i):o+" "+i)}}),b(function(){b.support.reliableMarginRight||(b.cssHooks.marginRight={get:function(e,n){return n?b.swap(e,{display:"inline-block"},Wt,[e,"marginRight"]):t}}),!b.support.pixelPosition&&b.fn.position&&b.each(["top","left"],function(e,n){b.cssHooks[n]={get:function(e,r){return r?(r=Wt(e,n),Yt.test(r)?b(e).position()[n]+"px":r):t}}})}),b.expr&&b.expr.filters&&(b.expr.filters.hidden=function(e){return 0>=e.offsetWidth&&0>=e.offsetHeight||!b.support.reliableHiddenOffsets&&"none"===(e.style&&e.style.display||b.css(e,"display"))},b.expr.filters.visible=function(e){return!b.expr.filters.hidden(e)}),b.each({margin:"",padding:"",border:"Width"},function(e,t){b.cssHooks[e+t]={expand:function(n){var r=0,i={},o="string"==typeof n?n.split(" "):[n];for(;4>r;r++)i[e+Zt[r]+t]=o[r]!
||o[r-2]||o[0];return i}},Ut.test(e)||(b.cssHooks[e+t].set=on)});var cn=!
/%20/g,pn=/\[\]$/,fn=/\r?\n/g,dn=/^(?:submit|button|image|reset|file)$/i,hn=/^(?:input|select|textarea|keygen)/i;b.fn.extend({serialize:function(){return b.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=b.prop(this,"elements");return e?b.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!b(this).is(":disabled")&&hn.test(this.nodeName)&&!dn.test(e)&&(this.checked||!Nt.test(e))}).map(function(e,t){var n=b(this).val();return null==n?null:b.isArray(n)?b.map(n,function(e){return{name:t.name,value:e.replace(fn,"\r\n")}}):{name:t.name,value:n.replace(fn,"\r\n")}}).get()}}),b.param=function(e,n){var r,i=[],o=function(e,t){t=b.isFunction(t)?t():null==t?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};if(n===t&&(n=b.ajaxSettings&&b.ajaxSettings.traditional),b.isArray(e)||e.jquery&&!b.isPlainObject(e))b.each(e,function(){o(this.name,this.value)});else for(r in e)gn(r,e[r],n,o);return i.join("&").replace(c!
n,"+")};function gn(e,t,n,r){var i;if(b.isArray(t))b.each(t,function(t,i){n||pn.test(e)?r(e,i):gn(e+"["+("object"==typeof i?t:"")+"]",i,n,r)});else if(n||"object"!==b.type(t))r(e,t);else for(i in t)gn(e+"["+i+"]",t[i],n,r)}b.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error contextmenu".split(" "),function(e,t){b.fn[t]=function(e,n){return arguments.length>0?this.on(t,null,e,n):this.trigger(t)}}),b.fn.hover=function(e,t){return this.mouseenter(e).mouseleave(t||e)};var mn,yn,vn=b.now(),bn=/\?/,xn=/#.*$/,wn=/([?&])_=[^&]*/,Tn=/^(.*?):[ \t]*([^\r\n]*)\r?$/gm,Nn=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Cn=/^(?:GET|HEAD)$/,kn=/^\/\//,En=/^([\w.+-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,Sn=b.fn.load,An={},jn={},Dn="*/".concat("*");try{yn=a.href}catch(Ln){yn=o.createElement("a"),yn.href="",yn=yn.href}mn=En.exec(yn.toLowerCas!
e())||[];function Hn(e){return function(t,n){"string"!=typeof t&&(n=t,t!
="*");var r,i=0,o=t.toLowerCase().match(w)||[];if(b.isFunction(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function qn(e,n,r,i){var o={},a=e===jn;function s(u){var l;return o[u]=!0,b.each(e[u]||[],function(e,u){var c=u(n,r,i);return"string"!=typeof c||a||o[c]?a?!(l=c):t:(n.dataTypes.unshift(c),s(c),!1)}),l}return s(n.dataTypes[0])||!o["*"]&&s("*")}function Mn(e,n){var r,i,o=b.ajaxSettings.flatOptions||{};for(i in n)n[i]!==t&&((o[i]?e:r||(r={}))[i]=n[i]);return r&&b.extend(!0,e,r),e}b.fn.load=function(e,n,r){if("string"!=typeof e&&Sn)return Sn.apply(this,arguments);var i,o,a,s=this,u=e.indexOf(" ");return u>=0&&(i=e.slice(u,e.length),e=e.slice(0,u)),b.isFunction(n)?(r=n,n=t):n&&"object"==typeof n&&(a="POST"),s.length>0&&b.ajax({url:e,type:a,dataType:"html",data:n}).done(function(e){o=arguments,s.html(i?b("<div>").append(b.parseHTML(e)).find(i):e)}).complete(r&&function(e,t){s.each(r,o||[e.responseText,t,e])}),this},b.e!
ach(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){b.fn[t]=function(e){return this.on(t,e)}}),b.each(["get","post"],function(e,n){b[n]=function(e,r,i,o){return b.isFunction(r)&&(o=o||i,i=r,r=t),b.ajax({url:e,type:n,dataType:o,data:r,success:i})}}),b.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:yn,type:"GET",isLocal:Nn.test(mn[1]),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Dn,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":b.parseJSON,"text xml":b.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?Mn(Mn(e,b.ajaxSettings),t):Mn(b.ajaxSettings,e)},ajaxPrefilter:Hn(An),ajaxTransport:Hn(jn),ajax:function(e,n){"object"!
==typeof e&&(n=e,e=t),n=n||{};var r,i,o,a,s,u,l,c,p=b.ajaxSetup({},n),f!
=p.context||p,d=p.context&&(f.nodeType||f.jquery)?b(f):b.event,h=b.Deferred(),g=b.Callbacks("once memory"),m=p.statusCode||{},y={},v={},x=0,T="canceled",N={readyState:0,getResponseHeader:function(e){var t;if(2===x){if(!c){c={};while(t=Tn.exec(a))c[t[1].toLowerCase()]=t[2]}t=c[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return 2===x?a:null},setRequestHeader:function(e,t){var n=e.toLowerCase();return x||(e=v[n]=v[n]||e,y[e]=t),this},overrideMimeType:function(e){return x||(p.mimeType=e),this},statusCode:function(e){var t;if(e)if(2>x)for(t in e)m[t]=[m[t],e[t]];else N.always(e[N.status]);return this},abort:function(e){var t=e||T;return l&&l.abort(t),k(0,t),this}};if(h.promise(N).complete=g.add,N.success=N.done,N.error=N.fail,p.url=((e||p.url||yn)+"").replace(xn,"").replace(kn,mn[1]+"//"),p.type=n.method||n.type||p.method||p.type,p.dataTypes=b.trim(p.dataType||"*").toLowerCase().match(w)||[""],null==p.crossDomain&&(r=En.exec(p.url.toLowerCase()),p.cr!
ossDomain=!(!r||r[1]===mn[1]&&r[2]===mn[2]&&(r[3]||("http:"===r[1]?80:443))==(mn[3]||("http:"===mn[1]?80:443)))),p.data&&p.processData&&"string"!=typeof p.data&&(p.data=b.param(p.data,p.traditional)),qn(An,p,n,N),2===x)return N;u=p.global,u&&0===b.active++&&b.event.trigger("ajaxStart"),p.type=p.type.toUpperCase(),p.hasContent=!Cn.test(p.type),o=p.url,p.hasContent||(p.data&&(o=p.url+=(bn.test(o)?"&":"?")+p.data,delete p.data),p.cache===!1&&(p.url=wn.test(o)?o.replace(wn,"$1_="+vn++):o+(bn.test(o)?"&":"?")+"_="+vn++)),p.ifModified&&(b.lastModified[o]&&N.setRequestHeader("If-Modified-Since",b.lastModified[o]),b.etag[o]&&N.setRequestHeader("If-None-Match",b.etag[o])),(p.data&&p.hasContent&&p.contentType!==!1||n.contentType)&&N.setRequestHeader("Content-Type",p.contentType),N.setRequestHeader("Accept",p.dataTypes[0]&&p.accepts[p.dataTypes[0]]?p.accepts[p.dataTypes[0]]+("*"!==p.dataTypes[0]?", "+Dn+"; q=0.01":""):p.accepts["*"]);for(i in p.headers)N.setRequestHeader(i,p.headers[i!
]);if(p.beforeSend&&(p.beforeSend.call(f,N,p)===!1||2===x))return N.abo!
rt();T="abort";for(i in{success:1,error:1,complete:1})N[i](p[i]);if(l=qn(jn,p,n,N)){N.readyState=1,u&&d.trigger("ajaxSend",[N,p]),p.async&&p.timeout>0&&(s=setTimeout(function(){N.abort("timeout")},p.timeout));try{x=1,l.send(y,k)}catch(C){if(!(2>x))throw C;k(-1,C)}}else k(-1,"No Transport");function k(e,n,r,i){var c,y,v,w,T,C=n;2!==x&&(x=2,s&&clearTimeout(s),l=t,a=i||"",N.readyState=e>0?4:0,r&&(w=_n(p,N,r)),e>=200&&300>e||304===e?(p.ifModified&&(T=N.getResponseHeader("Last-Modified"),T&&(b.lastModified[o]=T),T=N.getResponseHeader("etag"),T&&(b.etag[o]=T)),204===e?(c=!0,C="nocontent"):304===e?(c=!0,C="notmodified"):(c=Fn(p,w),C=c.state,y=c.data,v=c.error,c=!v)):(v=C,(e||!C)&&(C="error",0>e&&(e=0))),N.status=e,N.statusText=(n||C)+"",c?h.resolveWith(f,[y,C,N]):h.rejectWith(f,[N,C,v]),N.statusCode(m),m=t,u&&d.trigger(c?"ajaxSuccess":"ajaxError",[N,p,c?y:v]),g.fireWith(f,[N,C]),u&&(d.trigger("ajaxComplete",[N,p]),--b.active||b.event.trigger("ajaxStop")))}return N},getScript:funct!
ion(e,n){return b.get(e,t,n,"script")},getJSON:function(e,t,n){return b.get(e,t,n,"json")}});function _n(e,n,r){var i,o,a,s,u=e.contents,l=e.dataTypes,c=e.responseFields;for(s in c)s in r&&(n[c[s]]=r[s]);while("*"===l[0])l.shift(),o===t&&(o=e.mimeType||n.getResponseHeader("Content-Type"));if(o)for(s in u)if(u[s]&&u[s].test(o)){l.unshift(s);break}if(l[0]in r)a=l[0];else{for(s in r){if(!l[0]||e.converters[s+" "+l[0]]){a=s;break}i||(i=s)}a=a||i}return a?(a!==l[0]&&l.unshift(a),r[a]):t}function Fn(e,t){var n,r,i,o,a={},s=0,u=e.dataTypes.slice(),l=u[0];if(e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u[1])for(i in e.converters)a[i.toLowerCase()]=e.converters[i];for(;r=u[++s];)if("*"!==r){if("*"!==l&&l!==r){if(i=a[l+" "+r]||a["* "+r],!i)for(n in a)if(o=n.split(" "),o[1]===r&&(i=a[l+" "+o[0]]||a["* "+o[0]])){i===!0?i=a[n]:a[n]!==!0&&(r=o[0],u.splice(s--,0,r));break}if(i!==!0)if(i&&e["throws"])t=i(t);else try{t=i(t)}catch(c){return{state:"parsererror",error:i?c:"No conversion from "!
+l+" to "+r}}}l=r}return{state:"success",data:t}}b.ajaxSetup({accepts:{!
script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/(?:java|ecma)script/},converters:{"text script":function(e){return b.globalEval(e),e}}}),b.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),b.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=o.head||b("head")[0]||o.documentElement;return{send:function(t,i){n=o.createElement("script"),n.async=!0,e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,t){(t||!n.readyState||/loaded|complete/.test(n.readyState))&&(n.onload=n.onreadystatechange=null,n.parentNode&&n.parentNode.removeChild(n),n=null,t||i(200,"success"))},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(t,!0)}}}});var On=[],Bn=/(=)\?(?=&|$)|\?\?/;b.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=On.pop()||b.expando+"_"+vn++;return this[e]=!0,e}}),b.ajaxPrefilter("json !
jsonp",function(n,r,i){var o,a,s,u=n.jsonp!==!1&&(Bn.test(n.url)?"url":"string"==typeof n.data&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Bn.test(n.data)&&"data");return u||"jsonp"===n.dataTypes[0]?(o=n.jsonpCallback=b.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,u?n[u]=n[u].replace(Bn,"$1"+o):n.jsonp!==!1&&(n.url+=(bn.test(n.url)?"&":"?")+n.jsonp+"="+o),n.converters["script json"]=function(){return s||b.error(o+" was not called"),s[0]},n.dataTypes[0]="json",a=e[o],e[o]=function(){s=arguments},i.always(function(){e[o]=a,n[o]&&(n.jsonpCallback=r.jsonpCallback,On.push(o)),s&&b.isFunction(a)&&a(s[0]),s=a=t}),"script"):t});var Pn,Rn,Wn=0,$n=e.ActiveXObject&&function(){var e;for(e in Pn)Pn[e](t,!0)};function In(){try{return new e.XMLHttpRequest}catch(t){}}function zn(){try{return new e.ActiveXObject("Microsoft.XMLHTTP")}catch(t){}}b.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&In()||zn()}:In,Rn=b.ajaxSettings.xhr(),b.!
support.cors=!!Rn&&"withCredentials"in Rn,Rn=b.support.ajax=!!Rn,Rn&&b.!
ajaxTransport(function(n){if(!n.crossDomain||b.support.cors){var r;return{send:function(i,o){var a,s,u=n.xhr();if(n.username?u.open(n.type,n.url,n.async,n.username,n.password):u.open(n.type,n.url,n.async),n.xhrFields)for(s in n.xhrFields)u[s]=n.xhrFields[s];n.mimeType&&u.overrideMimeType&&u.overrideMimeType(n.mimeType),n.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");try{for(s in i)u.setRequestHeader(s,i[s])}catch(l){}u.send(n.hasContent&&n.data||null),r=function(e,i){var s,l,c,p;try{if(r&&(i||4===u.readyState))if(r=t,a&&(u.onreadystatechange=b.noop,$n&&delete Pn[a]),i)4!==u.readyState&&u.abort();else{p={},s=u.status,l=u.getAllResponseHeaders(),"string"==typeof u.responseText&&(p.text=u.responseText);try{c=u.statusText}catch(f){c=""}s||!n.isLocal||n.crossDomain?1223===s&&(s=204):s=p.text?200:404}}catch(d){i||o(-1,d)}p&&o(s,c,p,l)},n.async?4===u.readyState?setTimeout(r):(a=++Wn,$n&&(Pn||(Pn={},b(e).unload($n)),Pn[a]=r),u.onreadystatechange=r):r(!
)},abort:function(){r&&r(t,!0)}}}});var Xn,Un,Vn=/^(?:toggle|show|hide)$/,Yn=RegExp("^(?:([+-])=|)("+x+")([a-z%]*)$","i"),Jn=/queueHooks$/,Gn=[nr],Qn={"*":[function(e,t){var n,r,i=this.createTween(e,t),o=Yn.exec(t),a=i.cur(),s=+a||0,u=1,l=20;if(o){if(n=+o[2],r=o[3]||(b.cssNumber[e]?"":"px"),"px"!==r&&s){s=b.css(i.elem,e,!0)||n||1;do u=u||".5",s/=u,b.style(i.elem,e,s+r);while(u!==(u=i.cur()/a)&&1!==u&&--l)}i.unit=r,i.start=s,i.end=o[1]?s+(o[1]+1)*n:n}return i}]};function Kn(){return setTimeout(function(){Xn=t}),Xn=b.now()}function Zn(e,t){b.each(t,function(t,n){var r=(Qn[t]||[]).concat(Qn["*"]),i=0,o=r.length;for(;o>i;i++)if(r[i].call(e,t,n))return})}function er(e,t,n){var r,i,o=0,a=Gn.length,s=b.Deferred().always(function(){delete u.elem}),u=function(){if(i)return!1;var t=Xn||Kn(),n=Math.max(0,l.startTime+l.duration-t),r=n/l.duration||0,o=1-r,a=0,u=l.tweens.length;for(;u>a;a++)l.tweens[a].run(o);return s.notifyWith(e,[l,o,n]),1>o&&u?n:(s.resolveWith(e,[l]),!1)},l=s.promise(!
{elem:e,props:b.extend({},t),opts:b.extend(!0,{specialEasing:{}},n),ori!
ginalProperties:t,originalOptions:n,startTime:Xn||Kn(),duration:n.duration,tweens:[],createTween:function(t,n){var r=b.Tween(e,l.opts,t,n,l.opts.specialEasing[t]||l.opts.easing);return l.tweens.push(r),r},stop:function(t){var n=0,r=t?l.tweens.length:0;if(i)return this;for(i=!0;r>n;n++)l.tweens[n].run(1);return t?s.resolveWith(e,[l,t]):s.rejectWith(e,[l,t]),this}}),c=l.props;for(tr(c,l.opts.specialEasing);a>o;o++)if(r=Gn[o].call(l,e,c,l.opts))return r;return Zn(l,c),b.isFunction(l.opts.start)&&l.opts.start.call(e,l),b.fx.timer(b.extend(u,{elem:e,anim:l,queue:l.opts.queue})),l.progress(l.opts.progress).done(l.opts.done,l.opts.complete).fail(l.opts.fail).always(l.opts.always)}function tr(e,t){var n,r,i,o,a;for(i in e)if(r=b.camelCase(i),o=t[r],n=e[i],b.isArray(n)&&(o=n[1],n=e[i]=n[0]),i!==r&&(e[r]=n,delete e[i]),a=b.cssHooks[r],a&&"expand"in a){n=a.expand(n),delete e[r];for(i in n)i in e||(e[i]=n[i],t[i]=o)}else t[r]=o}b.Animation=b.extend(er,{tweener:function(e,t){b.isFunctio!
n(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;i>r;r++)n=e[r],Qn[n]=Qn[n]||[],Qn[n].unshift(t)},prefilter:function(e,t){t?Gn.unshift(e):Gn.push(e)}});function nr(e,t,n){var r,i,o,a,s,u,l,c,p,f=this,d=e.style,h={},g=[],m=e.nodeType&&nn(e);n.queue||(c=b._queueHooks(e,"fx"),null==c.unqueued&&(c.unqueued=0,p=c.empty.fire,c.empty.fire=function(){c.unqueued||p()}),c.unqueued++,f.always(function(){f.always(function(){c.unqueued--,b.queue(e,"fx").length||c.empty.fire()})})),1===e.nodeType&&("height"in t||"width"in t)&&(n.overflow=[d.overflow,d.overflowX,d.overflowY],"inline"===b.css(e,"display")&&"none"===b.css(e,"float")&&(b.support.inlineBlockNeedsLayout&&"inline"!==un(e.nodeName)?d.zoom=1:d.display="inline-block")),n.overflow&&(d.overflow="hidden",b.support.shrinkWrapBlocks||f.always(function(){d.overflow=n.overflow[0],d.overflowX=n.overflow[1],d.overflowY=n.overflow[2]}));for(i in t)if(a=t[i],Vn.exec(a)){if(delete t[i],u=u||"toggle"===a,a===(m?"hide":"show"))contin!
ue;g.push(i)}if(o=g.length){s=b._data(e,"fxshow")||b._data(e,"fxshow",{!
}),"hidden"in s&&(m=s.hidden),u&&(s.hidden=!m),m?b(e).show():f.done(function(){b(e).hide()}),f.done(function(){var t;b._removeData(e,"fxshow");for(t in h)b.style(e,t,h[t])});for(i=0;o>i;i++)r=g[i],l=f.createTween(r,m?s[r]:0),h[r]=s[r]||b.style(e,r),r in s||(s[r]=l.start,m&&(l.end=l.start,l.start="width"===r||"height"===r?1:0))}}function rr(e,t,n,r,i){return new rr.prototype.init(e,t,n,r,i)}b.Tween=rr,rr.prototype={constructor:rr,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||"swing",this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(b.cssNumber[n]?"":"px")},cur:function(){var e=rr.propHooks[this.prop];return e&&e.get?e.get(this):rr.propHooks._default.get(this)},run:function(e){var t,n=rr.propHooks[this.prop];return this.pos=t=this.options.duration?b.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&!
n.set?n.set(this):rr.propHooks._default.set(this),this}},rr.prototype.init.prototype=rr.prototype,rr.propHooks={_default:{get:function(e){var t;return null==e.elem[e.prop]||e.elem.style&&null!=e.elem.style[e.prop]?(t=b.css(e.elem,e.prop,""),t&&"auto"!==t?t:0):e.elem[e.prop]},set:function(e){b.fx.step[e.prop]?b.fx.step[e.prop](e):e.elem.style&&(null!=e.elem.style[b.cssProps[e.prop]]||b.cssHooks[e.prop])?b.style(e.elem,e.prop,e.now+e.unit):e.elem[e.prop]=e.now}}},rr.propHooks.scrollTop=rr.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},b.each(["toggle","show","hide"],function(e,t){var n=b.fn[t];b.fn[t]=function(e,r,i){return null==e||"boolean"==typeof e?n.apply(this,arguments):this.animate(ir(t,!0),e,r,i)}}),b.fn.extend({fadeTo:function(e,t,n,r){return this.filter(nn).css("opacity",0).show().end().animate({opacity:t},e,n,r)},animate:function(e,t,n,r){var i=b.isEmptyObject(e),o=b.speed(t,n,r),a=function(){var t=er(this,b.exten!
d({},e),o);a.finish=function(){t.stop(!0)},(i||b._data(this,"finish"))&!
&t.stop(!0)};return a.finish=a,i||o.queue===!1?this.each(a):this.queue(o.queue,a)},stop:function(e,n,r){var i=function(e){var t=e.stop;delete e.stop,t(r)};return"string"!=typeof e&&(r=n,n=e,e=t),n&&e!==!1&&this.queue(e||"fx",[]),this.each(function(){var t=!0,n=null!=e&&e+"queueHooks",o=b.timers,a=b._data(this);if(n)a[n]&&a[n].stop&&i(a[n]);else for(n in a)a[n]&&a[n].stop&&Jn.test(n)&&i(a[n]);for(n=o.length;n--;)o[n].elem!==this||null!=e&&o[n].queue!==e||(o[n].anim.stop(r),t=!1,o.splice(n,1));(t||!r)&&b.dequeue(this,e)})},finish:function(e){return e!==!1&&(e=e||"fx"),this.each(function(){var t,n=b._data(this),r=n[e+"queue"],i=n[e+"queueHooks"],o=b.timers,a=r?r.length:0;for(n.finish=!0,b.queue(this,e,[]),i&&i.cur&&i.cur.finish&&i.cur.finish.call(this),t=o.length;t--;)o[t].elem===this&&o[t].queue===e&&(o[t].anim.stop(!0),o.splice(t,1));for(t=0;a>t;t++)r[t]&&r[t].finish&&r[t].finish.call(this);delete n.finish})}});function ir(e,t){var n,r={height:e},i=0;for(t=t?1:0;4>i;i+=2-t)n!
=Zt[i],r["margin"+n]=r["padding"+n]=e;return t&&(r.opacity=r.width=e),r}b.each({slideDown:ir("show"),slideUp:ir("hide"),slideToggle:ir("toggle"),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(e,t){b.fn[e]=function(e,n,r){return this.animate(t,e,n,r)}}),b.speed=function(e,t,n){var r=e&&"object"==typeof e?b.extend({},e):{complete:n||!n&&t||b.isFunction(e)&&e,duration:e,easing:n&&t||t&&!b.isFunction(t)&&t};return r.duration=b.fx.off?0:"number"==typeof r.duration?r.duration:r.duration in b.fx.speeds?b.fx.speeds[r.duration]:b.fx.speeds._default,(null==r.queue||r.queue===!0)&&(r.queue="fx"),r.old=r.complete,r.complete=function(){b.isFunction(r.old)&&r.old.call(this),r.queue&&b.dequeue(this,r.queue)},r},b.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2}},b.timers=[],b.fx=rr.prototype.init,b.fx.tick=function(){var e,n=b.timers,r=0;for(Xn=b.now();n.length>r;r++)e=n[r],e()||n[r]!==e||n.splice(r--,1);n.len!
gth||b.fx.stop(),Xn=t},b.fx.timer=function(e){e()&&b.timers.push(e)&&b.!
fx.start()},b.fx.interval=13,b.fx.start=function(){Un||(Un=setInterval(b.fx.tick,b.fx.interval))},b.fx.stop=function(){clearInterval(Un),Un=null},b.fx.speeds={slow:600,fast:200,_default:400},b.fx.step={},b.expr&&b.expr.filters&&(b.expr.filters.animated=function(e){return b.grep(b.timers,function(t){return e===t.elem}).length}),b.fn.offset=function(e){if(arguments.length)return e===t?this:this.each(function(t){b.offset.setOffset(this,e,t)});var n,r,o={top:0,left:0},a=this[0],s=a&&a.ownerDocument;if(s)return n=s.documentElement,b.contains(n,a)?(typeof a.getBoundingClientRect!==i&&(o=a.getBoundingClientRect()),r=or(s),{top:o.top+(r.pageYOffset||n.scrollTop)-(n.clientTop||0),left:o.left+(r.pageXOffset||n.scrollLeft)-(n.clientLeft||0)}):o},b.offset={setOffset:function(e,t,n){var r=b.css(e,"position");"static"===r&&(e.style.position="relative");var i=b(e),o=i.offset(),a=b.css(e,"top"),s=b.css(e,"left"),u=("absolute"===r||"fixed"===r)&&b.inArray("auto",[a,s])>-1,l={},c={},p,f;u?(c!
=i.position(),p=c.top,f=c.left):(p=parseFloat(a)||0,f=parseFloat(s)||0),b.isFunction(t)&&(t=t.call(e,n,o)),null!=t.top&&(l.top=t.top-o.top+p),null!=t.left&&(l.left=t.left-o.left+f),"using"in t?t.using.call(e,l):i.css(l)}},b.fn.extend({position:function(){if(this[0]){var e,t,n={top:0,left:0},r=this[0];return"fixed"===b.css(r,"position")?t=r.getBoundingClientRect():(e=this.offsetParent(),t=this.offset(),b.nodeName(e[0],"html")||(n=e.offset()),n.top+=b.css(e[0],"borderTopWidth",!0),n.left+=b.css(e[0],"borderLeftWidth",!0)),{top:t.top-n.top-b.css(r,"marginTop",!0),left:t.left-n.left-b.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||o.documentElement;while(e&&!b.nodeName(e,"html")&&"static"===b.css(e,"position"))e=e.offsetParent;return e||o.documentElement})}}),b.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);b.fn[e]=function(i){return b.access(this,function(e,i,o){var a=or(e);return o!
===t?a?n in a?a[n]:a.document.documentElement[i]:e[i]:(a?a.scrollTo(r?b!
(a).scrollLeft():o,r?o:b(a).scrollTop()):e[i]=o,t)},e,i,arguments.length,null)}});function or(e){return b.isWindow(e)?e:9===e.nodeType?e.defaultView||e.parentWindow:!1}b.each({Height:"height",Width:"width"},function(e,n){b.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){b.fn[i]=function(i,o){var a=arguments.length&&(r||"boolean"!=typeof i),s=r||(i===!0||o===!0?"margin":"border");return b.access(this,function(n,r,i){var o;return b.isWindow(n)?n.document.documentElement["client"+e]:9===n.nodeType?(o=n.documentElement,Math.max(n.body["scroll"+e],o["scroll"+e],n.body["offset"+e],o["offset"+e],o["client"+e])):i===t?b.css(n,r,s):b.style(n,r,i,s)},n,a?i:t,a,null)}})}),e.jQuery=e.$=b,"function"==typeof define&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return b})})(window);
\ No newline at end of file
--
1.9.3
1
0
3
6
Hi all,
v10:
Adapted to RHEL 6. RHEL 6 does not provide iommu information in sysfs.
The old patch series would report wrong affected passthrough devices
list.
The new patch series would report empty affected passthrough devices
list in this case. In future release we'll develop functions to gather
this information ourselves.
v9:
Update API.md, API.json, unit tests and MockModel. Investigated
distro compatibility problems, adapted to Fedora 20, Fedora 19 and
Ubuntu 14.04.
v8:
Change the URI for listing affected passthrough devices, suggested by
Aline. Move hostdev.py to src/kimchi/model.py. Still discussing API
with the front-end developer. Once the API accepted, The author should
add tests for model and mockmodel, and change API.md and API.json
accordingly.
v7:
Some minor coding style improvements.
v6:
Do not passthrough PCI device of class code 0x07. It might contains
system device not suitable to assign to guest.
v5:
Filter ealigible pci devices according to pci class. When assigning a
device to VM, check if there are other VMs holding it. Use
"kimchi.model.utils.get_vm_config_flag()" to correctly set the device
attaching API flag.
v4:
Add new sub-collection to host device to list the VMs holding the device.
v3:
Fix a small naming error introduced by rebase.
v2:
Handle the devices in VM's sub-collection "hostdevs".
v1:
Handle the devices in VM template.
This patch series is to enable Kimchi to assign hos devices directly to
a VM, thus greately improve VM performance. Currently we support assigning
PCI device, USB device and SCSI LUN. For example, we can assign an NIC
to VM to improve guest network throughput, or passthrough a USB camera
to enable the guest OS to record video.
Host devices form a tree. We can assign most of the devices in the tree
to VM. By assigning a device, all the devices in its sub-tree are also
assigned. It might not make sense to assign a USB controller, because
the host may be using one of the devices connected to the controller.
Instead, Kimchi just presents the "leaf" devices to assign to guest.
In recent Linux kernel and KVM, it is able to recognize the IOMMU group
of a PCI device. The "leaf" PCI devices in the same IOMMU group should
be assigned and dismissed together. The IOMMU group is the actual
smallest isolation granularity of the PCI devices.
The first patch is to list all host devices information. It's useful on
its own to show host devices information.
The second patch is to list all eligible host devices to assign, as well
as the "affected" devices in the same IOMMU group.
The third patch creates a sub-collection "hostdevs" to the VM resource,
and deals with assigning and dismissing devices.
The fourth patch adds a sub-collection "vm_holders" to the host device
resource. It's to list all VMs that are holding the device.
Zhou Zheng Sheng (5):
Host device passthrough: List all types of host devices
Host device passthrough: List eligible device to passthrough
Host device passthrough: Directly assign and dissmis host device from
VM
Host device passthrough: List VMs that are holding a host device
Host device passthrough: Add unit tests and documents
docs/API.md | 66 ++++++-
src/kimchi/API.json | 31 +++
src/kimchi/control/host.py | 7 +
src/kimchi/control/vm/hostdevs.py | 44 +++++
src/kimchi/featuretests.py | 10 +-
src/kimchi/i18n.py | 10 +
src/kimchi/mockmodel.py | 127 ++++++++++++-
src/kimchi/model/config.py | 2 +
src/kimchi/model/host.py | 39 ++--
src/kimchi/model/hostdev.py | 338 +++++++++++++++++++++++++++++++++
src/kimchi/model/libvirtstoragepool.py | 18 +-
src/kimchi/model/vmhostdevs.py | 324 +++++++++++++++++++++++++++++++
src/kimchi/rollbackcontext.py | 3 +
src/kimchi/xmlutils.py | 26 ++-
tests/test_model.py | 31 +++
tests/test_rest.py | 12 +-
tests/test_storagepool.py | 7 +-
17 files changed, 1043 insertions(+), 52 deletions(-)
create mode 100644 src/kimchi/control/vm/hostdevs.py
create mode 100644 src/kimchi/model/hostdev.py
create mode 100644 src/kimchi/model/vmhostdevs.py
--
1.9.3
1
5
Hi all,
v9:
Update API.md, API.json, unit tests and MockModel. Investigated
distro compatibility problems, adapted to Fedora 20, Fedora 19 and
Ubuntu 14.04.
v8:
Change the URI for listing affected passthrough devices, suggested by
Aline. Move hostdev.py to src/kimchi/model.py. Still discussing API
with the front-end developer. Once the API accepted, The author should
add tests for model and mockmodel, and change API.md and API.json
accordingly.
v7:
Some minor coding style improvements.
v6:
Do not passthrough PCI device of class code 0x07. It might contains
system device not suitable to assign to guest.
v5:
Filter ealigible pci devices according to pci class. When assigning a
device to VM, check if there are other VMs holding it. Use
"kimchi.model.utils.get_vm_config_flag()" to correctly set the device
attaching API flag.
v4:
Add new sub-collection to host device to list the VMs holding the device.
v3:
Fix a small naming error introduced by rebase.
v2:
Handle the devices in VM's sub-collection "hostdevs".
v1:
Handle the devices in VM template.
This patch series is to enable Kimchi to assign hos devices directly to
a VM, thus greately improve VM performance. Currently we support assigning
PCI device, USB device and SCSI LUN. For example, we can assign an NIC
to VM to improve guest network throughput, or passthrough a USB camera
to enable the guest OS to record video.
Host devices form a tree. We can assign most of the devices in the tree
to VM. By assigning a device, all the devices in its sub-tree are also
assigned. It might not make sense to assign a USB controller, because
the host may be using one of the devices connected to the controller.
Instead, Kimchi just presents the "leaf" devices to assign to guest.
In recent Linux kernel and KVM, it is able to recognize the IOMMU group
of a PCI device. The "leaf" PCI devices in the same IOMMU group should
be assigned and dismissed together. The IOMMU group is the actual
smallest isolation granularity of the PCI devices.
The first patch is to list all host devices information. It's useful on
its own to show host devices information.
The second patch is to list all eligible host devices to assign, as well
as the "affected" devices in the same IOMMU group.
The third patch creates a sub-collection "hostdevs" to the VM resource,
and deals with assigning and dismissing devices.
The fourth patch adds a sub-collection "vm_holders" to the host device
resource. It's to list all VMs that are holding the device.
Zhou Zheng Sheng (5):
Host device passthrough: List all types of host devices
Host device passthrough: List eligible device to passthrough
Host device passthrough: Directly assign and dissmis host device from
VM
Host device passthrough: List VMs that are holding a host device
Host device passthrough: Add unit tests and documents
docs/API.md | 66 ++++++-
src/kimchi/API.json | 31 +++
src/kimchi/control/host.py | 7 +
src/kimchi/control/vm/hostdevs.py | 44 +++++
src/kimchi/featuretests.py | 10 +-
src/kimchi/i18n.py | 10 +
src/kimchi/mockmodel.py | 127 ++++++++++++-
src/kimchi/model/config.py | 2 +
src/kimchi/model/host.py | 39 ++--
src/kimchi/model/hostdev.py | 337 +++++++++++++++++++++++++++++++++
src/kimchi/model/libvirtstoragepool.py | 18 +-
src/kimchi/model/vmhostdevs.py | 324 +++++++++++++++++++++++++++++++
src/kimchi/rollbackcontext.py | 3 +
src/kimchi/xmlutils.py | 26 ++-
tests/test_model.py | 31 +++
tests/test_rest.py | 12 +-
tests/test_storagepool.py | 7 +-
17 files changed, 1042 insertions(+), 52 deletions(-)
create mode 100644 src/kimchi/control/vm/hostdevs.py
create mode 100644 src/kimchi/model/hostdev.py
create mode 100644 src/kimchi/model/vmhostdevs.py
--
1.9.3
2
7