Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
emulab
emulab-devel
Commits
e0c9477d
Commit
e0c9477d
authored
Feb 27, 2017
by
Leigh B Stoller
Browse files
Web UI part of image deletion.
parent
d2414cce
Changes
17
Hide whitespace changes
Inline
Side-by-side
www/aptui/css/tablesorter-widget-grouping.css
0 → 100644
View file @
e0c9477d
/* Grouping widget css */
tr
.group-header
td
{
background
:
#eee
;
}
.group-name
{
text-transform
:
uppercase
;
font-weight
:
bold
;
}
.group-count
{
color
:
#999
;
}
.group-hidden
{
display
:
none
!important
;
}
.group-header
,
.group-header
td
{
user-select
:
none
;
-moz-user-select
:
none
;
}
/* collapsed arrow */
tr
.group-header
td
i
{
display
:
inline-block
;
width
:
0
;
height
:
0
;
border-top
:
4px
solid
transparent
;
border-bottom
:
4px
solid
#888
;
border-right
:
4px
solid
#888
;
border-left
:
4px
solid
transparent
;
margin-right
:
7px
;
user-select
:
none
;
-moz-user-select
:
none
;
}
tr
.group-header.collapsed
td
i
{
border-top
:
5px
solid
transparent
;
border-bottom
:
5px
solid
transparent
;
border-left
:
5px
solid
#888
;
border-right
:
0
;
margin-right
:
10px
;
}
www/aptui/images.ajax
0 → 100644
View file @
e0c9477d
<?php
#
# Copyright (c) 2000-2017 University of Utah and the Flux Group.
#
# {{{EMULAB-LICENSE
#
# This file is part of the Emulab network testbed software.
#
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
#
# This file 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 Affero General Public
# License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this file. If not, see <http://www.gnu.org/licenses/>.
#
# }}}
#
chdir
(
".."
);
include_once
(
"webtask.php"
);
chdir
(
"apt"
);
# We set this in CheckPageArgs
$target_user
=
null
;
#
# Need to check the permission, since we allow admins to mess with
# other accounts.
#
function
CheckPageArgs
()
{
global
$this_user
,
$target_user
;
global
$ajax_args
;
global
$TB_USERINFO_READINFO
;
if
(
!
isset
(
$ajax_args
[
"uid"
]))
{
SPITAJAX_ERROR
(
-
1
,
"Missing target uid"
);
return
-
1
;
}
$uid
=
$ajax_args
[
"uid"
];
if
(
!
TBvalid_uid
(
$uid
))
{
SPITAJAX_ERROR
(
-
1
,
"Invalid target uid"
);
return
-
1
;
}
$target_user
=
User
::
Lookup
(
$uid
);
if
(
!
$target_user
)
{
sleep
(
2
);
SPITAJAX_ERROR
(
-
1
,
"Unknown target uid"
);
return
-
1
;
}
if
(
$uid
==
$this_user
->
uid
())
return
0
;
if
(
!
ISADMIN
()
&&
!
ISFOREIGN_ADMIN
()
&&
!
$target_user
->
AccessCheck
(
$this_user
,
$TB_USERINFO_READINFO
))
{
SPITAJAX_ERROR
(
-
1
,
"Not enough permission"
);
return
-
1
;
}
return
0
;
}
#
# List images at a cluster (for a user).
#
function
Do_ListImages
()
{
global
$this_user
,
$target_user
;
global
$ajax_args
;
global
$TB_PROJECT_CREATEEXPT
,
$suexec_output
;
if
(
CheckPageArgs
())
{
return
;
}
if
(
!
isset
(
$ajax_args
[
"cluster"
]))
{
SPITAJAX_ERROR
(
-
1
,
"Missing cluster"
);
return
;
}
if
(
!
preg_match
(
"/^[-\w]+$/"
,
$ajax_args
[
"cluster"
]))
{
SPITAJAX_ERROR
(
-
1
,
"Invalid cluster name"
);
return
;
}
$aggregate
=
Aggregate
::
LookupByNickname
(
$ajax_args
[
"cluster"
]);
if
(
!
$aggregate
)
{
SPITAJAX_ERROR
(
-
1
,
"No such cluster"
);
return
;
}
$uid
=
$target_user
->
uid
();
$urn
=
$aggregate
->
urn
();
$webtask
=
WebTask
::
CreateAnonymous
();
$webtask_id
=
$webtask
->
task_id
();
$retval
=
SUEXEC
(
$uid
,
"nobody"
,
"webmanage_images -t
$webtask_id
list -a '
$urn
'"
,
SUEXEC_ACTION_CONTINUE
);
if
(
$retval
)
{
$webtask
->
Delete
();
SPITAJAX_ERROR
(
-
1
,
$suexec_output
);
return
;
}
$webtask
->
Refresh
();
$images
=
$webtask
->
TaskValue
(
"value"
);
$webtask
->
Delete
();
SPITAJAX_RESPONSE
(
$images
);
}
#
# Delete image at a cluster (for a user).
#
function
Do_DeleteImage
()
{
global
$this_user
,
$target_user
;
global
$ajax_args
;
global
$suexec_output
;
$pdarg
=
""
;
if
(
CheckPageArgs
())
{
return
;
}
if
(
!
isset
(
$ajax_args
[
"urn"
])
||
$ajax_args
[
"urn"
]
==
""
)
{
SPITAJAX_ERROR
(
-
1
,
"Missing image urn"
);
return
;
}
$image_urn
=
escapeshellarg
(
$ajax_args
[
"urn"
]);
if
(
!
isset
(
$ajax_args
[
"cluster"
]))
{
SPITAJAX_ERROR
(
-
1
,
"Missing cluster"
);
return
;
}
if
(
!
preg_match
(
"/^[-\w]+$/"
,
$ajax_args
[
"cluster"
]))
{
SPITAJAX_ERROR
(
-
1
,
"Invalid cluster name"
);
return
;
}
$aggregate
=
Aggregate
::
LookupByNickname
(
$ajax_args
[
"cluster"
]);
if
(
!
$aggregate
)
{
SPITAJAX_ERROR
(
-
1
,
"No such cluster"
);
return
;
}
if
(
isset
(
$ajax_args
[
"profile-delete"
])
&&
$ajax_args
[
"profile-delete"
]
!=
""
)
{
if
(
!
preg_match
(
"/^[-\w]+$/"
,
$ajax_args
[
"profile-delete"
]))
{
SPITAJAX_ERROR
(
-
1
,
"Invalid profile uuid for deletion"
);
return
;
}
$pdarg
=
"-d "
.
escapeshellarg
(
$ajax_args
[
"profile-delete"
]);
}
$uid
=
$target_user
->
uid
();
$aggurn
=
$aggregate
->
urn
();
$webtask
=
WebTask
::
CreateAnonymous
();
$webtask_id
=
$webtask
->
task_id
();
$retval
=
SUEXEC
(
$uid
,
"nobody"
,
"webmanage_images -t
$webtask_id
"
.
" delete -a '
$aggurn
' -n
$pdarg
$image_urn
"
,
SUEXEC_ACTION_CONTINUE
);
if
(
$retval
)
{
$webtask
->
Delete
();
SPITAJAX_ERROR
(
-
1
,
$suexec_output
);
return
;
}
$webtask
->
Delete
();
SPITAJAX_RESPONSE
(
0
);
}
# Local Variables:
# mode:php
# End:
?>
www/aptui/js/lib/jquery.tablesorter.widget-grouping.js
0 → 100644
View file @
e0c9477d
/*! Widget: grouping - updated 3/5/2015 (v2.21.0) *//*
* Requires tablesorter v2.8+ and jQuery 1.7+
* by Rob Garrison
*/
/*jshint browser:true, jquery:true, unused:false */
/*global jQuery: false */
;(
function
(
$
){
"
use strict
"
;
var
ts
=
$
.
tablesorter
;
ts
.
grouping
=
{
types
:
{
number
:
function
(
c
,
$column
,
txt
,
num
,
group
){
var
value
,
word
;
if
(
num
>
1
&&
txt
!==
''
)
{
if
(
$column
.
hasClass
(
ts
.
css
.
sortAsc
))
{
value
=
Math
.
floor
(
parseFloat
(
txt
)
/
num
)
*
num
;
return
value
>
parseFloat
(
group
||
0
)
?
value
:
parseFloat
(
group
||
0
);
}
else
{
value
=
Math
.
ceil
(
parseFloat
(
txt
)
/
num
)
*
num
;
return
value
<
parseFloat
(
group
||
num
)
-
value
?
parseFloat
(
group
||
num
)
-
value
:
value
;
}
}
else
{
word
=
(
txt
+
''
).
match
(
/
\d
+/g
);
return
word
&&
word
.
length
>=
num
?
word
[
num
-
1
]
:
txt
||
''
;
}
},
separator
:
function
(
c
,
$column
,
txt
,
num
){
var
word
=
(
txt
+
''
).
split
(
c
.
widgetOptions
.
group_separator
);
return
$
.
trim
(
word
&&
num
>
0
&&
word
.
length
>=
num
?
word
[(
num
||
1
)
-
1
]
:
''
);
},
word
:
function
(
c
,
$column
,
txt
,
num
){
var
word
=
(
txt
+
'
'
).
match
(
/
\w
+/g
);
return
word
&&
word
.
length
>=
num
?
word
[
num
-
1
]
:
txt
||
''
;
},
letter
:
function
(
c
,
$column
,
txt
,
num
){
return
txt
?
(
txt
+
'
'
).
substring
(
0
,
num
)
:
''
;
},
date
:
function
(
c
,
$column
,
txt
,
part
,
group
){
var
wo
=
c
.
widgetOptions
,
time
=
new
Date
(
txt
||
''
),
hours
=
time
.
getHours
();
return
part
===
'
year
'
?
time
.
getFullYear
()
:
part
===
'
month
'
?
wo
.
group_months
[
time
.
getMonth
()]
:
part
===
'
monthyear
'
?
wo
.
group_months
[
time
.
getMonth
()]
+
'
'
+
time
.
getFullYear
()
:
part
===
'
day
'
?
wo
.
group_months
[
time
.
getMonth
()]
+
'
'
+
time
.
getDate
()
:
part
===
'
week
'
?
wo
.
group_week
[
time
.
getDay
()]
:
part
===
'
time
'
?
(
'
00
'
+
(
hours
>
12
?
hours
-
12
:
hours
===
0
?
hours
+
12
:
hours
)).
slice
(
-
2
)
+
'
:
'
+
(
'
00
'
+
time
.
getMinutes
()).
slice
(
-
2
)
+
'
'
+
(
'
00
'
+
wo
.
group_time
[
hours
>=
12
?
1
:
0
]).
slice
(
-
2
)
:
wo
.
group_dateString
(
time
);
}
},
update
:
function
(
table
,
c
,
wo
){
if
(
$
.
isEmptyObject
(
c
.
cache
))
{
return
;
}
var
rowIndex
,
tbodyIndex
,
currentGroup
,
$rows
,
groupClass
,
grouping
,
norm_rows
,
saveName
,
direction
,
lang
=
wo
.
grouping_language
,
group
=
''
,
savedGroup
=
false
,
column
=
c
.
sortList
[
0
]
?
c
.
sortList
[
0
][
0
]
:
-
1
;
c
.
$table
.
find
(
'
tr.group-hidden
'
).
removeClass
(
'
group-hidden
'
).
end
()
.
find
(
'
tr.group-header
'
).
remove
();
if
(
wo
.
group_collapsible
)
{
// clear pager saved spacer height (in case the rows are collapsed)
c
.
$table
.
data
(
'
pagerSavedHeight
'
,
0
);
}
if
(
column
>=
0
&&
!
c
.
$headerIndexed
[
column
].
hasClass
(
'
group-false
'
))
{
wo
.
group_currentGroup
=
''
;
// save current groups
wo
.
group_currentGroups
=
{};
// group class finds "group-{word/separator/letter/number/date/false}-{optional:#/year/month/day/week/time}"
groupClass
=
(
c
.
$headerIndexed
[
column
].
attr
(
'
class
'
)
||
''
).
match
(
/
(
group-
\w
+
(
-
\w
+
)?)
/g
);
// grouping = [ 'group', '{word/separator/letter/number/date/false}', '{#/year/month/day/week/time}' ]
grouping
=
groupClass
?
groupClass
[
0
].
split
(
'
-
'
)
:
[
'
group
'
,
'
letter
'
,
1
];
// default to letter 1
// save current grouping
if
(
wo
.
group_collapsible
&&
wo
.
group_saveGroups
&&
ts
.
storage
)
{
wo
.
group_currentGroups
=
ts
.
storage
(
table
,
'
tablesorter-groups
'
)
||
{};
// include direction when grouping numbers > 1 (reversed direction shows different range values)
direction
=
(
grouping
[
1
]
===
'
number
'
&&
grouping
[
2
]
>
1
)
?
'
dir
'
+
c
.
sortList
[
0
][
1
]
:
''
;
// combine column, sort direction & grouping as save key
saveName
=
wo
.
group_currentGroup
=
''
+
column
+
direction
+
grouping
.
join
(
''
);
if
(
!
wo
.
group_currentGroups
[
saveName
])
{
wo
.
group_currentGroups
[
saveName
]
=
[];
}
else
{
savedGroup
=
true
;
}
}
for
(
tbodyIndex
=
0
;
tbodyIndex
<
c
.
$tbodies
.
length
;
tbodyIndex
++
)
{
norm_rows
=
c
.
cache
[
tbodyIndex
].
normalized
;
group
=
''
;
// clear grouping across tbodies
$rows
=
c
.
$tbodies
.
eq
(
tbodyIndex
).
children
(
'
tr
'
).
not
(
'
.
'
+
c
.
cssChildRow
);
for
(
rowIndex
=
0
;
rowIndex
<
$rows
.
length
;
rowIndex
++
)
{
if
(
$rows
.
eq
(
rowIndex
).
is
(
'
:visible
'
)
)
{
// fixes #438
if
(
ts
.
grouping
.
types
[
grouping
[
1
]])
{
currentGroup
=
norm_rows
[
rowIndex
]
?
ts
.
grouping
.
types
[
grouping
[
1
]](
c
,
c
.
$headerIndexed
[
column
],
norm_rows
[
rowIndex
][
column
],
/date/
.
test
(
groupClass
)
?
grouping
[
2
]
:
parseInt
(
grouping
[
2
]
||
1
,
10
)
||
1
,
group
,
lang
)
:
currentGroup
;
if
(
group
!==
currentGroup
)
{
group
=
currentGroup
;
// show range if number > 1
if
(
grouping
[
1
]
===
'
number
'
&&
grouping
[
2
]
>
1
&&
currentGroup
!==
''
)
{
currentGroup
+=
'
-
'
+
(
parseInt
(
currentGroup
,
10
)
+
((
parseInt
(
grouping
[
2
],
10
)
-
1
)
*
(
c
.
$headerIndexed
[
column
].
hasClass
(
ts
.
css
.
sortAsc
)
?
1
:
-
1
)));
}
if
(
$
.
isFunction
(
wo
.
group_formatter
))
{
currentGroup
=
wo
.
group_formatter
((
currentGroup
||
''
).
toString
(),
column
,
table
,
c
,
wo
)
||
currentGroup
;
}
$rows
.
eq
(
rowIndex
).
before
(
'
<tr class="group-header
'
+
c
.
selectorRemove
.
slice
(
1
)
+
'
" unselectable="on"
'
+
(
c
.
tabIndex
?
'
tabindex="0"
'
:
''
)
+
'
><td colspan="
'
+
c
.
columns
+
'
">
'
+
(
wo
.
group_collapsible
?
'
<i/>
'
:
''
)
+
'
<span class="group-name">
'
+
currentGroup
+
'
</span><span class="group-count"></span></td></tr>
'
);
if
(
wo
.
group_saveGroups
&&
!
savedGroup
&&
wo
.
group_collapsed
&&
wo
.
group_collapsible
)
{
// all groups start collapsed
wo
.
group_currentGroups
[
wo
.
group_currentGroup
].
push
(
currentGroup
);
}
}
}
}
}
}
c
.
$table
.
find
(
'
tr.group-header
'
)
.
bind
(
'
selectstart
'
,
false
)
.
each
(
function
(){
var
isHidden
,
$label
,
name
,
$row
=
$
(
this
),
$rows
=
$row
.
nextUntil
(
'
tr.group-header
'
).
filter
(
'
:visible
'
);
if
(
wo
.
group_count
||
$
.
isFunction
(
wo
.
group_callback
))
{
$label
=
$row
.
find
(
'
.group-count
'
);
if
(
$label
.
length
)
{
if
(
wo
.
group_count
)
{
$label
.
html
(
wo
.
group_count
.
replace
(
/
\{
num
\}
/g
,
$rows
.
length
)
);
}
if
(
$
.
isFunction
(
wo
.
group_callback
))
{
wo
.
group_callback
(
$row
.
find
(
'
td
'
),
$rows
,
column
,
table
);
}
}
}
if
(
wo
.
group_saveGroups
&&
wo
.
group_currentGroups
.
length
&&
wo
.
group_currentGroups
[
wo
.
group_currentGroup
].
length
)
{
name
=
$row
.
find
(
'
.group-name
'
).
text
().
toLowerCase
();
isHidden
=
$
.
inArray
(
name
,
wo
.
group_currentGroups
[
wo
.
group_currentGroup
]
)
>
-
1
;
$row
.
toggleClass
(
'
collapsed
'
,
isHidden
);
$rows
.
toggleClass
(
'
group-hidden
'
,
isHidden
);
}
else
if
(
wo
.
group_collapsed
&&
wo
.
group_collapsible
)
{
$row
.
addClass
(
'
collapsed
'
);
$rows
.
addClass
(
'
group-hidden
'
);
}
});
c
.
$table
.
trigger
(
wo
.
group_complete
);
}
},
bindEvents
:
function
(
table
,
c
,
wo
){
if
(
wo
.
group_collapsible
)
{
wo
.
group_currentGroups
=
[];
// .on() requires jQuery 1.7+
c
.
$table
.
on
(
'
click toggleGroup keyup
'
,
'
tr.group-header
'
,
function
(
event
){
event
.
stopPropagation
();
// pressing enter will toggle the group
if
(
event
.
type
===
'
keyup
'
&&
event
.
which
!==
13
)
{
return
;
}
var
isCollapsed
,
$groups
,
indx
,
$this
=
$
(
this
),
name
=
$this
.
find
(
'
.group-name
'
).
text
().
toLowerCase
();
// use shift-click to toggle ALL groups
if
(
event
.
shiftKey
&&
(
event
.
type
===
'
click
'
||
event
.
type
===
'
keyup
'
))
{
$this
.
siblings
(
'
.group-header
'
).
trigger
(
'
toggleGroup
'
);
}
$this
.
toggleClass
(
'
collapsed
'
);
// nextUntil requires jQuery 1.4+
$this
.
nextUntil
(
'
tr.group-header
'
).
toggleClass
(
'
group-hidden
'
,
$this
.
hasClass
(
'
collapsed
'
)
);
// save collapsed groups
if
(
wo
.
group_saveGroups
&&
ts
.
storage
)
{
$groups
=
c
.
$table
.
find
(
'
.group-header
'
);
isCollapsed
=
$this
.
hasClass
(
'
collapsed
'
);
if
(
!
wo
.
group_currentGroups
[
wo
.
group_currentGroup
])
{
wo
.
group_currentGroups
[
wo
.
group_currentGroup
]
=
[];
}
if
(
isCollapsed
&&
wo
.
group_currentGroup
)
{
wo
.
group_currentGroups
[
wo
.
group_currentGroup
].
push
(
name
);
}
else
if
(
wo
.
group_currentGroup
)
{
indx
=
$
.
inArray
(
name
,
wo
.
group_currentGroups
[
wo
.
group_currentGroup
]
);
if
(
indx
>
-
1
)
{
wo
.
group_currentGroups
[
wo
.
group_currentGroup
].
splice
(
indx
,
1
);
}
}
ts
.
storage
(
table
,
'
tablesorter-groups
'
,
wo
.
group_currentGroups
);
}
});
}
$
(
wo
.
group_saveReset
).
on
(
'
click
'
,
function
(){
ts
.
grouping
.
clearSavedGroups
(
table
);
});
c
.
$table
.
on
(
'
pagerChange.tsgrouping
'
,
function
(){
ts
.
grouping
.
update
(
table
,
c
,
wo
);
});
},
clearSavedGroups
:
function
(
table
){
if
(
table
&&
ts
.
storage
)
{
ts
.
storage
(
table
,
'
tablesorter-groups
'
,
''
);
ts
.
grouping
.
update
(
table
,
table
.
config
,
table
.
config
.
widgetOptions
);
}
}
};
ts
.
addWidget
({
id
:
'
group
'
,
priority
:
100
,
options
:
{
group_collapsible
:
true
,
// make the group header clickable and collapse the rows below it.
group_collapsed
:
false
,
// start with all groups collapsed
group_saveGroups
:
true
,
// remember collapsed groups
group_saveReset
:
null
,
// element to clear saved collapsed groups
group_count
:
'
({num})
'
,
// if not false, the "{num}" string is replaced with the number of rows in the group
group_separator
:
'
-
'
,
// group name separator; used when group-separator-# class is used.
group_formatter
:
null
,
// function(txt, column, table, c, wo) { return txt; }
group_callback
:
null
,
// function($cell, $rows, column, table){}, callback allowing modification of the group header labels
group_complete
:
'
groupingComplete
'
,
// event triggered on the table when the grouping widget has finished work
// checkbox parser text used for checked/unchecked values
group_checkbox
:
[
'
checked
'
,
'
unchecked
'
],
// change these default date names based on your language preferences
group_months
:
[
'
Jan
'
,
'
Feb
'
,
'
Mar
'
,
'
Apr
'
,
'
May
'
,
'
Jun
'
,
'
Jul
'
,
'
Aug
'
,
'
Sep
'
,
'
Oct
'
,
'
Nov
'
,
'
Dec
'
],
group_week
:
[
'
Sunday
'
,
'
Monday
'
,
'
Tuesday
'
,
'
Wednesday
'
,
'
Thursday
'
,
'
Friday
'
,
'
Saturday
'
],
group_time
:
[
'
AM
'
,
'
PM
'
],
// this function is used when "group-date" is set to create the date string
// you can just return date, date.toLocaleString(), date.toLocaleDateString() or d.toLocaleTimeString()
// reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date#Conversion_getter
group_dateString
:
function
(
date
)
{
return
date
.
toLocaleString
();
}
},
init
:
function
(
table
,
thisWidget
,
c
,
wo
){
ts
.
grouping
.
bindEvents
(
table
,
c
,
wo
);
},
format
:
function
(
table
,
c
,
wo
)
{
ts
.
grouping
.
update
(
table
,
c
,
wo
);
},
remove
:
function
(
table
,
c
,
wo
){
c
.
$table
.
off
(
'
click
'
,
'
tr.group-header
'
)
.
off
(
'
pagerChange.tsgrouping
'
)
.
find
(
'
.group-hidden
'
).
removeClass
(
'
group-hidden
'
).
end
()
.
find
(
'
tr.group-header
'
).
remove
();
}
});
})(
jQuery
);
www/aptui/js/list-images.js
0 → 100644
View file @
e0c9477d
$
(
function
()
{
'
use strict
'
;
var
template_list
=
[
"
image-list
"
,
"
oops-modal
"
,
"
confirm-delete-image
"
,
"
waitwait-modal
"
];
var
templates
=
APT_OPTIONS
.
fetchTemplateList
(
template_list
);
var
listTemplate
=
_
.
template
(
templates
[
"
image-list
"
]);
var
confirmTemplate
=
_
.
template
(
templates
[
"
confirm-delete-image
"
]);
var
oopsString
=
templates
[
"
oops-modal
"
];
var
waitwaitString
=
templates
[
"
waitwait-modal
"
];
var
amlist
=
null
;
// Results for each AM so we can get it later.
var
imagelist
=
[];
function
initialize
()
{
window
.
APT_OPTIONS
.
initialize
(
sup
);
amlist
=
decodejson
(
'
#amlist-json
'
);
$
(
'
#oops_div
'
).
html
(
oopsString
);
$
(
'
#waitwait_div
'
).
html
(
waitwaitString
);
LoadData
();
}
/*
* Load images from each am in the list and generate a table.
*/
function
LoadData
()
{
var
count
=
Object
.
keys
(
amlist
).
length
;
_
.
each
(
amlist
,
function
(
urn
,
name
)
{
var
callback
=
function
(
json
)
{
console
.
info
(
json
);
// Kill the spinner.
count
--
;
if
(
count
<=
0
)
{
$
(
'
#spinner
'
).
addClass
(
"
hidden
"
);
}
if
(
json
.
code
)
{
console
.
info
(
"
Could not get image list for
"
+
name
+
"
:
"
+
json
.
value
);
return
;
}
var
images
=
json
.
value
;
if
(
images
.
length
==
0
)
return
;
// Save for later
imagelist
[
name
]
=
images
;
// Generate the main template.
var
html
=
listTemplate
({
"
images
"
:
images
,
"
showproject
"
:
false
,
"
showuser
"
:
false
,
"
name
"
:
name
,
});
html
=
"
<div class='row' id='
"
+
name
+
"
'>
"
+
"
<div class='col-xs-12 col-xs-offset-0'>
"
+
html
+
"
</div>
"
+
"
</div>
"
;
$
(
'
#main-body
'
).
prepend
(
html
);
// Format dates with moment before display.
$
(
'
#
'
+
name
+
'
.format-date
'
).
each
(
function
()
{
var
date
=
$
.
trim
(
$
(
this
).
html
());
if
(
date
!=
""
)
{
$
(
this
).
html
(
moment
(
$
(
this
).
html
()).
format
(
"
lll
"
));
}
});
var
TableInit
=
function
(
tablename
)
{
var
table
=
$
(
'
#
'
+
name
+
'
#
'
+
tablename
)
.
tablesorter
({
theme
:
'
green
'
,
widgets
:
[
"
zebra
"
],
cssChildRow
:
'
tablesorter-childRow-versions
'
,
});
table
.
find
(
'
.tablesorter-childRow-versions
'
)
.
addClass
(
'
hidden
'
);
/*
* This little diddy sums up the filesizes for each
* image version, and writes into the filesize for
* the entire image.
*/
table
.
find
(
'
tr.tablesorter-hasChildRow
'
)
.
each
(
function
()
{
var
sum
=
0
;
var
re
=
/^
(\d
+
)
MB$/
;
$
(
this
).
nextUntil
(
'
tr.tablesorter-hasChildRow
'
,
'
.image-version
'
)
.
each
(
function
()
{
var
size
=
$
(
this
).
find
(
'
td.version-filesize
'
)
.
text
();
var
match
=
size
.
match
(
re
);
if
(
match
)
{
sum
=
sum
+
parseInt
(
match
[
1
]);
}
});
$
(
this
).
find
(
'
td.image-filesize
'
).
text
(
sum
+
"
MB
"
);
});
table
.
trigger
(
'
update
'
);
// Toggle child row content. Using delegate cause the
// tablesorter example page says to.
table
.
delegate
(
'
.toggle-image
'
,
'
click
'
,
function
()
{
// use "nextUntil" to toggl