diff --git a/mod/scorm/module.js b/mod/scorm/module.js
index dd943b96495..7e1e9051b40 100644
--- a/mod/scorm/module.js
+++ b/mod/scorm/module.js
@@ -48,7 +48,7 @@ M.mod_scorm.init = function(Y, nav_display, navposition_left, navposition_top, h
var scorm_bloody_labelclick = false;
var scorm_nav_panel;
- Y.use('button', 'dd-plugin', 'panel', 'resize', 'gallery-sm-treeview', function(Y) {
+ Y.use('button', 'dd-plugin', 'panel', 'resize', 'moodle-mod_scorm-treeview', function(Y) {
Y.TreeView.prototype.getNodeByAttribute = function(attribute, value) {
var node = null,
diff --git a/mod/scorm/thirdpartylibs.xml b/mod/scorm/thirdpartylibs.xml
new file mode 100644
index 00000000000..0631f0d5bb4
--- /dev/null
+++ b/mod/scorm/thirdpartylibs.xml
@@ -0,0 +1,10 @@
+
+
+
+ yui/src/treeview
+ Gallery SmugMug TreeView
+ BSD
+ 2013.08.06-16-12
+
+
+
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/moodle-mod_scorm-treeview-core.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/moodle-mod_scorm-treeview-core.css
new file mode 100644
index 00000000000..f7c7ae1384b
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/moodle-mod_scorm-treeview-core.css
@@ -0,0 +1,65 @@
+.yui3-treeview,
+.yui3-treeview * {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+.yui3-treeview-children {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+/* This ensures that the root node is always "open". */
+.yui3-treeview-children .yui3-treeview-children { display: none; }
+
+.yui3-treeview-indicator {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ visibility: hidden;
+ top: 0;
+ width: 20px;
+}
+
+.yui3-treeview-indicator s {
+ border: 5px solid transparent;
+ border-left-color: #afafaf;
+ display: inline-block;
+ *display: none; /* Hide from IE 6-7 */
+ height: 5px;
+ left: 6px;
+ position: relative;
+ top: -1px;
+ vertical-align: middle;
+ width: 5px;
+}
+
+.yui3-treeview-label { white-space: pre; }
+.yui3-treeview-node .yui3-treeview-children { margin-left: 16px; }
+
+.yui3-treeview-row {
+ cursor: pointer;
+ overflow: hidden;
+ padding: 0 4px 0 20px;
+ position: relative;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+.yui3-treeview-can-have-children > .yui3-treeview-row > .yui3-treeview-indicator { visibility: visible; }
+
+.yui3-treeview-open > .yui3-treeview-row > .yui3-treeview-indicator s {
+ border-left-color: transparent;
+ border-top-color: #afafaf;
+ left: 4px;
+ top: 2px;
+}
+
+.yui3-treeview-open > .yui3-treeview-children { display: block; }
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/folder.png b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/folder.png
new file mode 100644
index 00000000000..76d9d12b57d
Binary files /dev/null and b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/folder.png differ
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/folder@2x.png b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/folder@2x.png
new file mode 100644
index 00000000000..c18674e2608
Binary files /dev/null and b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/folder@2x.png differ
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/gallery-sm-treeview-skin-2x.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/gallery-sm-treeview-skin-2x.css
new file mode 100644
index 00000000000..a4b607e1525
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/gallery-sm-treeview-skin-2x.css
@@ -0,0 +1,15 @@
+/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/
+
+.yui3-skin-sam .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);
+ background-size: contain;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+
+.yui3-skin-sam .yui3-treeview-has-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=);
+}
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/item.png b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/item.png
new file mode 100644
index 00000000000..33bcadaaf51
Binary files /dev/null and b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/item.png differ
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/item@2x.png b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/item@2x.png
new file mode 100644
index 00000000000..768e57985d6
Binary files /dev/null and b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/item@2x.png differ
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/moodle-mod_scorm-treeview-skin.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/moodle-mod_scorm-treeview-skin.css
new file mode 100644
index 00000000000..5c4395dac2d
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/moodle-mod_scorm-treeview-skin.css
@@ -0,0 +1,75 @@
+/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/
+
+.yui3-skin-sam .yui3-treeview-row > .yui3-treeview-indicator:after { content: ''; }
+
+.yui3-skin-sam .yui3-treeview-label,
+.yui3-skin-sam .yui3-treeview-row {
+ line-height: 1.6;
+}
+
+/* Page icon for all nodes by default. */
+.yui3-skin-sam .yui3-treeview-icon {
+ background: left center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4jY2Sv2rDMBDGT3JKl/oB2kdI3sJ7lwx9hvYZQkkIDXQuhWYOeCvdS6DP4D3dstsUZArWH/dOsRQnlk0F8onT3U/fJ2tU1zXQuFssbjFcw/D4MsZ8G63hY7WyiZHbwY2bzXy+rnBT0jQGNMLdAVmWwWa7vcclw7lzfUeA1sxQk5SgEVBhrJQChTmCCCHgfblcT2ezh6ZldwLAJkZojt+LKDqKbiB5nsNrmkIyHr+9pOkj7jydK+DUzC0GOpAkSaySOI7hM8uKjgVFClqAEISUuNoOQCvlLbTHOYQOodoQ4GCBc/olvZDoAOBdC5j0CgYgEUbVA7B3YO+BEj2QCPMqaEFKr4Da+iDWAtaGLZAC/FVDENbUhi04BQMQygct/JalXbs76IOwVu0J4KcoLr3M5kHRy2NNtFCEMJztWg8ohdhfTSbP8I+BkL1b/wF76M1c9BEbZwAAAABJRU5ErkJggg==);
+ float: left;
+ height: 1.6em; /* should match the line-height of the row and label */
+ min-height: 16px; /* to prevent the icon from being clipped */
+ width: 16px;
+}
+
+.yui3-skin-sam .yui3-treeview-label { padding-left: 6px; }
+.yui3-skin-sam .yui3-treeview-node { outline: none; }
+.yui3-skin-sam .yui3-treeview-node .yui3-treeview-children { margin-left: 20px; }
+
+.yui3-skin-sam .yui3-treeview-row {
+ background-image: -moz-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -ms-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -o-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ border: 1px solid transparent;
+ border-radius: 2px;
+ _border: none;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+
+/* Light blue background for hovered nodes, except on touch devices. */
+.yui3-skin-sam .yui3-treeview-notouch .yui3-treeview-row:hover {
+ background-color: #F0F6FE;
+ border-color: #B6D4FC;
+}
+
+/* Use a folder icon for nodes that can have children. */
+.yui3-skin-sam .yui3-treeview-can-have-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4T63TsUrDUBTG8Y+TUhfnIhQR6tC8i4uC+BBFKVGETiLUoUPQRfENXARRfApfwCVLRSlFsMTSapsmOd6vuLRJoLEOvyU3/8PhwhVVxTLE2m4g007DNpwMNv8RAHvGUSrF1v1Z/TwNz9iK2WPjsXXo3jUPZvDbVW3Xbbdf0Ov1E25P9122HCDj8QTD4fcM3++jXF6HiKDb7SR4nge2ZgCsKIqRxvc/USqtoVLZTKhWbbCdbhCGMbIMBl+pRqPgd4NYrfloUWy5QSEMFX/B9h82iLUwf3mLYrv8AJ1EqyIWisWVXNhMW3y8vtVOLm7qzctc2LAVfX5o6dO1Yxzn5LAV8yQD493o5MQm+AEX3F8P8w0R8QAAAABJRU5ErkJggg==);
+}
+
+/* Gray background for selected, unfocused nodes. */
+.yui3-skin-sam .yui3-treeview-selected > .yui3-treeview-row {
+ background-color: #E6E6E6;
+ border-color: #D9D9D9;
+}
+
+/* Blue background for selected, focused nodes and selected, hovered nodes,
+ as well as selected unfocused nodes when multiselect is enabled. */
+.yui3-skin-sam .yui3-treeview-selected:focus > .yui3-treeview-row,
+.yui3-skin-sam .yui3-treeview-children[aria-multiselectable="true"] .yui3-treeview-selected > .yui3-treeview-row,
+.yui3-skin-sam .yui3-treeview-selected > .yui3-treeview-row:hover {
+ background-color: #C9E0FC;
+ border-color: #7DA2CE;
+}
+
+/* -- High DPI styles ------------------------------------------------------- */
+@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
+ .yui3-skin-sam .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);
+ background-size: contain;
+ }
+
+ .yui3-skin-sam .yui3-treeview-has-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=);
+ }
+}
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/moodle-mod_scorm-treeview.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/moodle-mod_scorm-treeview.css
new file mode 100644
index 00000000000..76dab7fe8cd
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/assets/skins/sam/moodle-mod_scorm-treeview.css
@@ -0,0 +1,4 @@
+.yui3-treeview,.yui3-treeview *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.yui3-treeview-children{margin:0;padding:0;list-style:none}.yui3-treeview-children .yui3-treeview-children{display:none}.yui3-treeview-indicator{bottom:0;left:0;position:absolute;visibility:hidden;top:0;width:20px}.yui3-treeview-indicator s{border:5px solid transparent;border-left-color:#afafaf;display:inline-block;*display:none;height:5px;left:6px;position:relative;top:-1px;vertical-align:middle;width:5px}.yui3-treeview-label{white-space:pre}.yui3-treeview-node .yui3-treeview-children{margin-left:16px}.yui3-treeview-row{cursor:pointer;overflow:hidden;padding:0 4px 0 20px;position:relative;text-overflow:ellipsis;white-space:nowrap}.yui3-treeview-can-have-children>.yui3-treeview-row>.yui3-treeview-indicator{visibility:visible}.yui3-treeview-open>.yui3-treeview-row>.yui3-treeview-indicator s{border-left-color:transparent;border-top-color:#afafaf;left:4px;top:2px}.yui3-treeview-open>.yui3-treeview-children{display:block}/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/.yui3-skin-sam .yui3-treeview-row>.yui3-treeview-indicator:after{content:''}.yui3-skin-sam .yui3-treeview-label,.yui3-skin-sam .yui3-treeview-row{line-height:1.6}.yui3-skin-sam .yui3-treeview-icon{background:left center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4jY2Sv2rDMBDGT3JKl/oB2kdI3sJ7lwx9hvYZQkkIDXQuhWYOeCvdS6DP4D3dstsUZArWH/dOsRQnlk0F8onT3U/fJ2tU1zXQuFssbjFcw/D4MsZ8G63hY7WyiZHbwY2bzXy+rnBT0jQGNMLdAVmWwWa7vcclw7lzfUeA1sxQk5SgEVBhrJQChTmCCCHgfblcT2ezh6ZldwLAJkZojt+LKDqKbiB5nsNrmkIyHr+9pOkj7jydK+DUzC0GOpAkSaySOI7hM8uKjgVFClqAEISUuNoOQCvlLbTHOYQOodoQ4GCBc/olvZDoAOBdC5j0CgYgEUbVA7B3YO+BEj2QCPMqaEFKr4Da+iDWAtaGLZAC/FVDENbUhi04BQMQygct/JalXbs76IOwVu0J4KcoLr3M5kHRy2NNtFCEMJztWg8ohdhfTSbP8I+BkL1b/wF76M1c9BEbZwAAAABJRU5ErkJggg==);float:left;height:1.6em;min-height:16px;width:16px}.yui3-skin-sam .yui3-treeview-label{padding-left:6px}.yui3-skin-sam .yui3-treeview-node{outline:0}.yui3-skin-sam .yui3-treeview-node .yui3-treeview-children{margin-left:20px}.yui3-skin-sam .yui3-treeview-row{background-image:-moz-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-ms-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-o-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-webkit-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));border:1px solid transparent;border-radius:2px;_border:0}.yui3-skin-sam .yui3-treeview-notouch .yui3-treeview-row:hover{background-color:#f0f6fe;border-color:#b6d4fc}.yui3-skin-sam .yui3-treeview-can-have-children>.yui3-treeview-row>.yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4T63TsUrDUBTG8Y+TUhfnIhQR6tC8i4uC+BBFKVGETiLUoUPQRfENXARRfApfwCVLRSlFsMTSapsmOd6vuLRJoLEOvyU3/8PhwhVVxTLE2m4g007DNpwMNv8RAHvGUSrF1v1Z/TwNz9iK2WPjsXXo3jUPZvDbVW3Xbbdf0Ov1E25P9122HCDj8QTD4fcM3++jXF6HiKDb7SR4nge2ZgCsKIqRxvc/USqtoVLZTKhWbbCdbhCGMbIMBl+pRqPgd4NYrfloUWy5QSEMFX/B9h82iLUwf3mLYrv8AJ1EqyIWisWVXNhMW3y8vtVOLm7qzctc2LAVfX5o6dO1Yxzn5LAV8yQD493o5MQm+AEX3F8P8w0R8QAAAABJRU5ErkJggg==)}.yui3-skin-sam .yui3-treeview-selected>.yui3-treeview-row{background-color:#e6e6e6;border-color:#d9d9d9}.yui3-skin-sam .yui3-treeview-selected:focus>.yui3-treeview-row,.yui3-skin-sam .yui3-treeview-children[aria-multiselectable="true"] .yui3-treeview-selected>.yui3-treeview-row,.yui3-skin-sam .yui3-treeview-selected>.yui3-treeview-row:hover{background-color:#c9e0fc;border-color:#7da2ce}@media(-webkit-min-device-pixel-ratio:1.5),(min-resolution:144dpi){.yui3-skin-sam .yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);background-size:contain}.yui3-skin-sam .yui3-treeview-has-children>.yui3-treeview-row>.yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=)}}#yui3-css-stamp.skin-sam-gallery-sm-treeview{display:none}
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable-debug.js b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable-debug.js
new file mode 100644
index 00000000000..796207cf8a1
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable-debug.js
@@ -0,0 +1,746 @@
+YUI.add('gallery-sm-treeview', function (Y, NAME) {
+
+var Micro = Y.Template.Micro;
+
+Y.namespace('TreeView').Templates = {
+ children: Micro.compile(
+ '
' +
+ 'role="tree" tabindex="0"' +
+ '<% } else { %>' +
+ 'role="group"' +
+ '<% } %>' +
+
+ '>
'
+ ),
+
+ node: Micro.compile(
+ '" role="treeitem" aria-labelled-by="<%= data.node.id %>-label">' +
+ '' +
+ '' +
+ '' +
+ '<%== data.node.label %>' +
+ '
' +
+ ''
+ )
+};
+/*jshint expr:true, onevar:false */
+
+/**
+Provides the `Y.TreeView` widget.
+
+@module gallery-sm-treeview
+@main gallery-sm-treeview
+**/
+
+/**
+TreeView widget.
+
+@class TreeView
+@constructor
+@extends View
+@uses Tree
+@uses Tree.Labelable
+@uses Tree.Openable
+@uses Tree.Selectable
+**/
+
+var getClassName = Y.ClassNameManager.getClassName,
+
+TreeView = Y.Base.create('treeView', Y.View, [
+ Y.Tree,
+ Y.Tree.Labelable,
+ Y.Tree.Openable,
+ Y.Tree.Selectable
+], {
+ // -- Public Properties ----------------------------------------------------
+
+ /**
+ CSS class names used by this treeview.
+
+ @property {Object} classNames
+ @param {String} canHaveChildren Class name indicating that a tree node can
+ contain child nodes (whether or not it actually does).
+ @param {String} children Class name for a list of child nodes.
+ @param {String} hasChildren Class name indicating that a tree node has one
+ or more child nodes.
+ @param {String} icon Class name for a tree node's icon.
+ @param {String} indicator Class name for an open/closed indicator.
+ @param {String} label Class name for a tree node's user-visible label.
+ @param {String} node Class name for a tree node item.
+ @param {String} noTouch Class name added to the TreeView container when not
+ using a touchscreen device.
+ @param {String} open Class name indicating that a tree node is open.
+ @param {String} row Class name for a row container encompassing the
+ indicator and label within a tree node.
+ @param {String} selected Class name for a tree node that's selected.
+ @param {String} touch Class name added to the TreeView container when using
+ a touchscreen device.
+ @param {String} treeview Class name for the TreeView container.
+ **/
+ classNames: {
+ canHaveChildren: getClassName('treeview-can-have-children'),
+ children : getClassName('treeview-children'),
+ hasChildren : getClassName('treeview-has-children'),
+ icon : getClassName('treeview-icon'),
+ indicator : getClassName('treeview-indicator'),
+ label : getClassName('treeview-label'),
+ node : getClassName('treeview-node'),
+ noTouch : getClassName('treeview-notouch'),
+ open : getClassName('treeview-open'),
+ row : getClassName('treeview-row'),
+ selected : getClassName('treeview-selected'),
+ touch : getClassName('treeview-touch'),
+ treeview : getClassName('treeview')
+ },
+
+ /**
+ Whether or not this TreeView has been rendered.
+
+ @property {Boolean} rendered
+ @default false
+ **/
+ rendered: false,
+
+ /**
+ Default templates used to render this TreeView.
+
+ @property {Object} templates
+ **/
+ templates: Y.TreeView.Templates,
+
+ // -- Protected Properties -------------------------------------------------
+
+ /**
+ Simple way to type-check that this is a TreeView instance.
+
+ @property {Boolean} _isYUITreeView
+ @default true
+ @protected
+ **/
+ _isYUITreeView: true,
+
+ /**
+ Cached value of the `lazyRender` attribute.
+
+ @property {Boolean} _lazyRender
+ @protected
+ **/
+
+ // -- Lifecycle Methods ----------------------------------------------------
+
+ initializer: function (config) {
+ if (config && config.templates) {
+ this.templates = Y.merge(this.templates, config.templates);
+ }
+
+ this._renderQueue = {};
+ this._attachTreeViewEvents();
+ },
+
+ destructor: function () {
+ clearTimeout(this._renderTimeout);
+ this._detachTreeViewEvents();
+
+ this._renderQueue = null;
+ },
+
+ // -- Public Methods -------------------------------------------------------
+
+ destroyNode: function (node, options) {
+ node._htmlNode = null;
+ return Y.Tree.prototype.destroyNode.call(this, node, options);
+ },
+
+ /**
+ Returns the HTML node (as a `Y.Node` instance) associated with the specified
+ `Tree.Node` instance, if any.
+
+ @method getHTMLNode
+ @param {Tree.Node} treeNode Tree node.
+ @return {Node} `Y.Node` instance associated with the given tree node, or
+ `undefined` if one was not found.
+ **/
+ getHTMLNode: function (treeNode) {
+ if (!treeNode._htmlNode) {
+ treeNode._htmlNode = this.get('container').one('#' + treeNode.id);
+ }
+
+ return treeNode._htmlNode;
+ },
+
+ /**
+ Renders this TreeView into its container.
+
+ If the container hasn't already been added to the current document, it will
+ be appended to the `` element.
+
+ @method render
+ @chainable
+ **/
+ render: function () {
+ var container = this.get('container'),
+ isTouchDevice = 'ontouchstart' in Y.config.win;
+
+ container.addClass(this.classNames.treeview);
+ container.addClass(this.classNames[isTouchDevice ? 'touch' : 'noTouch']);
+
+ this._childrenNode = this.renderChildren(this.rootNode, {
+ container: container
+ });
+
+ if (!container.inDoc()) {
+ Y.one('body').append(container);
+ }
+
+ this.rendered = true;
+
+ return this;
+ },
+
+ /**
+ Renders the children of the specified tree node.
+
+ If a container is specified, it will be assumed to be an existing rendered
+ tree node, and the children will be rendered (or re-rendered) inside it.
+
+ @method renderChildren
+ @param {Tree.Node} treeNode Tree node whose children should be rendered.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container into
+ which the children should be rendered. If the container already
+ contains rendered children, they will be re-rendered in place.
+
+ @param {Boolean} [options.force=false] If `true`, children will be
+ re-rendered from scratch even if they've already been rendered.
+
+ @return {Node} `Y.Node` instance containing the rendered children.
+ **/
+ renderChildren: function (treeNode, options) {
+ options || (options = {});
+
+ var container = options.container,
+ childrenNode = container && container.one('>.' + this.classNames.children),
+ lazyRender = this._lazyRender;
+
+ if (childrenNode && options.force) {
+ childrenNode.remove(true);
+ childrenNode = null;
+ }
+
+ if (!childrenNode) {
+ childrenNode = Y.Node.create(this.templates.children({
+ classNames: this.classNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ if (treeNode.hasChildren()) {
+ childrenNode.set('aria-expanded', treeNode.isOpen());
+
+ for (var i = 0, len = treeNode.children.length; i < len; i++) {
+ var child = treeNode.children[i];
+
+ this.renderNode(child, {
+ container : childrenNode,
+ renderChildren: !lazyRender || child.isOpen()
+ });
+ }
+ }
+
+ // Keep track of whether or not this node's children have been rendered
+ // so we'll know whether we need to render them later if the node is
+ // opened.
+ treeNode.state.renderedChildren = true;
+
+ if (container) {
+ container.append(childrenNode);
+ }
+
+ return childrenNode;
+ },
+
+ /**
+ Renders the specified tree node and its children (if any).
+
+ If a container is specified, the rendered node will be appended to it.
+
+ @method renderNode
+ @param {Tree.Node} treeNode Tree node to render.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container to
+ which the rendered tree node should be appended.
+
+ @param {Boolean} [options.force=false] If `true`, this node (and its
+ children if `renderChildren` is `true`) will be re-rendered from
+ scratch, even if it's already been rendered.
+
+ @param {Boolean} [options.renderChildren=false] Whether or not to render
+ this node's children.
+
+ @return {Node} `Y.Node` instance of the rendered tree node.
+ **/
+ renderNode: function (treeNode, options) {
+ options || (options = {});
+
+ var classNames = this.classNames,
+ hasChildren = treeNode.hasChildren(),
+ htmlNode = treeNode._htmlNode,
+ oldHtmlNode = options.force && htmlNode,
+ nodeClassNames = {},
+
+ className;
+
+ // Build the hash of CSS classes for this node.
+ nodeClassNames[classNames.node] = true;
+ nodeClassNames[classNames.canHaveChildren] = !!treeNode.canHaveChildren;
+ nodeClassNames[classNames.hasChildren] = hasChildren;
+
+ if (htmlNode && !options.force) {
+ // This node has already been rendered, so we just need to update
+ // the DOM instead of re-rendering it from scratch.
+ htmlNode.one('.' + classNames.label).setHTML(treeNode.label);
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className)) {
+ htmlNode.toggleClass(className, nodeClassNames[className]);
+ }
+ }
+ } else {
+ // This node hasn't been rendered yet or is being forcibly
+ // re-rendered.
+ var enabledClassNames = [];
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className) && nodeClassNames[className]) {
+ enabledClassNames.push(className);
+ }
+ }
+
+ htmlNode = treeNode._htmlNode = Y.Node.create(this.templates.node({
+ classNames : classNames,
+ nodeClassNames: enabledClassNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ this._syncNodeSelectedState(treeNode, htmlNode);
+
+ if (hasChildren) {
+ if (options.renderChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+ } else {
+ // If children were previously rendered but this node no longer has
+ // children, remove the empty child list.
+ var childrenNode = htmlNode.one('>.' + classNames.children);
+
+ if (childrenNode) {
+ childrenNode.remove(true);
+ }
+ }
+
+ treeNode.state.rendered = true;
+
+ if (options.force) {
+ oldHtmlNode.replace(htmlNode);
+ oldHtmlNode.destroy();
+ } else {
+ if (options.container && htmlNode.get('parentNode') !== options.container) {
+ options.container.append(htmlNode);
+ }
+ }
+
+ return htmlNode;
+ },
+
+ // -- Protected Methods ----------------------------------------------------
+
+ _attachTreeViewEvents: function () {
+ this._treeViewEvents || (this._treeViewEvents = []);
+
+ var classNames = this.classNames,
+ container = this.get('container');
+
+ this._treeViewEvents.push(
+ // Custom events.
+ this.after({
+ add : this._afterAdd,
+ clear : this._afterClear,
+ close : this._afterClose,
+ multiSelectChange: this._afterTreeViewMultiSelectChange, // sheesh
+ open : this._afterOpen,
+ remove : this._afterRemove,
+ select : this._afterSelect,
+ unselect : this._afterUnselect
+ }),
+
+ // DOM events.
+ container.on('mousedown', this._onMouseDown, this),
+
+ container.delegate('click', this._onIndicatorClick,
+ '.' + classNames.indicator, this),
+
+ container.delegate('click', this._onRowClick,
+ '.' + classNames.row, this),
+
+ container.delegate('dblclick', this._onRowDoubleClick,
+ '.' + classNames.canHaveChildren + ' > .' + classNames.row, this)
+ );
+ },
+
+ _detachTreeViewEvents: function () {
+ (new Y.EventHandle(this._treeViewEvents)).detach();
+ },
+
+ _processRenderQueue: function () {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ node;
+
+ for (var id in queue) {
+ if (queue.hasOwnProperty(id)) {
+ node = this.getNodeById(id);
+
+ if (node) {
+ this.renderNode(node, queue[id]);
+ }
+ }
+ }
+
+ this._renderQueue = {};
+ },
+
+ _queueRender: function (node, options) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ self = this;
+
+ clearTimeout(this._renderTimeout);
+
+ queue[node.id] = Y.merge(queue[node.id], options);
+
+ this._renderTimeout = setTimeout(function () {
+ self._processRenderQueue();
+ }, 15);
+
+ return this;
+ },
+
+ /**
+ Setter for the `lazyRender` attribute.
+
+ Just caches the value in a property for faster lookups.
+
+ @method _setLazyRender
+ @return {Boolean} Value.
+ @protected
+ **/
+ _setLazyRender: function (value) {
+ /*jshint boss:true */
+ return this._lazyRender = value;
+ },
+
+ _syncNodeOpenState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ if (node.isOpen()) {
+ htmlNode
+ .addClass(this.classNames.open)
+ .set('aria-expanded', true);
+ } else {
+ htmlNode
+ .removeClass(this.classNames.open)
+ .set('aria-expanded', false);
+ }
+ },
+
+ _syncNodeSelectedState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ var multiSelect = this.get('multiSelect');
+
+ if (node.isSelected()) {
+ htmlNode.addClass(this.classNames.selected);
+
+ if (multiSelect) {
+ // It's only necessary to set aria-selected when multi-select is
+ // enabled and focus can't be used to track the selection state.
+ htmlNode.set('aria-selected', true);
+ } else {
+ htmlNode.set('tabIndex', 0);
+ }
+ } else {
+ htmlNode
+ .removeClass(this.classNames.selected)
+ .removeAttribute('tabIndex');
+
+ if (multiSelect) {
+ htmlNode.set('aria-selected', false);
+ }
+ }
+ },
+
+ // -- Protected Event Handlers ---------------------------------------------
+
+ _afterAdd: function (e) {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ var parent = e.parent,
+ parentIsRoot = parent.isRoot(),
+ treeNode = e.node,
+
+ htmlChildren,
+ htmlParent;
+
+ if (parentIsRoot) {
+ htmlChildren = this._childrenNode;
+ } else {
+ htmlParent = this.getHTMLNode(parent),
+ htmlChildren = htmlParent && htmlParent.one('>.' + this.classNames.children);
+ }
+
+ if (htmlChildren) {
+ // Parent's children have already been rendered. Instead of
+ // re-rendering all of them, just render the new node and insert it
+ // at the correct position.
+ htmlChildren.insert(this.renderNode(treeNode, {
+ renderChildren: !this._lazyRender || treeNode.isOpen()
+ }), e.index);
+
+ // Schedule the parent node to be re-rendered in order to update its
+ // state. This is done asynchronously and throttled in order to
+ // avoid re-rendering the parent many times if multiple children are
+ // added in quick succession.
+ if (!parentIsRoot) {
+ this._queueRender(parent);
+ }
+ } else if (!parentIsRoot) {
+ // Either the parent hasn't been rendered yet, or its children
+ // haven't been rendered yet. Schedule it to be rendered. This is
+ // done asynchronously and throttled in order to avoid re-rendering
+ // the parent many times if multiple children are added in quick
+ // succession.
+ this._queueRender(parent, {renderChildren: true});
+ }
+ },
+
+ _afterClear: function () {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ clearTimeout(this._renderTimeout);
+ this._renderQueue = {};
+
+ delete this._childrenNode;
+ this.rendered = false;
+
+ this.get('container').empty();
+ this.render();
+ },
+
+ _afterClose: function (e) {
+ if (this.rendered) {
+ this._syncNodeOpenState(e.node);
+ }
+ },
+
+ _afterOpen: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ htmlNode = this.getHTMLNode(treeNode);
+
+ // If this node's children haven't been rendered yet, render them.
+ if (!treeNode.state.renderedChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ },
+
+ _afterRemove: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ parent = e.parent;
+
+ // If this node is in the render queue, remove it from the queue.
+ if (this._renderQueue[treeNode.id]) {
+ delete this._renderQueue[treeNode.id];
+ }
+
+ // Remove DOM nodes associated with this node and any of its
+ // descendants, and mark all nodes as unrendered so that they'll be
+ // re-rendered if they're reinserted in the tree.
+ var htmlNode = this.getHTMLNode(treeNode);
+
+ if (htmlNode) {
+ htmlNode
+ .empty()
+ .remove(true);
+
+ treeNode._htmlNode = null;
+ }
+
+ if (!treeNode.state.destroyed) {
+ treeNode.traverse(function (node) {
+ node._htmlNode = null;
+ node.state.rendered = false;
+ node.state.renderedChildren = false;
+ });
+ }
+
+ // Re-render the parent to update its state if this was its last child.
+ if (parent && !parent.hasChildren()) {
+ this.renderNode(parent);
+ }
+ },
+
+ _afterSelect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _afterTreeViewMultiSelectChange: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var container = this.get('container'),
+ rootList = container.one('> .' + this.classNames.children),
+ htmlNodes = container.all('.' + this.classNames.node);
+
+ if (e.newVal) {
+ rootList.set('aria-multiselectable', true);
+ htmlNodes.set('aria-selected', false);
+ } else {
+ // When multiselect is disabled, aria-selected must not be set on
+ // any nodes, since focus is used to indicate selection.
+ rootList.removeAttribute('aria-multiselectable');
+ htmlNodes.removeAttribute('aria-selected');
+ }
+ },
+
+ _afterUnselect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _onIndicatorClick: function (e) {
+ var rowNode = e.currentTarget.ancestor('.' + this.classNames.row);
+
+ // Indicator clicks shouldn't toggle selection state, so don't allow
+ // this event to propagate to the _onRowClick() handler.
+ e.stopImmediatePropagation();
+
+ this.getNodeById(rowNode.getData('node-id')).toggleOpen();
+ },
+
+ _onMouseDown: function (e) {
+ // This prevents the tree from momentarily grabbing focus before focus
+ // is set on a node.
+ e.preventDefault();
+ },
+
+ _onRowClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ var node = this.getNodeById(e.currentTarget.getData('node-id'));
+
+ if (this.get('multiSelect')) {
+ node[node.isSelected() ? 'unselect' : 'select']();
+ } else {
+ node.select();
+ }
+ },
+
+ _onRowDoubleClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ this.getNodeById(e.currentTarget.getData('node-id')).toggleOpen();
+ }
+}, {
+ ATTRS: {
+ /**
+ When `true`, a node's children won't be rendered until the first time
+ that node is opened.
+
+ This can significantly speed up the time it takes to render a large
+ tree, but might not make sense if you're using CSS that doesn't hide the
+ contents of closed nodes.
+
+ @attribute {Boolean} lazyRender
+ @default true
+ **/
+ lazyRender: {
+ lazyAdd: false, // to ensure that the setter runs on init
+ setter : '_setLazyRender',
+ value : true
+ }
+ }
+});
+
+Y.TreeView = Y.mix(TreeView, Y.TreeView);
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base-build",
+ "classnamemanager",
+ "template-micro",
+ "tree",
+ "tree-labelable",
+ "tree-openable",
+ "tree-selectable",
+ "view"
+ ],
+ "skinnable": true
+});
+YUI.add('moodle-mod_scorm-treeview-sortable', function (Y, NAME) {
+
+Y.use('gallery-sm-treeview-sortable');
+
+
+}, '@VERSION@', {"requires": ["moodle-mod_scorm-treeview", "tree-sortable"]});
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable-min.js b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable-min.js
new file mode 100644
index 00000000000..fb86e787381
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable-min.js
@@ -0,0 +1,2 @@
+YUI.add("gallery-sm-treeview",function(e,t){var n=e.Template.Micro;e.namespace("TreeView").Templates={children:n.compile('role="tree" tabindex="0"<% } else { %>role="group"<% } %>>
'),node:n.compile('" role="treeitem" aria-labelled-by="<%= data.node.id %>-label"><%== data.node.label %>
')};var r=e.ClassNameManager.getClassName,i=e.Base.create("treeView",e.View,[e.Tree,e.Tree.Labelable,e.Tree.Openable,e.Tree.Selectable],{classNames:{canHaveChildren:r("treeview-can-have-children"),children:r("treeview-children"),hasChildren:r("treeview-has-children"),icon:r("treeview-icon"),indicator:r("treeview-indicator"),label:r("treeview-label"),node:r("treeview-node"),noTouch:r("treeview-notouch"),open:r("treeview-open"),row:r("treeview-row"),selected:r("treeview-selected"),touch:r("treeview-touch"),treeview:r("treeview")},rendered:!1,templates:e.TreeView.Templates,_isYUITreeView:!0,initializer:function(t){t&&t.templates&&(this.templates=e.merge(this.templates,t.templates)),this._renderQueue={},this._attachTreeViewEvents()},destructor:function(){clearTimeout(this._renderTimeout),this._detachTreeViewEvents(),this._renderQueue=null},destroyNode:function(t,n){return t._htmlNode=null,e.Tree.prototype.destroyNode.call(this,t,n)},getHTMLNode:function(e){return e._htmlNode||(e._htmlNode=this.get("container").one("#"+e.id)),e._htmlNode},render:function(){var t=this.get("container"),n="ontouchstart"in e.config.win;return t.addClass(this.classNames.treeview),t.addClass(this.classNames[n?"touch":"noTouch"]),this._childrenNode=this.renderChildren(this.rootNode,{container:t}),t.inDoc()||e.one("body").append(t),this.rendered=!0,this},renderChildren:function(t,n){n||(n={});var r=n.container,i=r&&r.one(">."+this.classNames.children),s=this._lazyRender;i&&n.force&&(i.remove(!0),i=null),i||(i=e.Node.create(this.templates.children({classNames:this.classNames,node:t,treeview:this})));if(t.hasChildren()){i.set("aria-expanded",t.isOpen());for(var o=0,u=t.children.length;o."+r.children);l&&l.remove(!0)}return t.state.rendered=!0,n.force?(o.replace(s),o.destroy()):n.container&&s.get("parentNode")!==n.container&&n.container.append(s),s},_attachTreeViewEvents:function(){this._treeViewEvents||(this._treeViewEvents=[]);var e=this.classNames,t=this.get("container");this._treeViewEvents.push(this.after({add:this._afterAdd,clear:this._afterClear,close:this._afterClose,multiSelectChange:this._afterTreeViewMultiSelectChange,open:this._afterOpen,remove:this._afterRemove,select:this._afterSelect,unselect:this._afterUnselect}),t.on("mousedown",this._onMouseDown,this),t.delegate("click",this._onIndicatorClick,"."+e.indicator,this),t.delegate("click",this._onRowClick,"."+e.row,this),t.delegate("dblclick",this._onRowDoubleClick,"."+e.canHaveChildren+" > ."+e.row,this))},_detachTreeViewEvents:function(){(new e.EventHandle(this._treeViewEvents)).detach()},_processRenderQueue:function(){if(!this.rendered)return;var e=this._renderQueue,t;for(var n in e)e.hasOwnProperty(n)&&(t=this.getNodeById(n),t&&this.renderNode(t,e[n]));this._renderQueue={}},_queueRender:function(t,n){if(!this.rendered)return;var r=this._renderQueue,i=this;return clearTimeout(this._renderTimeout),r[t.id]=e.merge(r[t.id],n),this._renderTimeout=setTimeout(function(){i._processRenderQueue()},15),this},_setLazyRender:function(e){return this._lazyRender=e},_syncNodeOpenState:function(e,t){t||(t=this.getHTMLNode(e));if(!t)return;e.isOpen()?t.addClass(this.classNames.open).set("aria-expanded",!0):t.removeClass(this.classNames.open).set("aria-expanded",!1)},_syncNodeSelectedState:function(e,t){t||(t=this.getHTMLNode(e));if(!t)return;var n=this.get("multiSelect");e.isSelected()?(t.addClass(this.classNames.selected),n?t.set("aria-selected",!0):t.set("tabIndex",0)):(t.removeClass(this.classNames.selected).removeAttribute("tabIndex"),n&&t.set("aria-selected",!1))},_afterAdd:function(e){if(!this.rendered)return;var t=e.parent,n=t.isRoot(),r=e.node,i,s;n?i=this._childrenNode:(s=this.getHTMLNode(t),i=s&&s.one(">."+this.classNames.children)),i?(i.insert(this.renderNode(r,{renderChildren:!this._lazyRender||r.isOpen()}),e.index),n||this._queueRender(t)):n||this._queueRender(t,{renderChildren:!0})},_afterClear:function(){if(!this.rendered)return;clearTimeout(this._renderTimeout),this._renderQueue={},delete this._childrenNode,this.rendered=!1,this.get("container").empty(),this.render()},_afterClose:function(e){this.rendered&&this._syncNodeOpenState(e.node)},_afterOpen:function(e){if(!this.rendered)return;var t=e.node,n=this.getHTMLNode(t);t.state.renderedChildren||this.renderChildren(t,{container:n}),this._syncNodeOpenState(t,n)},_afterRemove:function(e){if(!this.rendered)return;var t=e.node,n=e.parent;this._renderQueue[t.id]&&delete this._renderQueue[t.id];var r=this.getHTMLNode(t);r&&(r.empty().remove(!0),t._htmlNode=null),t.state.destroyed||t.traverse
+(function(e){e._htmlNode=null,e.state.rendered=!1,e.state.renderedChildren=!1}),n&&!n.hasChildren()&&this.renderNode(n)},_afterSelect:function(e){this.rendered&&this._syncNodeSelectedState(e.node)},_afterTreeViewMultiSelectChange:function(e){if(!this.rendered)return;var t=this.get("container"),n=t.one("> ."+this.classNames.children),r=t.all("."+this.classNames.node);e.newVal?(n.set("aria-multiselectable",!0),r.set("aria-selected",!1)):(n.removeAttribute("aria-multiselectable"),r.removeAttribute("aria-selected"))},_afterUnselect:function(e){this.rendered&&this._syncNodeSelectedState(e.node)},_onIndicatorClick:function(e){var t=e.currentTarget.ancestor("."+this.classNames.row);e.stopImmediatePropagation(),this.getNodeById(t.getData("node-id")).toggleOpen()},_onMouseDown:function(e){e.preventDefault()},_onRowClick:function(e){if(e.button>1)return;var t=this.getNodeById(e.currentTarget.getData("node-id"));this.get("multiSelect")?t[t.isSelected()?"unselect":"select"]():t.select()},_onRowDoubleClick:function(e){if(e.button>1)return;this.getNodeById(e.currentTarget.getData("node-id")).toggleOpen()}},{ATTRS:{lazyRender:{lazyAdd:!1,setter:"_setLazyRender",value:!0}}});e.TreeView=e.mix(i,e.TreeView)},"@VERSION@",{requires:["base-build","classnamemanager","template-micro","tree","tree-labelable","tree-openable","tree-selectable","view"],skinnable:!0}),YUI.add("moodle-mod_scorm-treeview-sortable",function(e,t){e.use("gallery-sm-treeview-sortable")},"@VERSION@",{requires:["moodle-mod_scorm-treeview","tree-sortable"]});
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable.js b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable.js
new file mode 100644
index 00000000000..796207cf8a1
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview-sortable/moodle-mod_scorm-treeview-sortable.js
@@ -0,0 +1,746 @@
+YUI.add('gallery-sm-treeview', function (Y, NAME) {
+
+var Micro = Y.Template.Micro;
+
+Y.namespace('TreeView').Templates = {
+ children: Micro.compile(
+ '' +
+ 'role="tree" tabindex="0"' +
+ '<% } else { %>' +
+ 'role="group"' +
+ '<% } %>' +
+
+ '>
'
+ ),
+
+ node: Micro.compile(
+ '" role="treeitem" aria-labelled-by="<%= data.node.id %>-label">' +
+ '' +
+ '' +
+ '' +
+ '<%== data.node.label %>' +
+ '
' +
+ ''
+ )
+};
+/*jshint expr:true, onevar:false */
+
+/**
+Provides the `Y.TreeView` widget.
+
+@module gallery-sm-treeview
+@main gallery-sm-treeview
+**/
+
+/**
+TreeView widget.
+
+@class TreeView
+@constructor
+@extends View
+@uses Tree
+@uses Tree.Labelable
+@uses Tree.Openable
+@uses Tree.Selectable
+**/
+
+var getClassName = Y.ClassNameManager.getClassName,
+
+TreeView = Y.Base.create('treeView', Y.View, [
+ Y.Tree,
+ Y.Tree.Labelable,
+ Y.Tree.Openable,
+ Y.Tree.Selectable
+], {
+ // -- Public Properties ----------------------------------------------------
+
+ /**
+ CSS class names used by this treeview.
+
+ @property {Object} classNames
+ @param {String} canHaveChildren Class name indicating that a tree node can
+ contain child nodes (whether or not it actually does).
+ @param {String} children Class name for a list of child nodes.
+ @param {String} hasChildren Class name indicating that a tree node has one
+ or more child nodes.
+ @param {String} icon Class name for a tree node's icon.
+ @param {String} indicator Class name for an open/closed indicator.
+ @param {String} label Class name for a tree node's user-visible label.
+ @param {String} node Class name for a tree node item.
+ @param {String} noTouch Class name added to the TreeView container when not
+ using a touchscreen device.
+ @param {String} open Class name indicating that a tree node is open.
+ @param {String} row Class name for a row container encompassing the
+ indicator and label within a tree node.
+ @param {String} selected Class name for a tree node that's selected.
+ @param {String} touch Class name added to the TreeView container when using
+ a touchscreen device.
+ @param {String} treeview Class name for the TreeView container.
+ **/
+ classNames: {
+ canHaveChildren: getClassName('treeview-can-have-children'),
+ children : getClassName('treeview-children'),
+ hasChildren : getClassName('treeview-has-children'),
+ icon : getClassName('treeview-icon'),
+ indicator : getClassName('treeview-indicator'),
+ label : getClassName('treeview-label'),
+ node : getClassName('treeview-node'),
+ noTouch : getClassName('treeview-notouch'),
+ open : getClassName('treeview-open'),
+ row : getClassName('treeview-row'),
+ selected : getClassName('treeview-selected'),
+ touch : getClassName('treeview-touch'),
+ treeview : getClassName('treeview')
+ },
+
+ /**
+ Whether or not this TreeView has been rendered.
+
+ @property {Boolean} rendered
+ @default false
+ **/
+ rendered: false,
+
+ /**
+ Default templates used to render this TreeView.
+
+ @property {Object} templates
+ **/
+ templates: Y.TreeView.Templates,
+
+ // -- Protected Properties -------------------------------------------------
+
+ /**
+ Simple way to type-check that this is a TreeView instance.
+
+ @property {Boolean} _isYUITreeView
+ @default true
+ @protected
+ **/
+ _isYUITreeView: true,
+
+ /**
+ Cached value of the `lazyRender` attribute.
+
+ @property {Boolean} _lazyRender
+ @protected
+ **/
+
+ // -- Lifecycle Methods ----------------------------------------------------
+
+ initializer: function (config) {
+ if (config && config.templates) {
+ this.templates = Y.merge(this.templates, config.templates);
+ }
+
+ this._renderQueue = {};
+ this._attachTreeViewEvents();
+ },
+
+ destructor: function () {
+ clearTimeout(this._renderTimeout);
+ this._detachTreeViewEvents();
+
+ this._renderQueue = null;
+ },
+
+ // -- Public Methods -------------------------------------------------------
+
+ destroyNode: function (node, options) {
+ node._htmlNode = null;
+ return Y.Tree.prototype.destroyNode.call(this, node, options);
+ },
+
+ /**
+ Returns the HTML node (as a `Y.Node` instance) associated with the specified
+ `Tree.Node` instance, if any.
+
+ @method getHTMLNode
+ @param {Tree.Node} treeNode Tree node.
+ @return {Node} `Y.Node` instance associated with the given tree node, or
+ `undefined` if one was not found.
+ **/
+ getHTMLNode: function (treeNode) {
+ if (!treeNode._htmlNode) {
+ treeNode._htmlNode = this.get('container').one('#' + treeNode.id);
+ }
+
+ return treeNode._htmlNode;
+ },
+
+ /**
+ Renders this TreeView into its container.
+
+ If the container hasn't already been added to the current document, it will
+ be appended to the `` element.
+
+ @method render
+ @chainable
+ **/
+ render: function () {
+ var container = this.get('container'),
+ isTouchDevice = 'ontouchstart' in Y.config.win;
+
+ container.addClass(this.classNames.treeview);
+ container.addClass(this.classNames[isTouchDevice ? 'touch' : 'noTouch']);
+
+ this._childrenNode = this.renderChildren(this.rootNode, {
+ container: container
+ });
+
+ if (!container.inDoc()) {
+ Y.one('body').append(container);
+ }
+
+ this.rendered = true;
+
+ return this;
+ },
+
+ /**
+ Renders the children of the specified tree node.
+
+ If a container is specified, it will be assumed to be an existing rendered
+ tree node, and the children will be rendered (or re-rendered) inside it.
+
+ @method renderChildren
+ @param {Tree.Node} treeNode Tree node whose children should be rendered.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container into
+ which the children should be rendered. If the container already
+ contains rendered children, they will be re-rendered in place.
+
+ @param {Boolean} [options.force=false] If `true`, children will be
+ re-rendered from scratch even if they've already been rendered.
+
+ @return {Node} `Y.Node` instance containing the rendered children.
+ **/
+ renderChildren: function (treeNode, options) {
+ options || (options = {});
+
+ var container = options.container,
+ childrenNode = container && container.one('>.' + this.classNames.children),
+ lazyRender = this._lazyRender;
+
+ if (childrenNode && options.force) {
+ childrenNode.remove(true);
+ childrenNode = null;
+ }
+
+ if (!childrenNode) {
+ childrenNode = Y.Node.create(this.templates.children({
+ classNames: this.classNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ if (treeNode.hasChildren()) {
+ childrenNode.set('aria-expanded', treeNode.isOpen());
+
+ for (var i = 0, len = treeNode.children.length; i < len; i++) {
+ var child = treeNode.children[i];
+
+ this.renderNode(child, {
+ container : childrenNode,
+ renderChildren: !lazyRender || child.isOpen()
+ });
+ }
+ }
+
+ // Keep track of whether or not this node's children have been rendered
+ // so we'll know whether we need to render them later if the node is
+ // opened.
+ treeNode.state.renderedChildren = true;
+
+ if (container) {
+ container.append(childrenNode);
+ }
+
+ return childrenNode;
+ },
+
+ /**
+ Renders the specified tree node and its children (if any).
+
+ If a container is specified, the rendered node will be appended to it.
+
+ @method renderNode
+ @param {Tree.Node} treeNode Tree node to render.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container to
+ which the rendered tree node should be appended.
+
+ @param {Boolean} [options.force=false] If `true`, this node (and its
+ children if `renderChildren` is `true`) will be re-rendered from
+ scratch, even if it's already been rendered.
+
+ @param {Boolean} [options.renderChildren=false] Whether or not to render
+ this node's children.
+
+ @return {Node} `Y.Node` instance of the rendered tree node.
+ **/
+ renderNode: function (treeNode, options) {
+ options || (options = {});
+
+ var classNames = this.classNames,
+ hasChildren = treeNode.hasChildren(),
+ htmlNode = treeNode._htmlNode,
+ oldHtmlNode = options.force && htmlNode,
+ nodeClassNames = {},
+
+ className;
+
+ // Build the hash of CSS classes for this node.
+ nodeClassNames[classNames.node] = true;
+ nodeClassNames[classNames.canHaveChildren] = !!treeNode.canHaveChildren;
+ nodeClassNames[classNames.hasChildren] = hasChildren;
+
+ if (htmlNode && !options.force) {
+ // This node has already been rendered, so we just need to update
+ // the DOM instead of re-rendering it from scratch.
+ htmlNode.one('.' + classNames.label).setHTML(treeNode.label);
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className)) {
+ htmlNode.toggleClass(className, nodeClassNames[className]);
+ }
+ }
+ } else {
+ // This node hasn't been rendered yet or is being forcibly
+ // re-rendered.
+ var enabledClassNames = [];
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className) && nodeClassNames[className]) {
+ enabledClassNames.push(className);
+ }
+ }
+
+ htmlNode = treeNode._htmlNode = Y.Node.create(this.templates.node({
+ classNames : classNames,
+ nodeClassNames: enabledClassNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ this._syncNodeSelectedState(treeNode, htmlNode);
+
+ if (hasChildren) {
+ if (options.renderChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+ } else {
+ // If children were previously rendered but this node no longer has
+ // children, remove the empty child list.
+ var childrenNode = htmlNode.one('>.' + classNames.children);
+
+ if (childrenNode) {
+ childrenNode.remove(true);
+ }
+ }
+
+ treeNode.state.rendered = true;
+
+ if (options.force) {
+ oldHtmlNode.replace(htmlNode);
+ oldHtmlNode.destroy();
+ } else {
+ if (options.container && htmlNode.get('parentNode') !== options.container) {
+ options.container.append(htmlNode);
+ }
+ }
+
+ return htmlNode;
+ },
+
+ // -- Protected Methods ----------------------------------------------------
+
+ _attachTreeViewEvents: function () {
+ this._treeViewEvents || (this._treeViewEvents = []);
+
+ var classNames = this.classNames,
+ container = this.get('container');
+
+ this._treeViewEvents.push(
+ // Custom events.
+ this.after({
+ add : this._afterAdd,
+ clear : this._afterClear,
+ close : this._afterClose,
+ multiSelectChange: this._afterTreeViewMultiSelectChange, // sheesh
+ open : this._afterOpen,
+ remove : this._afterRemove,
+ select : this._afterSelect,
+ unselect : this._afterUnselect
+ }),
+
+ // DOM events.
+ container.on('mousedown', this._onMouseDown, this),
+
+ container.delegate('click', this._onIndicatorClick,
+ '.' + classNames.indicator, this),
+
+ container.delegate('click', this._onRowClick,
+ '.' + classNames.row, this),
+
+ container.delegate('dblclick', this._onRowDoubleClick,
+ '.' + classNames.canHaveChildren + ' > .' + classNames.row, this)
+ );
+ },
+
+ _detachTreeViewEvents: function () {
+ (new Y.EventHandle(this._treeViewEvents)).detach();
+ },
+
+ _processRenderQueue: function () {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ node;
+
+ for (var id in queue) {
+ if (queue.hasOwnProperty(id)) {
+ node = this.getNodeById(id);
+
+ if (node) {
+ this.renderNode(node, queue[id]);
+ }
+ }
+ }
+
+ this._renderQueue = {};
+ },
+
+ _queueRender: function (node, options) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ self = this;
+
+ clearTimeout(this._renderTimeout);
+
+ queue[node.id] = Y.merge(queue[node.id], options);
+
+ this._renderTimeout = setTimeout(function () {
+ self._processRenderQueue();
+ }, 15);
+
+ return this;
+ },
+
+ /**
+ Setter for the `lazyRender` attribute.
+
+ Just caches the value in a property for faster lookups.
+
+ @method _setLazyRender
+ @return {Boolean} Value.
+ @protected
+ **/
+ _setLazyRender: function (value) {
+ /*jshint boss:true */
+ return this._lazyRender = value;
+ },
+
+ _syncNodeOpenState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ if (node.isOpen()) {
+ htmlNode
+ .addClass(this.classNames.open)
+ .set('aria-expanded', true);
+ } else {
+ htmlNode
+ .removeClass(this.classNames.open)
+ .set('aria-expanded', false);
+ }
+ },
+
+ _syncNodeSelectedState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ var multiSelect = this.get('multiSelect');
+
+ if (node.isSelected()) {
+ htmlNode.addClass(this.classNames.selected);
+
+ if (multiSelect) {
+ // It's only necessary to set aria-selected when multi-select is
+ // enabled and focus can't be used to track the selection state.
+ htmlNode.set('aria-selected', true);
+ } else {
+ htmlNode.set('tabIndex', 0);
+ }
+ } else {
+ htmlNode
+ .removeClass(this.classNames.selected)
+ .removeAttribute('tabIndex');
+
+ if (multiSelect) {
+ htmlNode.set('aria-selected', false);
+ }
+ }
+ },
+
+ // -- Protected Event Handlers ---------------------------------------------
+
+ _afterAdd: function (e) {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ var parent = e.parent,
+ parentIsRoot = parent.isRoot(),
+ treeNode = e.node,
+
+ htmlChildren,
+ htmlParent;
+
+ if (parentIsRoot) {
+ htmlChildren = this._childrenNode;
+ } else {
+ htmlParent = this.getHTMLNode(parent),
+ htmlChildren = htmlParent && htmlParent.one('>.' + this.classNames.children);
+ }
+
+ if (htmlChildren) {
+ // Parent's children have already been rendered. Instead of
+ // re-rendering all of them, just render the new node and insert it
+ // at the correct position.
+ htmlChildren.insert(this.renderNode(treeNode, {
+ renderChildren: !this._lazyRender || treeNode.isOpen()
+ }), e.index);
+
+ // Schedule the parent node to be re-rendered in order to update its
+ // state. This is done asynchronously and throttled in order to
+ // avoid re-rendering the parent many times if multiple children are
+ // added in quick succession.
+ if (!parentIsRoot) {
+ this._queueRender(parent);
+ }
+ } else if (!parentIsRoot) {
+ // Either the parent hasn't been rendered yet, or its children
+ // haven't been rendered yet. Schedule it to be rendered. This is
+ // done asynchronously and throttled in order to avoid re-rendering
+ // the parent many times if multiple children are added in quick
+ // succession.
+ this._queueRender(parent, {renderChildren: true});
+ }
+ },
+
+ _afterClear: function () {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ clearTimeout(this._renderTimeout);
+ this._renderQueue = {};
+
+ delete this._childrenNode;
+ this.rendered = false;
+
+ this.get('container').empty();
+ this.render();
+ },
+
+ _afterClose: function (e) {
+ if (this.rendered) {
+ this._syncNodeOpenState(e.node);
+ }
+ },
+
+ _afterOpen: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ htmlNode = this.getHTMLNode(treeNode);
+
+ // If this node's children haven't been rendered yet, render them.
+ if (!treeNode.state.renderedChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ },
+
+ _afterRemove: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ parent = e.parent;
+
+ // If this node is in the render queue, remove it from the queue.
+ if (this._renderQueue[treeNode.id]) {
+ delete this._renderQueue[treeNode.id];
+ }
+
+ // Remove DOM nodes associated with this node and any of its
+ // descendants, and mark all nodes as unrendered so that they'll be
+ // re-rendered if they're reinserted in the tree.
+ var htmlNode = this.getHTMLNode(treeNode);
+
+ if (htmlNode) {
+ htmlNode
+ .empty()
+ .remove(true);
+
+ treeNode._htmlNode = null;
+ }
+
+ if (!treeNode.state.destroyed) {
+ treeNode.traverse(function (node) {
+ node._htmlNode = null;
+ node.state.rendered = false;
+ node.state.renderedChildren = false;
+ });
+ }
+
+ // Re-render the parent to update its state if this was its last child.
+ if (parent && !parent.hasChildren()) {
+ this.renderNode(parent);
+ }
+ },
+
+ _afterSelect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _afterTreeViewMultiSelectChange: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var container = this.get('container'),
+ rootList = container.one('> .' + this.classNames.children),
+ htmlNodes = container.all('.' + this.classNames.node);
+
+ if (e.newVal) {
+ rootList.set('aria-multiselectable', true);
+ htmlNodes.set('aria-selected', false);
+ } else {
+ // When multiselect is disabled, aria-selected must not be set on
+ // any nodes, since focus is used to indicate selection.
+ rootList.removeAttribute('aria-multiselectable');
+ htmlNodes.removeAttribute('aria-selected');
+ }
+ },
+
+ _afterUnselect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _onIndicatorClick: function (e) {
+ var rowNode = e.currentTarget.ancestor('.' + this.classNames.row);
+
+ // Indicator clicks shouldn't toggle selection state, so don't allow
+ // this event to propagate to the _onRowClick() handler.
+ e.stopImmediatePropagation();
+
+ this.getNodeById(rowNode.getData('node-id')).toggleOpen();
+ },
+
+ _onMouseDown: function (e) {
+ // This prevents the tree from momentarily grabbing focus before focus
+ // is set on a node.
+ e.preventDefault();
+ },
+
+ _onRowClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ var node = this.getNodeById(e.currentTarget.getData('node-id'));
+
+ if (this.get('multiSelect')) {
+ node[node.isSelected() ? 'unselect' : 'select']();
+ } else {
+ node.select();
+ }
+ },
+
+ _onRowDoubleClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ this.getNodeById(e.currentTarget.getData('node-id')).toggleOpen();
+ }
+}, {
+ ATTRS: {
+ /**
+ When `true`, a node's children won't be rendered until the first time
+ that node is opened.
+
+ This can significantly speed up the time it takes to render a large
+ tree, but might not make sense if you're using CSS that doesn't hide the
+ contents of closed nodes.
+
+ @attribute {Boolean} lazyRender
+ @default true
+ **/
+ lazyRender: {
+ lazyAdd: false, // to ensure that the setter runs on init
+ setter : '_setLazyRender',
+ value : true
+ }
+ }
+});
+
+Y.TreeView = Y.mix(TreeView, Y.TreeView);
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base-build",
+ "classnamemanager",
+ "template-micro",
+ "tree",
+ "tree-labelable",
+ "tree-openable",
+ "tree-selectable",
+ "view"
+ ],
+ "skinnable": true
+});
+YUI.add('moodle-mod_scorm-treeview-sortable', function (Y, NAME) {
+
+Y.use('gallery-sm-treeview-sortable');
+
+
+}, '@VERSION@', {"requires": ["moodle-mod_scorm-treeview", "tree-sortable"]});
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/gallery-sm-treeview-core.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/gallery-sm-treeview-core.css
new file mode 100644
index 00000000000..f7c7ae1384b
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/gallery-sm-treeview-core.css
@@ -0,0 +1,65 @@
+.yui3-treeview,
+.yui3-treeview * {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+.yui3-treeview-children {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+/* This ensures that the root node is always "open". */
+.yui3-treeview-children .yui3-treeview-children { display: none; }
+
+.yui3-treeview-indicator {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ visibility: hidden;
+ top: 0;
+ width: 20px;
+}
+
+.yui3-treeview-indicator s {
+ border: 5px solid transparent;
+ border-left-color: #afafaf;
+ display: inline-block;
+ *display: none; /* Hide from IE 6-7 */
+ height: 5px;
+ left: 6px;
+ position: relative;
+ top: -1px;
+ vertical-align: middle;
+ width: 5px;
+}
+
+.yui3-treeview-label { white-space: pre; }
+.yui3-treeview-node .yui3-treeview-children { margin-left: 16px; }
+
+.yui3-treeview-row {
+ cursor: pointer;
+ overflow: hidden;
+ padding: 0 4px 0 20px;
+ position: relative;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+.yui3-treeview-can-have-children > .yui3-treeview-row > .yui3-treeview-indicator { visibility: visible; }
+
+.yui3-treeview-open > .yui3-treeview-row > .yui3-treeview-indicator s {
+ border-left-color: transparent;
+ border-top-color: #afafaf;
+ left: 4px;
+ top: 2px;
+}
+
+.yui3-treeview-open > .yui3-treeview-children { display: block; }
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/moodle-mod_scorm-treeview-core.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/moodle-mod_scorm-treeview-core.css
new file mode 100644
index 00000000000..f7c7ae1384b
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/moodle-mod_scorm-treeview-core.css
@@ -0,0 +1,65 @@
+.yui3-treeview,
+.yui3-treeview * {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+.yui3-treeview-children {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+/* This ensures that the root node is always "open". */
+.yui3-treeview-children .yui3-treeview-children { display: none; }
+
+.yui3-treeview-indicator {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ visibility: hidden;
+ top: 0;
+ width: 20px;
+}
+
+.yui3-treeview-indicator s {
+ border: 5px solid transparent;
+ border-left-color: #afafaf;
+ display: inline-block;
+ *display: none; /* Hide from IE 6-7 */
+ height: 5px;
+ left: 6px;
+ position: relative;
+ top: -1px;
+ vertical-align: middle;
+ width: 5px;
+}
+
+.yui3-treeview-label { white-space: pre; }
+.yui3-treeview-node .yui3-treeview-children { margin-left: 16px; }
+
+.yui3-treeview-row {
+ cursor: pointer;
+ overflow: hidden;
+ padding: 0 4px 0 20px;
+ position: relative;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+.yui3-treeview-can-have-children > .yui3-treeview-row > .yui3-treeview-indicator { visibility: visible; }
+
+.yui3-treeview-open > .yui3-treeview-row > .yui3-treeview-indicator s {
+ border-left-color: transparent;
+ border-top-color: #afafaf;
+ left: 4px;
+ top: 2px;
+}
+
+.yui3-treeview-open > .yui3-treeview-children { display: block; }
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/folder.png b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/folder.png
new file mode 100644
index 00000000000..76d9d12b57d
Binary files /dev/null and b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/folder.png differ
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/folder@2x.png b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/folder@2x.png
new file mode 100644
index 00000000000..c18674e2608
Binary files /dev/null and b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/folder@2x.png differ
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview-skin-2x.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview-skin-2x.css
new file mode 100644
index 00000000000..a4b607e1525
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview-skin-2x.css
@@ -0,0 +1,15 @@
+/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/
+
+.yui3-skin-sam .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);
+ background-size: contain;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+
+.yui3-skin-sam .yui3-treeview-has-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=);
+}
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview-skin.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview-skin.css
new file mode 100644
index 00000000000..5c4395dac2d
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview-skin.css
@@ -0,0 +1,75 @@
+/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/
+
+.yui3-skin-sam .yui3-treeview-row > .yui3-treeview-indicator:after { content: ''; }
+
+.yui3-skin-sam .yui3-treeview-label,
+.yui3-skin-sam .yui3-treeview-row {
+ line-height: 1.6;
+}
+
+/* Page icon for all nodes by default. */
+.yui3-skin-sam .yui3-treeview-icon {
+ background: left center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4jY2Sv2rDMBDGT3JKl/oB2kdI3sJ7lwx9hvYZQkkIDXQuhWYOeCvdS6DP4D3dstsUZArWH/dOsRQnlk0F8onT3U/fJ2tU1zXQuFssbjFcw/D4MsZ8G63hY7WyiZHbwY2bzXy+rnBT0jQGNMLdAVmWwWa7vcclw7lzfUeA1sxQk5SgEVBhrJQChTmCCCHgfblcT2ezh6ZldwLAJkZojt+LKDqKbiB5nsNrmkIyHr+9pOkj7jydK+DUzC0GOpAkSaySOI7hM8uKjgVFClqAEISUuNoOQCvlLbTHOYQOodoQ4GCBc/olvZDoAOBdC5j0CgYgEUbVA7B3YO+BEj2QCPMqaEFKr4Da+iDWAtaGLZAC/FVDENbUhi04BQMQygct/JalXbs76IOwVu0J4KcoLr3M5kHRy2NNtFCEMJztWg8ohdhfTSbP8I+BkL1b/wF76M1c9BEbZwAAAABJRU5ErkJggg==);
+ float: left;
+ height: 1.6em; /* should match the line-height of the row and label */
+ min-height: 16px; /* to prevent the icon from being clipped */
+ width: 16px;
+}
+
+.yui3-skin-sam .yui3-treeview-label { padding-left: 6px; }
+.yui3-skin-sam .yui3-treeview-node { outline: none; }
+.yui3-skin-sam .yui3-treeview-node .yui3-treeview-children { margin-left: 20px; }
+
+.yui3-skin-sam .yui3-treeview-row {
+ background-image: -moz-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -ms-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -o-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ border: 1px solid transparent;
+ border-radius: 2px;
+ _border: none;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+
+/* Light blue background for hovered nodes, except on touch devices. */
+.yui3-skin-sam .yui3-treeview-notouch .yui3-treeview-row:hover {
+ background-color: #F0F6FE;
+ border-color: #B6D4FC;
+}
+
+/* Use a folder icon for nodes that can have children. */
+.yui3-skin-sam .yui3-treeview-can-have-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4T63TsUrDUBTG8Y+TUhfnIhQR6tC8i4uC+BBFKVGETiLUoUPQRfENXARRfApfwCVLRSlFsMTSapsmOd6vuLRJoLEOvyU3/8PhwhVVxTLE2m4g007DNpwMNv8RAHvGUSrF1v1Z/TwNz9iK2WPjsXXo3jUPZvDbVW3Xbbdf0Ov1E25P9122HCDj8QTD4fcM3++jXF6HiKDb7SR4nge2ZgCsKIqRxvc/USqtoVLZTKhWbbCdbhCGMbIMBl+pRqPgd4NYrfloUWy5QSEMFX/B9h82iLUwf3mLYrv8AJ1EqyIWisWVXNhMW3y8vtVOLm7qzctc2LAVfX5o6dO1Yxzn5LAV8yQD493o5MQm+AEX3F8P8w0R8QAAAABJRU5ErkJggg==);
+}
+
+/* Gray background for selected, unfocused nodes. */
+.yui3-skin-sam .yui3-treeview-selected > .yui3-treeview-row {
+ background-color: #E6E6E6;
+ border-color: #D9D9D9;
+}
+
+/* Blue background for selected, focused nodes and selected, hovered nodes,
+ as well as selected unfocused nodes when multiselect is enabled. */
+.yui3-skin-sam .yui3-treeview-selected:focus > .yui3-treeview-row,
+.yui3-skin-sam .yui3-treeview-children[aria-multiselectable="true"] .yui3-treeview-selected > .yui3-treeview-row,
+.yui3-skin-sam .yui3-treeview-selected > .yui3-treeview-row:hover {
+ background-color: #C9E0FC;
+ border-color: #7DA2CE;
+}
+
+/* -- High DPI styles ------------------------------------------------------- */
+@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
+ .yui3-skin-sam .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);
+ background-size: contain;
+ }
+
+ .yui3-skin-sam .yui3-treeview-has-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=);
+ }
+}
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview.css
new file mode 100644
index 00000000000..76dab7fe8cd
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/gallery-sm-treeview.css
@@ -0,0 +1,4 @@
+.yui3-treeview,.yui3-treeview *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.yui3-treeview-children{margin:0;padding:0;list-style:none}.yui3-treeview-children .yui3-treeview-children{display:none}.yui3-treeview-indicator{bottom:0;left:0;position:absolute;visibility:hidden;top:0;width:20px}.yui3-treeview-indicator s{border:5px solid transparent;border-left-color:#afafaf;display:inline-block;*display:none;height:5px;left:6px;position:relative;top:-1px;vertical-align:middle;width:5px}.yui3-treeview-label{white-space:pre}.yui3-treeview-node .yui3-treeview-children{margin-left:16px}.yui3-treeview-row{cursor:pointer;overflow:hidden;padding:0 4px 0 20px;position:relative;text-overflow:ellipsis;white-space:nowrap}.yui3-treeview-can-have-children>.yui3-treeview-row>.yui3-treeview-indicator{visibility:visible}.yui3-treeview-open>.yui3-treeview-row>.yui3-treeview-indicator s{border-left-color:transparent;border-top-color:#afafaf;left:4px;top:2px}.yui3-treeview-open>.yui3-treeview-children{display:block}/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/.yui3-skin-sam .yui3-treeview-row>.yui3-treeview-indicator:after{content:''}.yui3-skin-sam .yui3-treeview-label,.yui3-skin-sam .yui3-treeview-row{line-height:1.6}.yui3-skin-sam .yui3-treeview-icon{background:left center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4jY2Sv2rDMBDGT3JKl/oB2kdI3sJ7lwx9hvYZQkkIDXQuhWYOeCvdS6DP4D3dstsUZArWH/dOsRQnlk0F8onT3U/fJ2tU1zXQuFssbjFcw/D4MsZ8G63hY7WyiZHbwY2bzXy+rnBT0jQGNMLdAVmWwWa7vcclw7lzfUeA1sxQk5SgEVBhrJQChTmCCCHgfblcT2ezh6ZldwLAJkZojt+LKDqKbiB5nsNrmkIyHr+9pOkj7jydK+DUzC0GOpAkSaySOI7hM8uKjgVFClqAEISUuNoOQCvlLbTHOYQOodoQ4GCBc/olvZDoAOBdC5j0CgYgEUbVA7B3YO+BEj2QCPMqaEFKr4Da+iDWAtaGLZAC/FVDENbUhi04BQMQygct/JalXbs76IOwVu0J4KcoLr3M5kHRy2NNtFCEMJztWg8ohdhfTSbP8I+BkL1b/wF76M1c9BEbZwAAAABJRU5ErkJggg==);float:left;height:1.6em;min-height:16px;width:16px}.yui3-skin-sam .yui3-treeview-label{padding-left:6px}.yui3-skin-sam .yui3-treeview-node{outline:0}.yui3-skin-sam .yui3-treeview-node .yui3-treeview-children{margin-left:20px}.yui3-skin-sam .yui3-treeview-row{background-image:-moz-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-ms-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-o-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-webkit-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));border:1px solid transparent;border-radius:2px;_border:0}.yui3-skin-sam .yui3-treeview-notouch .yui3-treeview-row:hover{background-color:#f0f6fe;border-color:#b6d4fc}.yui3-skin-sam .yui3-treeview-can-have-children>.yui3-treeview-row>.yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4T63TsUrDUBTG8Y+TUhfnIhQR6tC8i4uC+BBFKVGETiLUoUPQRfENXARRfApfwCVLRSlFsMTSapsmOd6vuLRJoLEOvyU3/8PhwhVVxTLE2m4g007DNpwMNv8RAHvGUSrF1v1Z/TwNz9iK2WPjsXXo3jUPZvDbVW3Xbbdf0Ov1E25P9122HCDj8QTD4fcM3++jXF6HiKDb7SR4nge2ZgCsKIqRxvc/USqtoVLZTKhWbbCdbhCGMbIMBl+pRqPgd4NYrfloUWy5QSEMFX/B9h82iLUwf3mLYrv8AJ1EqyIWisWVXNhMW3y8vtVOLm7qzctc2LAVfX5o6dO1Yxzn5LAV8yQD493o5MQm+AEX3F8P8w0R8QAAAABJRU5ErkJggg==)}.yui3-skin-sam .yui3-treeview-selected>.yui3-treeview-row{background-color:#e6e6e6;border-color:#d9d9d9}.yui3-skin-sam .yui3-treeview-selected:focus>.yui3-treeview-row,.yui3-skin-sam .yui3-treeview-children[aria-multiselectable="true"] .yui3-treeview-selected>.yui3-treeview-row,.yui3-skin-sam .yui3-treeview-selected>.yui3-treeview-row:hover{background-color:#c9e0fc;border-color:#7da2ce}@media(-webkit-min-device-pixel-ratio:1.5),(min-resolution:144dpi){.yui3-skin-sam .yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);background-size:contain}.yui3-skin-sam .yui3-treeview-has-children>.yui3-treeview-row>.yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=)}}#yui3-css-stamp.skin-sam-gallery-sm-treeview{display:none}
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/item.png b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/item.png
new file mode 100644
index 00000000000..33bcadaaf51
Binary files /dev/null and b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/item.png differ
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/item@2x.png b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/item@2x.png
new file mode 100644
index 00000000000..768e57985d6
Binary files /dev/null and b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/item@2x.png differ
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/moodle-mod_scorm-treeview-skin.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/moodle-mod_scorm-treeview-skin.css
new file mode 100644
index 00000000000..5c4395dac2d
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/moodle-mod_scorm-treeview-skin.css
@@ -0,0 +1,75 @@
+/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/
+
+.yui3-skin-sam .yui3-treeview-row > .yui3-treeview-indicator:after { content: ''; }
+
+.yui3-skin-sam .yui3-treeview-label,
+.yui3-skin-sam .yui3-treeview-row {
+ line-height: 1.6;
+}
+
+/* Page icon for all nodes by default. */
+.yui3-skin-sam .yui3-treeview-icon {
+ background: left center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4jY2Sv2rDMBDGT3JKl/oB2kdI3sJ7lwx9hvYZQkkIDXQuhWYOeCvdS6DP4D3dstsUZArWH/dOsRQnlk0F8onT3U/fJ2tU1zXQuFssbjFcw/D4MsZ8G63hY7WyiZHbwY2bzXy+rnBT0jQGNMLdAVmWwWa7vcclw7lzfUeA1sxQk5SgEVBhrJQChTmCCCHgfblcT2ezh6ZldwLAJkZojt+LKDqKbiB5nsNrmkIyHr+9pOkj7jydK+DUzC0GOpAkSaySOI7hM8uKjgVFClqAEISUuNoOQCvlLbTHOYQOodoQ4GCBc/olvZDoAOBdC5j0CgYgEUbVA7B3YO+BEj2QCPMqaEFKr4Da+iDWAtaGLZAC/FVDENbUhi04BQMQygct/JalXbs76IOwVu0J4KcoLr3M5kHRy2NNtFCEMJztWg8ohdhfTSbP8I+BkL1b/wF76M1c9BEbZwAAAABJRU5ErkJggg==);
+ float: left;
+ height: 1.6em; /* should match the line-height of the row and label */
+ min-height: 16px; /* to prevent the icon from being clipped */
+ width: 16px;
+}
+
+.yui3-skin-sam .yui3-treeview-label { padding-left: 6px; }
+.yui3-skin-sam .yui3-treeview-node { outline: none; }
+.yui3-skin-sam .yui3-treeview-node .yui3-treeview-children { margin-left: 20px; }
+
+.yui3-skin-sam .yui3-treeview-row {
+ background-image: -moz-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -ms-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -o-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ border: 1px solid transparent;
+ border-radius: 2px;
+ _border: none;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+
+/* Light blue background for hovered nodes, except on touch devices. */
+.yui3-skin-sam .yui3-treeview-notouch .yui3-treeview-row:hover {
+ background-color: #F0F6FE;
+ border-color: #B6D4FC;
+}
+
+/* Use a folder icon for nodes that can have children. */
+.yui3-skin-sam .yui3-treeview-can-have-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4T63TsUrDUBTG8Y+TUhfnIhQR6tC8i4uC+BBFKVGETiLUoUPQRfENXARRfApfwCVLRSlFsMTSapsmOd6vuLRJoLEOvyU3/8PhwhVVxTLE2m4g007DNpwMNv8RAHvGUSrF1v1Z/TwNz9iK2WPjsXXo3jUPZvDbVW3Xbbdf0Ov1E25P9122HCDj8QTD4fcM3++jXF6HiKDb7SR4nge2ZgCsKIqRxvc/USqtoVLZTKhWbbCdbhCGMbIMBl+pRqPgd4NYrfloUWy5QSEMFX/B9h82iLUwf3mLYrv8AJ1EqyIWisWVXNhMW3y8vtVOLm7qzctc2LAVfX5o6dO1Yxzn5LAV8yQD493o5MQm+AEX3F8P8w0R8QAAAABJRU5ErkJggg==);
+}
+
+/* Gray background for selected, unfocused nodes. */
+.yui3-skin-sam .yui3-treeview-selected > .yui3-treeview-row {
+ background-color: #E6E6E6;
+ border-color: #D9D9D9;
+}
+
+/* Blue background for selected, focused nodes and selected, hovered nodes,
+ as well as selected unfocused nodes when multiselect is enabled. */
+.yui3-skin-sam .yui3-treeview-selected:focus > .yui3-treeview-row,
+.yui3-skin-sam .yui3-treeview-children[aria-multiselectable="true"] .yui3-treeview-selected > .yui3-treeview-row,
+.yui3-skin-sam .yui3-treeview-selected > .yui3-treeview-row:hover {
+ background-color: #C9E0FC;
+ border-color: #7DA2CE;
+}
+
+/* -- High DPI styles ------------------------------------------------------- */
+@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
+ .yui3-skin-sam .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);
+ background-size: contain;
+ }
+
+ .yui3-skin-sam .yui3-treeview-has-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=);
+ }
+}
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/moodle-mod_scorm-treeview.css b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/moodle-mod_scorm-treeview.css
new file mode 100644
index 00000000000..76dab7fe8cd
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/assets/skins/sam/moodle-mod_scorm-treeview.css
@@ -0,0 +1,4 @@
+.yui3-treeview,.yui3-treeview *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.yui3-treeview-children{margin:0;padding:0;list-style:none}.yui3-treeview-children .yui3-treeview-children{display:none}.yui3-treeview-indicator{bottom:0;left:0;position:absolute;visibility:hidden;top:0;width:20px}.yui3-treeview-indicator s{border:5px solid transparent;border-left-color:#afafaf;display:inline-block;*display:none;height:5px;left:6px;position:relative;top:-1px;vertical-align:middle;width:5px}.yui3-treeview-label{white-space:pre}.yui3-treeview-node .yui3-treeview-children{margin-left:16px}.yui3-treeview-row{cursor:pointer;overflow:hidden;padding:0 4px 0 20px;position:relative;text-overflow:ellipsis;white-space:nowrap}.yui3-treeview-can-have-children>.yui3-treeview-row>.yui3-treeview-indicator{visibility:visible}.yui3-treeview-open>.yui3-treeview-row>.yui3-treeview-indicator s{border-left-color:transparent;border-top-color:#afafaf;left:4px;top:2px}.yui3-treeview-open>.yui3-treeview-children{display:block}/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/.yui3-skin-sam .yui3-treeview-row>.yui3-treeview-indicator:after{content:''}.yui3-skin-sam .yui3-treeview-label,.yui3-skin-sam .yui3-treeview-row{line-height:1.6}.yui3-skin-sam .yui3-treeview-icon{background:left center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4jY2Sv2rDMBDGT3JKl/oB2kdI3sJ7lwx9hvYZQkkIDXQuhWYOeCvdS6DP4D3dstsUZArWH/dOsRQnlk0F8onT3U/fJ2tU1zXQuFssbjFcw/D4MsZ8G63hY7WyiZHbwY2bzXy+rnBT0jQGNMLdAVmWwWa7vcclw7lzfUeA1sxQk5SgEVBhrJQChTmCCCHgfblcT2ezh6ZldwLAJkZojt+LKDqKbiB5nsNrmkIyHr+9pOkj7jydK+DUzC0GOpAkSaySOI7hM8uKjgVFClqAEISUuNoOQCvlLbTHOYQOodoQ4GCBc/olvZDoAOBdC5j0CgYgEUbVA7B3YO+BEj2QCPMqaEFKr4Da+iDWAtaGLZAC/FVDENbUhi04BQMQygct/JalXbs76IOwVu0J4KcoLr3M5kHRy2NNtFCEMJztWg8ohdhfTSbP8I+BkL1b/wF76M1c9BEbZwAAAABJRU5ErkJggg==);float:left;height:1.6em;min-height:16px;width:16px}.yui3-skin-sam .yui3-treeview-label{padding-left:6px}.yui3-skin-sam .yui3-treeview-node{outline:0}.yui3-skin-sam .yui3-treeview-node .yui3-treeview-children{margin-left:20px}.yui3-skin-sam .yui3-treeview-row{background-image:-moz-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-ms-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-o-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-webkit-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));border:1px solid transparent;border-radius:2px;_border:0}.yui3-skin-sam .yui3-treeview-notouch .yui3-treeview-row:hover{background-color:#f0f6fe;border-color:#b6d4fc}.yui3-skin-sam .yui3-treeview-can-have-children>.yui3-treeview-row>.yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4T63TsUrDUBTG8Y+TUhfnIhQR6tC8i4uC+BBFKVGETiLUoUPQRfENXARRfApfwCVLRSlFsMTSapsmOd6vuLRJoLEOvyU3/8PhwhVVxTLE2m4g007DNpwMNv8RAHvGUSrF1v1Z/TwNz9iK2WPjsXXo3jUPZvDbVW3Xbbdf0Ov1E25P9122HCDj8QTD4fcM3++jXF6HiKDb7SR4nge2ZgCsKIqRxvc/USqtoVLZTKhWbbCdbhCGMbIMBl+pRqPgd4NYrfloUWy5QSEMFX/B9h82iLUwf3mLYrv8AJ1EqyIWisWVXNhMW3y8vtVOLm7qzctc2LAVfX5o6dO1Yxzn5LAV8yQD493o5MQm+AEX3F8P8w0R8QAAAABJRU5ErkJggg==)}.yui3-skin-sam .yui3-treeview-selected>.yui3-treeview-row{background-color:#e6e6e6;border-color:#d9d9d9}.yui3-skin-sam .yui3-treeview-selected:focus>.yui3-treeview-row,.yui3-skin-sam .yui3-treeview-children[aria-multiselectable="true"] .yui3-treeview-selected>.yui3-treeview-row,.yui3-skin-sam .yui3-treeview-selected>.yui3-treeview-row:hover{background-color:#c9e0fc;border-color:#7da2ce}@media(-webkit-min-device-pixel-ratio:1.5),(min-resolution:144dpi){.yui3-skin-sam .yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);background-size:contain}.yui3-skin-sam .yui3-treeview-has-children>.yui3-treeview-row>.yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=)}}#yui3-css-stamp.skin-sam-gallery-sm-treeview{display:none}
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview-debug.js b/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview-debug.js
new file mode 100644
index 00000000000..0f1f26f6d09
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview-debug.js
@@ -0,0 +1,758 @@
+YUI.add('gallery-sm-treeview', function (Y, NAME) {
+
+var Micro = Y.Template.Micro;
+
+Y.namespace('TreeView').Templates = {
+ children: Micro.compile(
+ '' +
+ 'role="tree" tabindex="0"' +
+ '<% } else { %>' +
+ 'role="group"' +
+ '<% } %>' +
+
+ '>
'
+ ),
+
+ node: Micro.compile(
+ '" role="treeitem" aria-labelled-by="<%= data.node.id %>-label">' +
+ '' +
+ '' +
+ '' +
+ '<%== data.node.label %>' +
+ '
' +
+ ''
+ )
+};
+/*jshint expr:true, onevar:false */
+
+/**
+Provides the `Y.TreeView` widget.
+
+@module gallery-sm-treeview
+@main gallery-sm-treeview
+**/
+
+/**
+TreeView widget.
+
+@class TreeView
+@constructor
+@extends View
+@uses Tree
+@uses Tree.Labelable
+@uses Tree.Openable
+@uses Tree.Selectable
+**/
+
+var getClassName = Y.ClassNameManager.getClassName,
+
+TreeView = Y.Base.create('treeView', Y.View, [
+ Y.Tree,
+ Y.Tree.Labelable,
+ Y.Tree.Openable,
+ Y.Tree.Selectable
+], {
+ // -- Public Properties ----------------------------------------------------
+
+ /**
+ CSS class names used by this treeview.
+
+ @property {Object} classNames
+ @param {String} canHaveChildren Class name indicating that a tree node can
+ contain child nodes (whether or not it actually does).
+ @param {String} children Class name for a list of child nodes.
+ @param {String} hasChildren Class name indicating that a tree node has one
+ or more child nodes.
+ @param {String} icon Class name for a tree node's icon.
+ @param {String} indicator Class name for an open/closed indicator.
+ @param {String} label Class name for a tree node's user-visible label.
+ @param {String} node Class name for a tree node item.
+ @param {String} noTouch Class name added to the TreeView container when not
+ using a touchscreen device.
+ @param {String} open Class name indicating that a tree node is open.
+ @param {String} row Class name for a row container encompassing the
+ indicator and label within a tree node.
+ @param {String} selected Class name for a tree node that's selected.
+ @param {String} touch Class name added to the TreeView container when using
+ a touchscreen device.
+ @param {String} treeview Class name for the TreeView container.
+ **/
+ classNames: {
+ canHaveChildren: getClassName('treeview-can-have-children'),
+ children : getClassName('treeview-children'),
+ hasChildren : getClassName('treeview-has-children'),
+ icon : getClassName('treeview-icon'),
+ indicator : getClassName('treeview-indicator'),
+ label : getClassName('treeview-label'),
+ node : getClassName('treeview-node'),
+ noTouch : getClassName('treeview-notouch'),
+ open : getClassName('treeview-open'),
+ row : getClassName('treeview-row'),
+ selected : getClassName('treeview-selected'),
+ touch : getClassName('treeview-touch'),
+ treeview : getClassName('treeview')
+ },
+
+ /**
+ Whether or not this TreeView has been rendered.
+
+ @property {Boolean} rendered
+ @default false
+ **/
+ rendered: false,
+
+ /**
+ Default templates used to render this TreeView.
+
+ @property {Object} templates
+ **/
+ templates: Y.TreeView.Templates,
+
+ // -- Protected Properties -------------------------------------------------
+
+ /**
+ Simple way to type-check that this is a TreeView instance.
+
+ @property {Boolean} _isYUITreeView
+ @default true
+ @protected
+ **/
+ _isYUITreeView: true,
+
+ /**
+ Cached value of the `lazyRender` attribute.
+
+ @property {Boolean} _lazyRender
+ @protected
+ **/
+
+ // -- Lifecycle Methods ----------------------------------------------------
+
+ initializer: function (config) {
+ if (config && config.templates) {
+ this.templates = Y.merge(this.templates, config.templates);
+ }
+
+ this._renderQueue = {};
+ this._attachTreeViewEvents();
+ },
+
+ destructor: function () {
+ clearTimeout(this._renderTimeout);
+ this._detachTreeViewEvents();
+
+ this._renderQueue = null;
+ },
+
+ // -- Public Methods -------------------------------------------------------
+
+ destroyNode: function (node, options) {
+ node._htmlNode = null;
+ return Y.Tree.prototype.destroyNode.call(this, node, options);
+ },
+
+ /**
+ Returns the HTML node (as a `Y.Node` instance) associated with the specified
+ `Tree.Node` instance, if any.
+
+ @method getHTMLNode
+ @param {Tree.Node} treeNode Tree node.
+ @return {Node} `Y.Node` instance associated with the given tree node, or
+ `undefined` if one was not found.
+ **/
+ getHTMLNode: function (treeNode) {
+ if (!treeNode._htmlNode) {
+ treeNode._htmlNode = this.get('container').one('#' + treeNode.id);
+ }
+
+ return treeNode._htmlNode;
+ },
+
+ /**
+ Renders this TreeView into its container.
+
+ If the container hasn't already been added to the current document, it will
+ be appended to the `` element.
+
+ @method render
+ @chainable
+ **/
+ render: function () {
+ var container = this.get('container'),
+ isTouchDevice = 'ontouchstart' in Y.config.win;
+
+ container.addClass(this.classNames.treeview);
+ container.addClass(this.classNames[isTouchDevice ? 'touch' : 'noTouch']);
+
+ this._childrenNode = this.renderChildren(this.rootNode, {
+ container: container
+ });
+
+ if (!container.inDoc()) {
+ Y.one('body').append(container);
+ }
+
+ this.rendered = true;
+
+ return this;
+ },
+
+ /**
+ Renders the children of the specified tree node.
+
+ If a container is specified, it will be assumed to be an existing rendered
+ tree node, and the children will be rendered (or re-rendered) inside it.
+
+ @method renderChildren
+ @param {Tree.Node} treeNode Tree node whose children should be rendered.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container into
+ which the children should be rendered. If the container already
+ contains rendered children, they will be re-rendered in place.
+
+ @param {Boolean} [options.force=false] If `true`, children will be
+ re-rendered from scratch even if they've already been rendered.
+
+ @return {Node} `Y.Node` instance containing the rendered children.
+ **/
+ renderChildren: function (treeNode, options) {
+ options || (options = {});
+
+ var container = options.container,
+ childrenNode = container && container.one('>.' + this.classNames.children),
+ lazyRender = this._lazyRender;
+
+ if (childrenNode && options.force) {
+ childrenNode.remove(true);
+ childrenNode = null;
+ }
+
+ if (!childrenNode) {
+ childrenNode = Y.Node.create(this.templates.children({
+ classNames: this.classNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ if (treeNode.hasChildren()) {
+ childrenNode.set('aria-expanded', treeNode.isOpen());
+
+ for (var i = 0, len = treeNode.children.length; i < len; i++) {
+ var child = treeNode.children[i];
+
+ this.renderNode(child, {
+ container : childrenNode,
+ renderChildren: !lazyRender || child.isOpen()
+ });
+ }
+ }
+
+ // Keep track of whether or not this node's children have been rendered
+ // so we'll know whether we need to render them later if the node is
+ // opened.
+ treeNode.state.renderedChildren = true;
+
+ if (container) {
+ container.append(childrenNode);
+ }
+
+ return childrenNode;
+ },
+
+ /**
+ Renders the specified tree node and its children (if any).
+
+ If a container is specified, the rendered node will be appended to it.
+
+ @method renderNode
+ @param {Tree.Node} treeNode Tree node to render.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container to
+ which the rendered tree node should be appended.
+
+ @param {Boolean} [options.force=false] If `true`, this node (and its
+ children if `renderChildren` is `true`) will be re-rendered from
+ scratch, even if it's already been rendered.
+
+ @param {Boolean} [options.renderChildren=false] Whether or not to render
+ this node's children.
+
+ @return {Node} `Y.Node` instance of the rendered tree node.
+ **/
+ renderNode: function (treeNode, options) {
+ options || (options = {});
+
+ var classNames = this.classNames,
+ hasChildren = treeNode.hasChildren(),
+ htmlNode = treeNode._htmlNode,
+ oldHtmlNode = options.force && htmlNode,
+ nodeClassNames = {},
+
+ className;
+
+ // Build the hash of CSS classes for this node.
+ nodeClassNames[classNames.node] = true;
+ nodeClassNames[classNames.canHaveChildren] = !!treeNode.canHaveChildren;
+ nodeClassNames[classNames.hasChildren] = hasChildren;
+
+ if (htmlNode && !options.force) {
+ // This node has already been rendered, so we just need to update
+ // the DOM instead of re-rendering it from scratch.
+ htmlNode.one('.' + classNames.label).setHTML(treeNode.label);
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className)) {
+ htmlNode.toggleClass(className, nodeClassNames[className]);
+ }
+ }
+ } else {
+ // This node hasn't been rendered yet or is being forcibly
+ // re-rendered.
+ var enabledClassNames = [];
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className) && nodeClassNames[className]) {
+ enabledClassNames.push(className);
+ }
+ }
+
+ htmlNode = treeNode._htmlNode = Y.Node.create(this.templates.node({
+ classNames : classNames,
+ nodeClassNames: enabledClassNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ this._syncNodeSelectedState(treeNode, htmlNode);
+
+ if (hasChildren) {
+ if (options.renderChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+ } else {
+ // If children were previously rendered but this node no longer has
+ // children, remove the empty child list.
+ var childrenNode = htmlNode.one('>.' + classNames.children);
+
+ if (childrenNode) {
+ childrenNode.remove(true);
+ }
+ }
+
+ treeNode.state.rendered = true;
+
+ if (options.force) {
+ oldHtmlNode.replace(htmlNode);
+ oldHtmlNode.destroy();
+ } else {
+ if (options.container && htmlNode.get('parentNode') !== options.container) {
+ options.container.append(htmlNode);
+ }
+ }
+
+ return htmlNode;
+ },
+
+ // -- Protected Methods ----------------------------------------------------
+
+ _attachTreeViewEvents: function () {
+ this._treeViewEvents || (this._treeViewEvents = []);
+
+ var classNames = this.classNames,
+ container = this.get('container');
+
+ this._treeViewEvents.push(
+ // Custom events.
+ this.after({
+ add : this._afterAdd,
+ clear : this._afterClear,
+ close : this._afterClose,
+ multiSelectChange: this._afterTreeViewMultiSelectChange, // sheesh
+ open : this._afterOpen,
+ remove : this._afterRemove,
+ select : this._afterSelect,
+ unselect : this._afterUnselect
+ }),
+
+ // DOM events.
+ container.on('mousedown', this._onMouseDown, this),
+
+ container.delegate('click', this._onIndicatorClick,
+ '.' + classNames.indicator, this),
+
+ container.delegate('click', this._onRowClick,
+ '.' + classNames.row, this),
+
+ container.delegate('dblclick', this._onRowDoubleClick,
+ '.' + classNames.canHaveChildren + ' > .' + classNames.row, this)
+ );
+ },
+
+ _detachTreeViewEvents: function () {
+ (new Y.EventHandle(this._treeViewEvents)).detach();
+ },
+
+ _processRenderQueue: function () {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ node;
+
+ for (var id in queue) {
+ if (queue.hasOwnProperty(id)) {
+ node = this.getNodeById(id);
+
+ if (node) {
+ this.renderNode(node, queue[id]);
+ }
+ }
+ }
+
+ this._renderQueue = {};
+ },
+
+ _queueRender: function (node, options) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ self = this;
+
+ clearTimeout(this._renderTimeout);
+
+ queue[node.id] = Y.merge(queue[node.id], options);
+
+ this._renderTimeout = setTimeout(function () {
+ self._processRenderQueue();
+ }, 15);
+
+ return this;
+ },
+
+ /**
+ Setter for the `lazyRender` attribute.
+
+ Just caches the value in a property for faster lookups.
+
+ @method _setLazyRender
+ @return {Boolean} Value.
+ @protected
+ **/
+ _setLazyRender: function (value) {
+ /*jshint boss:true */
+ return this._lazyRender = value;
+ },
+
+ _syncNodeOpenState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ if (node.isOpen()) {
+ htmlNode
+ .addClass(this.classNames.open)
+ .set('aria-expanded', true);
+ } else {
+ htmlNode
+ .removeClass(this.classNames.open)
+ .set('aria-expanded', false);
+ }
+ },
+
+ _syncNodeSelectedState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ var multiSelect = this.get('multiSelect');
+
+ if (node.isSelected()) {
+ htmlNode.addClass(this.classNames.selected);
+
+ if (multiSelect) {
+ // It's only necessary to set aria-selected when multi-select is
+ // enabled and focus can't be used to track the selection state.
+ htmlNode.set('aria-selected', true);
+ } else {
+ htmlNode.set('tabIndex', 0);
+ }
+ } else {
+ htmlNode
+ .removeClass(this.classNames.selected)
+ .removeAttribute('tabIndex');
+
+ if (multiSelect) {
+ htmlNode.set('aria-selected', false);
+ }
+ }
+ },
+
+ // -- Protected Event Handlers ---------------------------------------------
+
+ _afterAdd: function (e) {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ var parent = e.parent,
+ parentIsRoot = parent.isRoot(),
+ treeNode = e.node,
+
+ htmlChildren,
+ htmlParent;
+
+ if (parentIsRoot) {
+ htmlChildren = this._childrenNode;
+ } else {
+ htmlParent = this.getHTMLNode(parent),
+ htmlChildren = htmlParent && htmlParent.one('>.' + this.classNames.children);
+ }
+
+ if (htmlChildren) {
+ // Parent's children have already been rendered. Instead of
+ // re-rendering all of them, just render the new node and insert it
+ // at the correct position.
+ htmlChildren.insert(this.renderNode(treeNode, {
+ renderChildren: !this._lazyRender || treeNode.isOpen()
+ }), e.index);
+
+ // Schedule the parent node to be re-rendered in order to update its
+ // state. This is done asynchronously and throttled in order to
+ // avoid re-rendering the parent many times if multiple children are
+ // added in quick succession.
+ if (!parentIsRoot) {
+ this._queueRender(parent);
+ }
+ } else if (!parentIsRoot) {
+ // Either the parent hasn't been rendered yet, or its children
+ // haven't been rendered yet. Schedule it to be rendered. This is
+ // done asynchronously and throttled in order to avoid re-rendering
+ // the parent many times if multiple children are added in quick
+ // succession.
+ this._queueRender(parent, {renderChildren: true});
+ }
+ },
+
+ _afterClear: function () {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ clearTimeout(this._renderTimeout);
+ this._renderQueue = {};
+
+ delete this._childrenNode;
+ this.rendered = false;
+
+ this.get('container').empty();
+ this.render();
+ },
+
+ _afterClose: function (e) {
+ if (this.rendered) {
+ this._syncNodeOpenState(e.node);
+ }
+ },
+
+ _afterOpen: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ htmlNode = this.getHTMLNode(treeNode);
+
+ // If this node's children haven't been rendered yet, render them.
+ if (!treeNode.state.renderedChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ },
+
+ _afterRemove: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ parent = e.parent;
+
+ // If this node is in the render queue, remove it from the queue.
+ if (this._renderQueue[treeNode.id]) {
+ delete this._renderQueue[treeNode.id];
+ }
+
+ // Remove DOM nodes associated with this node and any of its
+ // descendants, and mark all nodes as unrendered so that they'll be
+ // re-rendered if they're reinserted in the tree.
+ var htmlNode = this.getHTMLNode(treeNode);
+
+ if (htmlNode) {
+ htmlNode
+ .empty()
+ .remove(true);
+
+ treeNode._htmlNode = null;
+ }
+
+ if (!treeNode.state.destroyed) {
+ treeNode.traverse(function (node) {
+ node._htmlNode = null;
+ node.state.rendered = false;
+ node.state.renderedChildren = false;
+ });
+ }
+
+ // Re-render the parent to update its state if this was its last child.
+ if (parent && !parent.hasChildren()) {
+ this.renderNode(parent);
+ }
+ },
+
+ _afterSelect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _afterTreeViewMultiSelectChange: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var container = this.get('container'),
+ rootList = container.one('> .' + this.classNames.children),
+ htmlNodes = container.all('.' + this.classNames.node);
+
+ if (e.newVal) {
+ rootList.set('aria-multiselectable', true);
+ htmlNodes.set('aria-selected', false);
+ } else {
+ // When multiselect is disabled, aria-selected must not be set on
+ // any nodes, since focus is used to indicate selection.
+ rootList.removeAttribute('aria-multiselectable');
+ htmlNodes.removeAttribute('aria-selected');
+ }
+ },
+
+ _afterUnselect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _onIndicatorClick: function (e) {
+ var rowNode = e.currentTarget.ancestor('.' + this.classNames.row);
+
+ // Indicator clicks shouldn't toggle selection state, so don't allow
+ // this event to propagate to the _onRowClick() handler.
+ e.stopImmediatePropagation();
+
+ this.getNodeById(rowNode.getData('node-id')).toggleOpen();
+ },
+
+ _onMouseDown: function (e) {
+ // This prevents the tree from momentarily grabbing focus before focus
+ // is set on a node.
+ e.preventDefault();
+ },
+
+ _onRowClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ var node = this.getNodeById(e.currentTarget.getData('node-id'));
+
+ if (this.get('multiSelect')) {
+ node[node.isSelected() ? 'unselect' : 'select']();
+ } else {
+ node.select();
+ }
+ },
+
+ _onRowDoubleClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ this.getNodeById(e.currentTarget.getData('node-id')).toggleOpen();
+ }
+}, {
+ ATTRS: {
+ /**
+ When `true`, a node's children won't be rendered until the first time
+ that node is opened.
+
+ This can significantly speed up the time it takes to render a large
+ tree, but might not make sense if you're using CSS that doesn't hide the
+ contents of closed nodes.
+
+ @attribute {Boolean} lazyRender
+ @default true
+ **/
+ lazyRender: {
+ lazyAdd: false, // to ensure that the setter runs on init
+ setter : '_setLazyRender',
+ value : true
+ }
+ }
+});
+
+Y.TreeView = Y.mix(TreeView, Y.TreeView);
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base-build",
+ "classnamemanager",
+ "template-micro",
+ "tree",
+ "tree-labelable",
+ "tree-openable",
+ "tree-selectable",
+ "view"
+ ],
+ "skinnable": true
+});
+YUI.add('moodle-mod_scorm-treeview', function (Y, NAME) {
+
+Y.use('gallery-sm-treeview');
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base-build",
+ "classnamemanager",
+ "template-micro",
+ "tree",
+ "tree-labelable",
+ "tree-openable",
+ "tree-selectable",
+ "view",
+ "moodle-mod_scorm-treeview-skin"
+ ]
+});
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview-min.js b/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview-min.js
new file mode 100644
index 00000000000..de1fff8ac49
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview-min.js
@@ -0,0 +1,2 @@
+YUI.add("gallery-sm-treeview",function(e,t){var n=e.Template.Micro;e.namespace("TreeView").Templates={children:n.compile('role="tree" tabindex="0"<% } else { %>role="group"<% } %>>
'),node:n.compile('" role="treeitem" aria-labelled-by="<%= data.node.id %>-label"><%== data.node.label %>
')};var r=e.ClassNameManager.getClassName,i=e.Base.create("treeView",e.View,[e.Tree,e.Tree.Labelable,e.Tree.Openable,e.Tree.Selectable],{classNames:{canHaveChildren:r("treeview-can-have-children"),children:r("treeview-children"),hasChildren:r("treeview-has-children"),icon:r("treeview-icon"),indicator:r("treeview-indicator"),label:r("treeview-label"),node:r("treeview-node"),noTouch:r("treeview-notouch"),open:r("treeview-open"),row:r("treeview-row"),selected:r("treeview-selected"),touch:r("treeview-touch"),treeview:r("treeview")},rendered:!1,templates:e.TreeView.Templates,_isYUITreeView:!0,initializer:function(t){t&&t.templates&&(this.templates=e.merge(this.templates,t.templates)),this._renderQueue={},this._attachTreeViewEvents()},destructor:function(){clearTimeout(this._renderTimeout),this._detachTreeViewEvents(),this._renderQueue=null},destroyNode:function(t,n){return t._htmlNode=null,e.Tree.prototype.destroyNode.call(this,t,n)},getHTMLNode:function(e){return e._htmlNode||(e._htmlNode=this.get("container").one("#"+e.id)),e._htmlNode},render:function(){var t=this.get("container"),n="ontouchstart"in e.config.win;return t.addClass(this.classNames.treeview),t.addClass(this.classNames[n?"touch":"noTouch"]),this._childrenNode=this.renderChildren(this.rootNode,{container:t}),t.inDoc()||e.one("body").append(t),this.rendered=!0,this},renderChildren:function(t,n){n||(n={});var r=n.container,i=r&&r.one(">."+this.classNames.children),s=this._lazyRender;i&&n.force&&(i.remove(!0),i=null),i||(i=e.Node.create(this.templates.children({classNames:this.classNames,node:t,treeview:this})));if(t.hasChildren()){i.set("aria-expanded",t.isOpen());for(var o=0,u=t.children.length;o."+r.children);l&&l.remove(!0)}return t.state.rendered=!0,n.force?(o.replace(s),o.destroy()):n.container&&s.get("parentNode")!==n.container&&n.container.append(s),s},_attachTreeViewEvents:function(){this._treeViewEvents||(this._treeViewEvents=[]);var e=this.classNames,t=this.get("container");this._treeViewEvents.push(this.after({add:this._afterAdd,clear:this._afterClear,close:this._afterClose,multiSelectChange:this._afterTreeViewMultiSelectChange,open:this._afterOpen,remove:this._afterRemove,select:this._afterSelect,unselect:this._afterUnselect}),t.on("mousedown",this._onMouseDown,this),t.delegate("click",this._onIndicatorClick,"."+e.indicator,this),t.delegate("click",this._onRowClick,"."+e.row,this),t.delegate("dblclick",this._onRowDoubleClick,"."+e.canHaveChildren+" > ."+e.row,this))},_detachTreeViewEvents:function(){(new e.EventHandle(this._treeViewEvents)).detach()},_processRenderQueue:function(){if(!this.rendered)return;var e=this._renderQueue,t;for(var n in e)e.hasOwnProperty(n)&&(t=this.getNodeById(n),t&&this.renderNode(t,e[n]));this._renderQueue={}},_queueRender:function(t,n){if(!this.rendered)return;var r=this._renderQueue,i=this;return clearTimeout(this._renderTimeout),r[t.id]=e.merge(r[t.id],n),this._renderTimeout=setTimeout(function(){i._processRenderQueue()},15),this},_setLazyRender:function(e){return this._lazyRender=e},_syncNodeOpenState:function(e,t){t||(t=this.getHTMLNode(e));if(!t)return;e.isOpen()?t.addClass(this.classNames.open).set("aria-expanded",!0):t.removeClass(this.classNames.open).set("aria-expanded",!1)},_syncNodeSelectedState:function(e,t){t||(t=this.getHTMLNode(e));if(!t)return;var n=this.get("multiSelect");e.isSelected()?(t.addClass(this.classNames.selected),n?t.set("aria-selected",!0):t.set("tabIndex",0)):(t.removeClass(this.classNames.selected).removeAttribute("tabIndex"),n&&t.set("aria-selected",!1))},_afterAdd:function(e){if(!this.rendered)return;var t=e.parent,n=t.isRoot(),r=e.node,i,s;n?i=this._childrenNode:(s=this.getHTMLNode(t),i=s&&s.one(">."+this.classNames.children)),i?(i.insert(this.renderNode(r,{renderChildren:!this._lazyRender||r.isOpen()}),e.index),n||this._queueRender(t)):n||this._queueRender(t,{renderChildren:!0})},_afterClear:function(){if(!this.rendered)return;clearTimeout(this._renderTimeout),this._renderQueue={},delete this._childrenNode,this.rendered=!1,this.get("container").empty(),this.render()},_afterClose:function(e){this.rendered&&this._syncNodeOpenState(e.node)},_afterOpen:function(e){if(!this.rendered)return;var t=e.node,n=this.getHTMLNode(t);t.state.renderedChildren||this.renderChildren(t,{container:n}),this._syncNodeOpenState(t,n)},_afterRemove:function(e){if(!this.rendered)return;var t=e.node,n=e.parent;this._renderQueue[t.id]&&delete this._renderQueue[t.id];var r=this.getHTMLNode(t);r&&(r.empty().remove(!0),t._htmlNode=null),t.state.destroyed||t.traverse
+(function(e){e._htmlNode=null,e.state.rendered=!1,e.state.renderedChildren=!1}),n&&!n.hasChildren()&&this.renderNode(n)},_afterSelect:function(e){this.rendered&&this._syncNodeSelectedState(e.node)},_afterTreeViewMultiSelectChange:function(e){if(!this.rendered)return;var t=this.get("container"),n=t.one("> ."+this.classNames.children),r=t.all("."+this.classNames.node);e.newVal?(n.set("aria-multiselectable",!0),r.set("aria-selected",!1)):(n.removeAttribute("aria-multiselectable"),r.removeAttribute("aria-selected"))},_afterUnselect:function(e){this.rendered&&this._syncNodeSelectedState(e.node)},_onIndicatorClick:function(e){var t=e.currentTarget.ancestor("."+this.classNames.row);e.stopImmediatePropagation(),this.getNodeById(t.getData("node-id")).toggleOpen()},_onMouseDown:function(e){e.preventDefault()},_onRowClick:function(e){if(e.button>1)return;var t=this.getNodeById(e.currentTarget.getData("node-id"));this.get("multiSelect")?t[t.isSelected()?"unselect":"select"]():t.select()},_onRowDoubleClick:function(e){if(e.button>1)return;this.getNodeById(e.currentTarget.getData("node-id")).toggleOpen()}},{ATTRS:{lazyRender:{lazyAdd:!1,setter:"_setLazyRender",value:!0}}});e.TreeView=e.mix(i,e.TreeView)},"@VERSION@",{requires:["base-build","classnamemanager","template-micro","tree","tree-labelable","tree-openable","tree-selectable","view"],skinnable:!0}),YUI.add("moodle-mod_scorm-treeview",function(e,t){e.use("gallery-sm-treeview")},"@VERSION@",{requires:["base-build","classnamemanager","template-micro","tree","tree-labelable","tree-openable","tree-selectable","view","moodle-mod_scorm-treeview-skin"]});
diff --git a/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview.js b/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview.js
new file mode 100644
index 00000000000..0f1f26f6d09
--- /dev/null
+++ b/mod/scorm/yui/build/moodle-mod_scorm-treeview/moodle-mod_scorm-treeview.js
@@ -0,0 +1,758 @@
+YUI.add('gallery-sm-treeview', function (Y, NAME) {
+
+var Micro = Y.Template.Micro;
+
+Y.namespace('TreeView').Templates = {
+ children: Micro.compile(
+ '' +
+ 'role="tree" tabindex="0"' +
+ '<% } else { %>' +
+ 'role="group"' +
+ '<% } %>' +
+
+ '>
'
+ ),
+
+ node: Micro.compile(
+ '" role="treeitem" aria-labelled-by="<%= data.node.id %>-label">' +
+ '' +
+ '' +
+ '' +
+ '<%== data.node.label %>' +
+ '
' +
+ ''
+ )
+};
+/*jshint expr:true, onevar:false */
+
+/**
+Provides the `Y.TreeView` widget.
+
+@module gallery-sm-treeview
+@main gallery-sm-treeview
+**/
+
+/**
+TreeView widget.
+
+@class TreeView
+@constructor
+@extends View
+@uses Tree
+@uses Tree.Labelable
+@uses Tree.Openable
+@uses Tree.Selectable
+**/
+
+var getClassName = Y.ClassNameManager.getClassName,
+
+TreeView = Y.Base.create('treeView', Y.View, [
+ Y.Tree,
+ Y.Tree.Labelable,
+ Y.Tree.Openable,
+ Y.Tree.Selectable
+], {
+ // -- Public Properties ----------------------------------------------------
+
+ /**
+ CSS class names used by this treeview.
+
+ @property {Object} classNames
+ @param {String} canHaveChildren Class name indicating that a tree node can
+ contain child nodes (whether or not it actually does).
+ @param {String} children Class name for a list of child nodes.
+ @param {String} hasChildren Class name indicating that a tree node has one
+ or more child nodes.
+ @param {String} icon Class name for a tree node's icon.
+ @param {String} indicator Class name for an open/closed indicator.
+ @param {String} label Class name for a tree node's user-visible label.
+ @param {String} node Class name for a tree node item.
+ @param {String} noTouch Class name added to the TreeView container when not
+ using a touchscreen device.
+ @param {String} open Class name indicating that a tree node is open.
+ @param {String} row Class name for a row container encompassing the
+ indicator and label within a tree node.
+ @param {String} selected Class name for a tree node that's selected.
+ @param {String} touch Class name added to the TreeView container when using
+ a touchscreen device.
+ @param {String} treeview Class name for the TreeView container.
+ **/
+ classNames: {
+ canHaveChildren: getClassName('treeview-can-have-children'),
+ children : getClassName('treeview-children'),
+ hasChildren : getClassName('treeview-has-children'),
+ icon : getClassName('treeview-icon'),
+ indicator : getClassName('treeview-indicator'),
+ label : getClassName('treeview-label'),
+ node : getClassName('treeview-node'),
+ noTouch : getClassName('treeview-notouch'),
+ open : getClassName('treeview-open'),
+ row : getClassName('treeview-row'),
+ selected : getClassName('treeview-selected'),
+ touch : getClassName('treeview-touch'),
+ treeview : getClassName('treeview')
+ },
+
+ /**
+ Whether or not this TreeView has been rendered.
+
+ @property {Boolean} rendered
+ @default false
+ **/
+ rendered: false,
+
+ /**
+ Default templates used to render this TreeView.
+
+ @property {Object} templates
+ **/
+ templates: Y.TreeView.Templates,
+
+ // -- Protected Properties -------------------------------------------------
+
+ /**
+ Simple way to type-check that this is a TreeView instance.
+
+ @property {Boolean} _isYUITreeView
+ @default true
+ @protected
+ **/
+ _isYUITreeView: true,
+
+ /**
+ Cached value of the `lazyRender` attribute.
+
+ @property {Boolean} _lazyRender
+ @protected
+ **/
+
+ // -- Lifecycle Methods ----------------------------------------------------
+
+ initializer: function (config) {
+ if (config && config.templates) {
+ this.templates = Y.merge(this.templates, config.templates);
+ }
+
+ this._renderQueue = {};
+ this._attachTreeViewEvents();
+ },
+
+ destructor: function () {
+ clearTimeout(this._renderTimeout);
+ this._detachTreeViewEvents();
+
+ this._renderQueue = null;
+ },
+
+ // -- Public Methods -------------------------------------------------------
+
+ destroyNode: function (node, options) {
+ node._htmlNode = null;
+ return Y.Tree.prototype.destroyNode.call(this, node, options);
+ },
+
+ /**
+ Returns the HTML node (as a `Y.Node` instance) associated with the specified
+ `Tree.Node` instance, if any.
+
+ @method getHTMLNode
+ @param {Tree.Node} treeNode Tree node.
+ @return {Node} `Y.Node` instance associated with the given tree node, or
+ `undefined` if one was not found.
+ **/
+ getHTMLNode: function (treeNode) {
+ if (!treeNode._htmlNode) {
+ treeNode._htmlNode = this.get('container').one('#' + treeNode.id);
+ }
+
+ return treeNode._htmlNode;
+ },
+
+ /**
+ Renders this TreeView into its container.
+
+ If the container hasn't already been added to the current document, it will
+ be appended to the `` element.
+
+ @method render
+ @chainable
+ **/
+ render: function () {
+ var container = this.get('container'),
+ isTouchDevice = 'ontouchstart' in Y.config.win;
+
+ container.addClass(this.classNames.treeview);
+ container.addClass(this.classNames[isTouchDevice ? 'touch' : 'noTouch']);
+
+ this._childrenNode = this.renderChildren(this.rootNode, {
+ container: container
+ });
+
+ if (!container.inDoc()) {
+ Y.one('body').append(container);
+ }
+
+ this.rendered = true;
+
+ return this;
+ },
+
+ /**
+ Renders the children of the specified tree node.
+
+ If a container is specified, it will be assumed to be an existing rendered
+ tree node, and the children will be rendered (or re-rendered) inside it.
+
+ @method renderChildren
+ @param {Tree.Node} treeNode Tree node whose children should be rendered.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container into
+ which the children should be rendered. If the container already
+ contains rendered children, they will be re-rendered in place.
+
+ @param {Boolean} [options.force=false] If `true`, children will be
+ re-rendered from scratch even if they've already been rendered.
+
+ @return {Node} `Y.Node` instance containing the rendered children.
+ **/
+ renderChildren: function (treeNode, options) {
+ options || (options = {});
+
+ var container = options.container,
+ childrenNode = container && container.one('>.' + this.classNames.children),
+ lazyRender = this._lazyRender;
+
+ if (childrenNode && options.force) {
+ childrenNode.remove(true);
+ childrenNode = null;
+ }
+
+ if (!childrenNode) {
+ childrenNode = Y.Node.create(this.templates.children({
+ classNames: this.classNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ if (treeNode.hasChildren()) {
+ childrenNode.set('aria-expanded', treeNode.isOpen());
+
+ for (var i = 0, len = treeNode.children.length; i < len; i++) {
+ var child = treeNode.children[i];
+
+ this.renderNode(child, {
+ container : childrenNode,
+ renderChildren: !lazyRender || child.isOpen()
+ });
+ }
+ }
+
+ // Keep track of whether or not this node's children have been rendered
+ // so we'll know whether we need to render them later if the node is
+ // opened.
+ treeNode.state.renderedChildren = true;
+
+ if (container) {
+ container.append(childrenNode);
+ }
+
+ return childrenNode;
+ },
+
+ /**
+ Renders the specified tree node and its children (if any).
+
+ If a container is specified, the rendered node will be appended to it.
+
+ @method renderNode
+ @param {Tree.Node} treeNode Tree node to render.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container to
+ which the rendered tree node should be appended.
+
+ @param {Boolean} [options.force=false] If `true`, this node (and its
+ children if `renderChildren` is `true`) will be re-rendered from
+ scratch, even if it's already been rendered.
+
+ @param {Boolean} [options.renderChildren=false] Whether or not to render
+ this node's children.
+
+ @return {Node} `Y.Node` instance of the rendered tree node.
+ **/
+ renderNode: function (treeNode, options) {
+ options || (options = {});
+
+ var classNames = this.classNames,
+ hasChildren = treeNode.hasChildren(),
+ htmlNode = treeNode._htmlNode,
+ oldHtmlNode = options.force && htmlNode,
+ nodeClassNames = {},
+
+ className;
+
+ // Build the hash of CSS classes for this node.
+ nodeClassNames[classNames.node] = true;
+ nodeClassNames[classNames.canHaveChildren] = !!treeNode.canHaveChildren;
+ nodeClassNames[classNames.hasChildren] = hasChildren;
+
+ if (htmlNode && !options.force) {
+ // This node has already been rendered, so we just need to update
+ // the DOM instead of re-rendering it from scratch.
+ htmlNode.one('.' + classNames.label).setHTML(treeNode.label);
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className)) {
+ htmlNode.toggleClass(className, nodeClassNames[className]);
+ }
+ }
+ } else {
+ // This node hasn't been rendered yet or is being forcibly
+ // re-rendered.
+ var enabledClassNames = [];
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className) && nodeClassNames[className]) {
+ enabledClassNames.push(className);
+ }
+ }
+
+ htmlNode = treeNode._htmlNode = Y.Node.create(this.templates.node({
+ classNames : classNames,
+ nodeClassNames: enabledClassNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ this._syncNodeSelectedState(treeNode, htmlNode);
+
+ if (hasChildren) {
+ if (options.renderChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+ } else {
+ // If children were previously rendered but this node no longer has
+ // children, remove the empty child list.
+ var childrenNode = htmlNode.one('>.' + classNames.children);
+
+ if (childrenNode) {
+ childrenNode.remove(true);
+ }
+ }
+
+ treeNode.state.rendered = true;
+
+ if (options.force) {
+ oldHtmlNode.replace(htmlNode);
+ oldHtmlNode.destroy();
+ } else {
+ if (options.container && htmlNode.get('parentNode') !== options.container) {
+ options.container.append(htmlNode);
+ }
+ }
+
+ return htmlNode;
+ },
+
+ // -- Protected Methods ----------------------------------------------------
+
+ _attachTreeViewEvents: function () {
+ this._treeViewEvents || (this._treeViewEvents = []);
+
+ var classNames = this.classNames,
+ container = this.get('container');
+
+ this._treeViewEvents.push(
+ // Custom events.
+ this.after({
+ add : this._afterAdd,
+ clear : this._afterClear,
+ close : this._afterClose,
+ multiSelectChange: this._afterTreeViewMultiSelectChange, // sheesh
+ open : this._afterOpen,
+ remove : this._afterRemove,
+ select : this._afterSelect,
+ unselect : this._afterUnselect
+ }),
+
+ // DOM events.
+ container.on('mousedown', this._onMouseDown, this),
+
+ container.delegate('click', this._onIndicatorClick,
+ '.' + classNames.indicator, this),
+
+ container.delegate('click', this._onRowClick,
+ '.' + classNames.row, this),
+
+ container.delegate('dblclick', this._onRowDoubleClick,
+ '.' + classNames.canHaveChildren + ' > .' + classNames.row, this)
+ );
+ },
+
+ _detachTreeViewEvents: function () {
+ (new Y.EventHandle(this._treeViewEvents)).detach();
+ },
+
+ _processRenderQueue: function () {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ node;
+
+ for (var id in queue) {
+ if (queue.hasOwnProperty(id)) {
+ node = this.getNodeById(id);
+
+ if (node) {
+ this.renderNode(node, queue[id]);
+ }
+ }
+ }
+
+ this._renderQueue = {};
+ },
+
+ _queueRender: function (node, options) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ self = this;
+
+ clearTimeout(this._renderTimeout);
+
+ queue[node.id] = Y.merge(queue[node.id], options);
+
+ this._renderTimeout = setTimeout(function () {
+ self._processRenderQueue();
+ }, 15);
+
+ return this;
+ },
+
+ /**
+ Setter for the `lazyRender` attribute.
+
+ Just caches the value in a property for faster lookups.
+
+ @method _setLazyRender
+ @return {Boolean} Value.
+ @protected
+ **/
+ _setLazyRender: function (value) {
+ /*jshint boss:true */
+ return this._lazyRender = value;
+ },
+
+ _syncNodeOpenState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ if (node.isOpen()) {
+ htmlNode
+ .addClass(this.classNames.open)
+ .set('aria-expanded', true);
+ } else {
+ htmlNode
+ .removeClass(this.classNames.open)
+ .set('aria-expanded', false);
+ }
+ },
+
+ _syncNodeSelectedState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ var multiSelect = this.get('multiSelect');
+
+ if (node.isSelected()) {
+ htmlNode.addClass(this.classNames.selected);
+
+ if (multiSelect) {
+ // It's only necessary to set aria-selected when multi-select is
+ // enabled and focus can't be used to track the selection state.
+ htmlNode.set('aria-selected', true);
+ } else {
+ htmlNode.set('tabIndex', 0);
+ }
+ } else {
+ htmlNode
+ .removeClass(this.classNames.selected)
+ .removeAttribute('tabIndex');
+
+ if (multiSelect) {
+ htmlNode.set('aria-selected', false);
+ }
+ }
+ },
+
+ // -- Protected Event Handlers ---------------------------------------------
+
+ _afterAdd: function (e) {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ var parent = e.parent,
+ parentIsRoot = parent.isRoot(),
+ treeNode = e.node,
+
+ htmlChildren,
+ htmlParent;
+
+ if (parentIsRoot) {
+ htmlChildren = this._childrenNode;
+ } else {
+ htmlParent = this.getHTMLNode(parent),
+ htmlChildren = htmlParent && htmlParent.one('>.' + this.classNames.children);
+ }
+
+ if (htmlChildren) {
+ // Parent's children have already been rendered. Instead of
+ // re-rendering all of them, just render the new node and insert it
+ // at the correct position.
+ htmlChildren.insert(this.renderNode(treeNode, {
+ renderChildren: !this._lazyRender || treeNode.isOpen()
+ }), e.index);
+
+ // Schedule the parent node to be re-rendered in order to update its
+ // state. This is done asynchronously and throttled in order to
+ // avoid re-rendering the parent many times if multiple children are
+ // added in quick succession.
+ if (!parentIsRoot) {
+ this._queueRender(parent);
+ }
+ } else if (!parentIsRoot) {
+ // Either the parent hasn't been rendered yet, or its children
+ // haven't been rendered yet. Schedule it to be rendered. This is
+ // done asynchronously and throttled in order to avoid re-rendering
+ // the parent many times if multiple children are added in quick
+ // succession.
+ this._queueRender(parent, {renderChildren: true});
+ }
+ },
+
+ _afterClear: function () {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ clearTimeout(this._renderTimeout);
+ this._renderQueue = {};
+
+ delete this._childrenNode;
+ this.rendered = false;
+
+ this.get('container').empty();
+ this.render();
+ },
+
+ _afterClose: function (e) {
+ if (this.rendered) {
+ this._syncNodeOpenState(e.node);
+ }
+ },
+
+ _afterOpen: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ htmlNode = this.getHTMLNode(treeNode);
+
+ // If this node's children haven't been rendered yet, render them.
+ if (!treeNode.state.renderedChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ },
+
+ _afterRemove: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ parent = e.parent;
+
+ // If this node is in the render queue, remove it from the queue.
+ if (this._renderQueue[treeNode.id]) {
+ delete this._renderQueue[treeNode.id];
+ }
+
+ // Remove DOM nodes associated with this node and any of its
+ // descendants, and mark all nodes as unrendered so that they'll be
+ // re-rendered if they're reinserted in the tree.
+ var htmlNode = this.getHTMLNode(treeNode);
+
+ if (htmlNode) {
+ htmlNode
+ .empty()
+ .remove(true);
+
+ treeNode._htmlNode = null;
+ }
+
+ if (!treeNode.state.destroyed) {
+ treeNode.traverse(function (node) {
+ node._htmlNode = null;
+ node.state.rendered = false;
+ node.state.renderedChildren = false;
+ });
+ }
+
+ // Re-render the parent to update its state if this was its last child.
+ if (parent && !parent.hasChildren()) {
+ this.renderNode(parent);
+ }
+ },
+
+ _afterSelect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _afterTreeViewMultiSelectChange: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var container = this.get('container'),
+ rootList = container.one('> .' + this.classNames.children),
+ htmlNodes = container.all('.' + this.classNames.node);
+
+ if (e.newVal) {
+ rootList.set('aria-multiselectable', true);
+ htmlNodes.set('aria-selected', false);
+ } else {
+ // When multiselect is disabled, aria-selected must not be set on
+ // any nodes, since focus is used to indicate selection.
+ rootList.removeAttribute('aria-multiselectable');
+ htmlNodes.removeAttribute('aria-selected');
+ }
+ },
+
+ _afterUnselect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _onIndicatorClick: function (e) {
+ var rowNode = e.currentTarget.ancestor('.' + this.classNames.row);
+
+ // Indicator clicks shouldn't toggle selection state, so don't allow
+ // this event to propagate to the _onRowClick() handler.
+ e.stopImmediatePropagation();
+
+ this.getNodeById(rowNode.getData('node-id')).toggleOpen();
+ },
+
+ _onMouseDown: function (e) {
+ // This prevents the tree from momentarily grabbing focus before focus
+ // is set on a node.
+ e.preventDefault();
+ },
+
+ _onRowClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ var node = this.getNodeById(e.currentTarget.getData('node-id'));
+
+ if (this.get('multiSelect')) {
+ node[node.isSelected() ? 'unselect' : 'select']();
+ } else {
+ node.select();
+ }
+ },
+
+ _onRowDoubleClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ this.getNodeById(e.currentTarget.getData('node-id')).toggleOpen();
+ }
+}, {
+ ATTRS: {
+ /**
+ When `true`, a node's children won't be rendered until the first time
+ that node is opened.
+
+ This can significantly speed up the time it takes to render a large
+ tree, but might not make sense if you're using CSS that doesn't hide the
+ contents of closed nodes.
+
+ @attribute {Boolean} lazyRender
+ @default true
+ **/
+ lazyRender: {
+ lazyAdd: false, // to ensure that the setter runs on init
+ setter : '_setLazyRender',
+ value : true
+ }
+ }
+});
+
+Y.TreeView = Y.mix(TreeView, Y.TreeView);
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base-build",
+ "classnamemanager",
+ "template-micro",
+ "tree",
+ "tree-labelable",
+ "tree-openable",
+ "tree-selectable",
+ "view"
+ ],
+ "skinnable": true
+});
+YUI.add('moodle-mod_scorm-treeview', function (Y, NAME) {
+
+Y.use('gallery-sm-treeview');
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base-build",
+ "classnamemanager",
+ "template-micro",
+ "tree",
+ "tree-labelable",
+ "tree-openable",
+ "tree-selectable",
+ "view",
+ "moodle-mod_scorm-treeview-skin"
+ ]
+});
diff --git a/mod/scorm/yui/src/treeview/assets/moodle-mod_scorm-treeview-core.css b/mod/scorm/yui/src/treeview/assets/moodle-mod_scorm-treeview-core.css
new file mode 100644
index 00000000000..f7c7ae1384b
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/assets/moodle-mod_scorm-treeview-core.css
@@ -0,0 +1,65 @@
+.yui3-treeview,
+.yui3-treeview * {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+}
+
+.yui3-treeview-children {
+ margin: 0;
+ padding: 0;
+ list-style: none;
+}
+
+/* This ensures that the root node is always "open". */
+.yui3-treeview-children .yui3-treeview-children { display: none; }
+
+.yui3-treeview-indicator {
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ visibility: hidden;
+ top: 0;
+ width: 20px;
+}
+
+.yui3-treeview-indicator s {
+ border: 5px solid transparent;
+ border-left-color: #afafaf;
+ display: inline-block;
+ *display: none; /* Hide from IE 6-7 */
+ height: 5px;
+ left: 6px;
+ position: relative;
+ top: -1px;
+ vertical-align: middle;
+ width: 5px;
+}
+
+.yui3-treeview-label { white-space: pre; }
+.yui3-treeview-node .yui3-treeview-children { margin-left: 16px; }
+
+.yui3-treeview-row {
+ cursor: pointer;
+ overflow: hidden;
+ padding: 0 4px 0 20px;
+ position: relative;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+.yui3-treeview-can-have-children > .yui3-treeview-row > .yui3-treeview-indicator { visibility: visible; }
+
+.yui3-treeview-open > .yui3-treeview-row > .yui3-treeview-indicator s {
+ border-left-color: transparent;
+ border-top-color: #afafaf;
+ left: 4px;
+ top: 2px;
+}
+
+.yui3-treeview-open > .yui3-treeview-children { display: block; }
diff --git a/mod/scorm/yui/src/treeview/assets/skins/sam/folder.png b/mod/scorm/yui/src/treeview/assets/skins/sam/folder.png
new file mode 100644
index 00000000000..76d9d12b57d
Binary files /dev/null and b/mod/scorm/yui/src/treeview/assets/skins/sam/folder.png differ
diff --git a/mod/scorm/yui/src/treeview/assets/skins/sam/folder@2x.png b/mod/scorm/yui/src/treeview/assets/skins/sam/folder@2x.png
new file mode 100644
index 00000000000..c18674e2608
Binary files /dev/null and b/mod/scorm/yui/src/treeview/assets/skins/sam/folder@2x.png differ
diff --git a/mod/scorm/yui/src/treeview/assets/skins/sam/item.png b/mod/scorm/yui/src/treeview/assets/skins/sam/item.png
new file mode 100644
index 00000000000..33bcadaaf51
Binary files /dev/null and b/mod/scorm/yui/src/treeview/assets/skins/sam/item.png differ
diff --git a/mod/scorm/yui/src/treeview/assets/skins/sam/item@2x.png b/mod/scorm/yui/src/treeview/assets/skins/sam/item@2x.png
new file mode 100644
index 00000000000..768e57985d6
Binary files /dev/null and b/mod/scorm/yui/src/treeview/assets/skins/sam/item@2x.png differ
diff --git a/mod/scorm/yui/src/treeview/assets/skins/sam/moodle-mod_scorm-treeview-skin.css b/mod/scorm/yui/src/treeview/assets/skins/sam/moodle-mod_scorm-treeview-skin.css
new file mode 100644
index 00000000000..5c4395dac2d
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/assets/skins/sam/moodle-mod_scorm-treeview-skin.css
@@ -0,0 +1,75 @@
+/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/
+
+.yui3-skin-sam .yui3-treeview-row > .yui3-treeview-indicator:after { content: ''; }
+
+.yui3-skin-sam .yui3-treeview-label,
+.yui3-skin-sam .yui3-treeview-row {
+ line-height: 1.6;
+}
+
+/* Page icon for all nodes by default. */
+.yui3-skin-sam .yui3-treeview-icon {
+ background: left center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4jY2Sv2rDMBDGT3JKl/oB2kdI3sJ7lwx9hvYZQkkIDXQuhWYOeCvdS6DP4D3dstsUZArWH/dOsRQnlk0F8onT3U/fJ2tU1zXQuFssbjFcw/D4MsZ8G63hY7WyiZHbwY2bzXy+rnBT0jQGNMLdAVmWwWa7vcclw7lzfUeA1sxQk5SgEVBhrJQChTmCCCHgfblcT2ezh6ZldwLAJkZojt+LKDqKbiB5nsNrmkIyHr+9pOkj7jydK+DUzC0GOpAkSaySOI7hM8uKjgVFClqAEISUuNoOQCvlLbTHOYQOodoQ4GCBc/olvZDoAOBdC5j0CgYgEUbVA7B3YO+BEj2QCPMqaEFKr4Da+iDWAtaGLZAC/FVDENbUhi04BQMQygct/JalXbs76IOwVu0J4KcoLr3M5kHRy2NNtFCEMJztWg8ohdhfTSbP8I+BkL1b/wF76M1c9BEbZwAAAABJRU5ErkJggg==);
+ float: left;
+ height: 1.6em; /* should match the line-height of the row and label */
+ min-height: 16px; /* to prevent the icon from being clipped */
+ width: 16px;
+}
+
+.yui3-skin-sam .yui3-treeview-label { padding-left: 6px; }
+.yui3-skin-sam .yui3-treeview-node { outline: none; }
+.yui3-skin-sam .yui3-treeview-node .yui3-treeview-children { margin-left: 20px; }
+
+.yui3-skin-sam .yui3-treeview-row {
+ background-image: -moz-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -ms-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -o-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: -webkit-linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ background-image: linear-gradient(rgba(255, 255, 255, 0.8), rgba(255, 255, 255, 0));
+ border: 1px solid transparent;
+ border-radius: 2px;
+ _border: none;
+}
+
+/* -- Node states ----------------------------------------------------------- */
+
+/* Light blue background for hovered nodes, except on touch devices. */
+.yui3-skin-sam .yui3-treeview-notouch .yui3-treeview-row:hover {
+ background-color: #F0F6FE;
+ border-color: #B6D4FC;
+}
+
+/* Use a folder icon for nodes that can have children. */
+.yui3-skin-sam .yui3-treeview-can-have-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4T63TsUrDUBTG8Y+TUhfnIhQR6tC8i4uC+BBFKVGETiLUoUPQRfENXARRfApfwCVLRSlFsMTSapsmOd6vuLRJoLEOvyU3/8PhwhVVxTLE2m4g007DNpwMNv8RAHvGUSrF1v1Z/TwNz9iK2WPjsXXo3jUPZvDbVW3Xbbdf0Ov1E25P9122HCDj8QTD4fcM3++jXF6HiKDb7SR4nge2ZgCsKIqRxvc/USqtoVLZTKhWbbCdbhCGMbIMBl+pRqPgd4NYrfloUWy5QSEMFX/B9h82iLUwf3mLYrv8AJ1EqyIWisWVXNhMW3y8vtVOLm7qzctc2LAVfX5o6dO1Yxzn5LAV8yQD493o5MQm+AEX3F8P8w0R8QAAAABJRU5ErkJggg==);
+}
+
+/* Gray background for selected, unfocused nodes. */
+.yui3-skin-sam .yui3-treeview-selected > .yui3-treeview-row {
+ background-color: #E6E6E6;
+ border-color: #D9D9D9;
+}
+
+/* Blue background for selected, focused nodes and selected, hovered nodes,
+ as well as selected unfocused nodes when multiselect is enabled. */
+.yui3-skin-sam .yui3-treeview-selected:focus > .yui3-treeview-row,
+.yui3-skin-sam .yui3-treeview-children[aria-multiselectable="true"] .yui3-treeview-selected > .yui3-treeview-row,
+.yui3-skin-sam .yui3-treeview-selected > .yui3-treeview-row:hover {
+ background-color: #C9E0FC;
+ border-color: #7DA2CE;
+}
+
+/* -- High DPI styles ------------------------------------------------------- */
+@media (-webkit-min-device-pixel-ratio: 1.5), (min-resolution: 144dpi) {
+ .yui3-skin-sam .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);
+ background-size: contain;
+ }
+
+ .yui3-skin-sam .yui3-treeview-has-children > .yui3-treeview-row > .yui3-treeview-icon {
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=);
+ }
+}
diff --git a/mod/scorm/yui/src/treeview/assets/skins/sam/moodle-mod_scorm-treeview.css b/mod/scorm/yui/src/treeview/assets/skins/sam/moodle-mod_scorm-treeview.css
new file mode 100644
index 00000000000..76dab7fe8cd
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/assets/skins/sam/moodle-mod_scorm-treeview.css
@@ -0,0 +1,4 @@
+.yui3-treeview,.yui3-treeview *{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;-moz-user-select:none;-ms-user-select:none;-webkit-user-select:none;user-select:none}.yui3-treeview-children{margin:0;padding:0;list-style:none}.yui3-treeview-children .yui3-treeview-children{display:none}.yui3-treeview-indicator{bottom:0;left:0;position:absolute;visibility:hidden;top:0;width:20px}.yui3-treeview-indicator s{border:5px solid transparent;border-left-color:#afafaf;display:inline-block;*display:none;height:5px;left:6px;position:relative;top:-1px;vertical-align:middle;width:5px}.yui3-treeview-label{white-space:pre}.yui3-treeview-node .yui3-treeview-children{margin-left:16px}.yui3-treeview-row{cursor:pointer;overflow:hidden;padding:0 4px 0 20px;position:relative;text-overflow:ellipsis;white-space:nowrap}.yui3-treeview-can-have-children>.yui3-treeview-row>.yui3-treeview-indicator{visibility:visible}.yui3-treeview-open>.yui3-treeview-row>.yui3-treeview-indicator s{border-left-color:transparent;border-top-color:#afafaf;left:4px;top:2px}.yui3-treeview-open>.yui3-treeview-children{display:block}/*!
+Icons by Yusuke Kamiyamane .
+Licensed under a Creative Commons Attribution 3.0 License.
+*/.yui3-skin-sam .yui3-treeview-row>.yui3-treeview-indicator:after{content:''}.yui3-skin-sam .yui3-treeview-label,.yui3-skin-sam .yui3-treeview-row{line-height:1.6}.yui3-skin-sam .yui3-treeview-icon{background:left center no-repeat url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABUUlEQVQ4jY2Sv2rDMBDGT3JKl/oB2kdI3sJ7lwx9hvYZQkkIDXQuhWYOeCvdS6DP4D3dstsUZArWH/dOsRQnlk0F8onT3U/fJ2tU1zXQuFssbjFcw/D4MsZ8G63hY7WyiZHbwY2bzXy+rnBT0jQGNMLdAVmWwWa7vcclw7lzfUeA1sxQk5SgEVBhrJQChTmCCCHgfblcT2ezh6ZldwLAJkZojt+LKDqKbiB5nsNrmkIyHr+9pOkj7jydK+DUzC0GOpAkSaySOI7hM8uKjgVFClqAEISUuNoOQCvlLbTHOYQOodoQ4GCBc/olvZDoAOBdC5j0CgYgEUbVA7B3YO+BEj2QCPMqaEFKr4Da+iDWAtaGLZAC/FVDENbUhi04BQMQygct/JalXbs76IOwVu0J4KcoLr3M5kHRy2NNtFCEMJztWg8ohdhfTSbP8I+BkL1b/wF76M1c9BEbZwAAAABJRU5ErkJggg==);float:left;height:1.6em;min-height:16px;width:16px}.yui3-skin-sam .yui3-treeview-label{padding-left:6px}.yui3-skin-sam .yui3-treeview-node{outline:0}.yui3-skin-sam .yui3-treeview-node .yui3-treeview-children{margin-left:20px}.yui3-skin-sam .yui3-treeview-row{background-image:-moz-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-ms-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-o-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:-webkit-linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));background-image:linear-gradient(rgba(255,255,255,0.8),rgba(255,255,255,0));border:1px solid transparent;border-radius:2px;_border:0}.yui3-skin-sam .yui3-treeview-notouch .yui3-treeview-row:hover{background-color:#f0f6fe;border-color:#b6d4fc}.yui3-skin-sam .yui3-treeview-can-have-children>.yui3-treeview-row>.yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABD0lEQVQ4T63TsUrDUBTG8Y+TUhfnIhQR6tC8i4uC+BBFKVGETiLUoUPQRfENXARRfApfwCVLRSlFsMTSapsmOd6vuLRJoLEOvyU3/8PhwhVVxTLE2m4g007DNpwMNv8RAHvGUSrF1v1Z/TwNz9iK2WPjsXXo3jUPZvDbVW3Xbbdf0Ov1E25P9122HCDj8QTD4fcM3++jXF6HiKDb7SR4nge2ZgCsKIqRxvc/USqtoVLZTKhWbbCdbhCGMbIMBl+pRqPgd4NYrfloUWy5QSEMFX/B9h82iLUwf3mLYrv8AJ1EqyIWisWVXNhMW3y8vtVOLm7qzctc2LAVfX5o6dO1Yxzn5LAV8yQD493o5MQm+AEX3F8P8w0R8QAAAABJRU5ErkJggg==)}.yui3-skin-sam .yui3-treeview-selected>.yui3-treeview-row{background-color:#e6e6e6;border-color:#d9d9d9}.yui3-skin-sam .yui3-treeview-selected:focus>.yui3-treeview-row,.yui3-skin-sam .yui3-treeview-children[aria-multiselectable="true"] .yui3-treeview-selected>.yui3-treeview-row,.yui3-skin-sam .yui3-treeview-selected>.yui3-treeview-row:hover{background-color:#c9e0fc;border-color:#7da2ce}@media(-webkit-min-device-pixel-ratio:1.5),(min-resolution:144dpi){.yui3-skin-sam .yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAACJElEQVRYw8WXvW4TQRSFz11vQUEeAAo3afIEFH4HyqSgoQ2PgKxY+VEoKCOh2F0okBAFVbpIvEaKpEjvwgVWbI937qHYmWWzXsPOzgpGsvbu2HP38znHV3JKEv9zpXWbB8fHrwG8iOz9g+Q9SZAEVfH9/LwZAMmXV6PR2FoLAlCyePl7luv8ELyWxhi8v7g4dLf3wQqQhFoLY0xeA1BVWBKqmsP4qwcrQU2nU3w7ORnvj0aHAATAXSiAiAhEBACQkECSAKqbV7f092HM53MYAF9OT8dvjo7euXfumgO4xgIALSBmsxmMq6/Ozi7fDodbIbYrAOQKkMEQxhh8mkyKvVf9/uXn6+shgA+NAFQVIoJEJP9WgRCDwQDWWmRZBmst0jTF15sbG5YBZ0FbCOn10EsSkMTznR30d3d/NgZQVWd/bkFbCF9JqWczBZwFRQhjIUSKYDdWYCOEERDBChQf9ipEQiQiwRYUIWQHEOJ6hlngJyEZDSGhCmh1EkZCOAviQ9gWol0IKz/DGIjgEKq1tQq0hUhEoNYGhrCSgRiIcAXKc6Dm4aEQ4RlwchVzoAOIcAvcweIhERBt5oAIAJaCGAMRZ0EHEMEWZFn2JIRdQBQ9mwAsFou0GsIoiFLPRgDLx8eer7uAkErPvwKslssn+7EQUtPzjwBmtdqg9VPRB7O8R79X+nvGUi1JUttzK8B6vX54trf3ER0uEXloDLC4vZ3gH61free2Cmjs7ecAAAAASUVORK5CYII=);background-size:contain}.yui3-skin-sam .yui3-treeview-has-children>.yui3-treeview-row>.yui3-treeview-icon{background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAECklEQVRYw+1XTUtUURh+752Z68fMaBpGRQRBULZwEZo/IdpICRFBfyCIFvkBoZuCwNIW+QtatAlaiC36DdJCK0ht0ybSwCRnwhxn7j2d57znPfeO4VXDclEH7pyPe877Pu/zfpw7nlKKDrL5dMDN8/uG7ei3jsu5M/q5tMPmV/pZJEO4IrLEZ/fBiLPH2luuPh25eT9t08WBh541c2E/GeiE5VMPbj8ul79TNpulKIrYtz57F3PEWaGQp8sjkwPMhJoXBpIArunfE3uF8OLerYlKpUKe51G1WqVcLkdhGFImkzHKAQTrANfc3ExXRp8M6mOfNIDn9QCIBqfH7oxXqzV9MNQHMyQZAuHGcyqy45iujY0fVKvVjALsh3KM0QRQkpWmpibqH50c0tMJjgEo8bzreD87+3ZP1jc2NlJb2yHTVyqbWjGsB0hljIDoMFQGMM/Rq7rsy1rTTr58NDy2V/rh94WFReroOEJB0EhgD8rQgy2EOghTKjTs+L7SIEMySBwAy/L6+rqmc8PiUY520IexzEE3xvAxWnf3eZqZeU35fIv2ccG6R+RDeJQYVzVbkcFVz4CmhOkh40MoFQXwW60mFvhaecYC44MrK6vU09NDc3NvaG3t246sLS0RnTp6uP3j8tcbWugzjgENGb6Df9hHsRWYex5AVG1gMkg0Abm8/IW6urp27bre3gtD/XfHR5Mu8IV6sVqYwBi95DjWsYa98uDd6uqqSz9xnZzdKgdBq/X61gX1DHDEco81sRpugBDMJTU56HjM7vHdvjQ5bCcHomMAi+yCuHhwDrMLMId1wg7GWJeGfckzaXIAKGaA4fjif1gFi5hy5YqMWAeG0dcrjxwDNs9T5TAA5dcxgEVWkLXpxpHPJVUZnEIj9ogCiZnNTS63OMcBur0cE+BbGPA4uiPru8imHxcWUSSByWWXXP3HHOBQDeUuSJcTuWLkGwYUGIijGwck7+U2wyMCpVBJBsh+WAlAu5FjGFBxFviSBQAiAckRHCYqm2eDiZwwyYzY+siU3DQ5NgvqYwAVLrLMcEDGlPO7yNEPf0MZAyJrfc6MpU+TYy6oOAbIZkHkqBUKkx8VUv9BsdCfbEI5M+KnyjGx4ABw2mRKpTLhwyKuXHIXeBSD810g2c4GI7k0S6bmdnIqlareoMyLrDKRTw35fMGkztY6L00KimRDcg/eBUGDK9dSrLaTUywWSettYAYsANztYIC/enzTJ2s4wOF9MlvYOpW4uNadxWlySqWStkIAvJsiOtcXtLa22nylBI3yaRbV5XHs98hlR/zZpn7Zt1VOEOQAIKD30wZ6q9fZN0TF46f/6j+S8ucPan56HAD03UgB3GA/ULw/rBo0VOwTev/8f8P/AH4C4GNS2u2jtnoAAAAASUVORK5CYII=)}}#yui3-css-stamp.skin-sam-gallery-sm-treeview{display:none}
diff --git a/mod/scorm/yui/src/treeview/build.json b/mod/scorm/yui/src/treeview/build.json
new file mode 100644
index 00000000000..e9aa5b97c4f
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/build.json
@@ -0,0 +1,23 @@
+{
+ "name": "moodle-mod_scorm-treeview",
+
+ "builds": {
+ "moodle-mod_scorm-treeview": {
+ "prependfiles": [
+ "gallery-sm-treeview-debug.js"
+ ],
+ "jsfiles": [
+ "treeview.js"
+ ]
+ },
+
+ "moodle-mod_scorm-treeview-sortable": {
+ "prependfiles": [
+ "gallery-sm-treeview-debug.js"
+ ],
+ "jsfiles": [
+ "treeview-sortable.js"
+ ]
+ }
+ }
+}
diff --git a/mod/scorm/yui/src/treeview/js/gallery-sm-treeview-debug.js b/mod/scorm/yui/src/treeview/js/gallery-sm-treeview-debug.js
new file mode 100644
index 00000000000..6da79aab9a9
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/js/gallery-sm-treeview-debug.js
@@ -0,0 +1,740 @@
+YUI.add('gallery-sm-treeview', function (Y, NAME) {
+
+var Micro = Y.Template.Micro;
+
+Y.namespace('TreeView').Templates = {
+ children: Micro.compile(
+ '' +
+ 'role="tree" tabindex="0"' +
+ '<% } else { %>' +
+ 'role="group"' +
+ '<% } %>' +
+
+ '>
'
+ ),
+
+ node: Micro.compile(
+ '" role="treeitem" aria-labelled-by="<%= data.node.id %>-label">' +
+ '' +
+ '' +
+ '' +
+ '<%== data.node.label %>' +
+ '
' +
+ ''
+ )
+};
+/*jshint expr:true, onevar:false */
+
+/**
+Provides the `Y.TreeView` widget.
+
+@module gallery-sm-treeview
+@main gallery-sm-treeview
+**/
+
+/**
+TreeView widget.
+
+@class TreeView
+@constructor
+@extends View
+@uses Tree
+@uses Tree.Labelable
+@uses Tree.Openable
+@uses Tree.Selectable
+**/
+
+var getClassName = Y.ClassNameManager.getClassName,
+
+TreeView = Y.Base.create('treeView', Y.View, [
+ Y.Tree,
+ Y.Tree.Labelable,
+ Y.Tree.Openable,
+ Y.Tree.Selectable
+], {
+ // -- Public Properties ----------------------------------------------------
+
+ /**
+ CSS class names used by this treeview.
+
+ @property {Object} classNames
+ @param {String} canHaveChildren Class name indicating that a tree node can
+ contain child nodes (whether or not it actually does).
+ @param {String} children Class name for a list of child nodes.
+ @param {String} hasChildren Class name indicating that a tree node has one
+ or more child nodes.
+ @param {String} icon Class name for a tree node's icon.
+ @param {String} indicator Class name for an open/closed indicator.
+ @param {String} label Class name for a tree node's user-visible label.
+ @param {String} node Class name for a tree node item.
+ @param {String} noTouch Class name added to the TreeView container when not
+ using a touchscreen device.
+ @param {String} open Class name indicating that a tree node is open.
+ @param {String} row Class name for a row container encompassing the
+ indicator and label within a tree node.
+ @param {String} selected Class name for a tree node that's selected.
+ @param {String} touch Class name added to the TreeView container when using
+ a touchscreen device.
+ @param {String} treeview Class name for the TreeView container.
+ **/
+ classNames: {
+ canHaveChildren: getClassName('treeview-can-have-children'),
+ children : getClassName('treeview-children'),
+ hasChildren : getClassName('treeview-has-children'),
+ icon : getClassName('treeview-icon'),
+ indicator : getClassName('treeview-indicator'),
+ label : getClassName('treeview-label'),
+ node : getClassName('treeview-node'),
+ noTouch : getClassName('treeview-notouch'),
+ open : getClassName('treeview-open'),
+ row : getClassName('treeview-row'),
+ selected : getClassName('treeview-selected'),
+ touch : getClassName('treeview-touch'),
+ treeview : getClassName('treeview')
+ },
+
+ /**
+ Whether or not this TreeView has been rendered.
+
+ @property {Boolean} rendered
+ @default false
+ **/
+ rendered: false,
+
+ /**
+ Default templates used to render this TreeView.
+
+ @property {Object} templates
+ **/
+ templates: Y.TreeView.Templates,
+
+ // -- Protected Properties -------------------------------------------------
+
+ /**
+ Simple way to type-check that this is a TreeView instance.
+
+ @property {Boolean} _isYUITreeView
+ @default true
+ @protected
+ **/
+ _isYUITreeView: true,
+
+ /**
+ Cached value of the `lazyRender` attribute.
+
+ @property {Boolean} _lazyRender
+ @protected
+ **/
+
+ // -- Lifecycle Methods ----------------------------------------------------
+
+ initializer: function (config) {
+ if (config && config.templates) {
+ this.templates = Y.merge(this.templates, config.templates);
+ }
+
+ this._renderQueue = {};
+ this._attachTreeViewEvents();
+ },
+
+ destructor: function () {
+ clearTimeout(this._renderTimeout);
+ this._detachTreeViewEvents();
+
+ this._renderQueue = null;
+ },
+
+ // -- Public Methods -------------------------------------------------------
+
+ destroyNode: function (node, options) {
+ node._htmlNode = null;
+ return Y.Tree.prototype.destroyNode.call(this, node, options);
+ },
+
+ /**
+ Returns the HTML node (as a `Y.Node` instance) associated with the specified
+ `Tree.Node` instance, if any.
+
+ @method getHTMLNode
+ @param {Tree.Node} treeNode Tree node.
+ @return {Node} `Y.Node` instance associated with the given tree node, or
+ `undefined` if one was not found.
+ **/
+ getHTMLNode: function (treeNode) {
+ if (!treeNode._htmlNode) {
+ treeNode._htmlNode = this.get('container').one('#' + treeNode.id);
+ }
+
+ return treeNode._htmlNode;
+ },
+
+ /**
+ Renders this TreeView into its container.
+
+ If the container hasn't already been added to the current document, it will
+ be appended to the `` element.
+
+ @method render
+ @chainable
+ **/
+ render: function () {
+ var container = this.get('container'),
+ isTouchDevice = 'ontouchstart' in Y.config.win;
+
+ container.addClass(this.classNames.treeview);
+ container.addClass(this.classNames[isTouchDevice ? 'touch' : 'noTouch']);
+
+ this._childrenNode = this.renderChildren(this.rootNode, {
+ container: container
+ });
+
+ if (!container.inDoc()) {
+ Y.one('body').append(container);
+ }
+
+ this.rendered = true;
+
+ return this;
+ },
+
+ /**
+ Renders the children of the specified tree node.
+
+ If a container is specified, it will be assumed to be an existing rendered
+ tree node, and the children will be rendered (or re-rendered) inside it.
+
+ @method renderChildren
+ @param {Tree.Node} treeNode Tree node whose children should be rendered.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container into
+ which the children should be rendered. If the container already
+ contains rendered children, they will be re-rendered in place.
+
+ @param {Boolean} [options.force=false] If `true`, children will be
+ re-rendered from scratch even if they've already been rendered.
+
+ @return {Node} `Y.Node` instance containing the rendered children.
+ **/
+ renderChildren: function (treeNode, options) {
+ options || (options = {});
+
+ var container = options.container,
+ childrenNode = container && container.one('>.' + this.classNames.children),
+ lazyRender = this._lazyRender;
+
+ if (childrenNode && options.force) {
+ childrenNode.remove(true);
+ childrenNode = null;
+ }
+
+ if (!childrenNode) {
+ childrenNode = Y.Node.create(this.templates.children({
+ classNames: this.classNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ if (treeNode.hasChildren()) {
+ childrenNode.set('aria-expanded', treeNode.isOpen());
+
+ for (var i = 0, len = treeNode.children.length; i < len; i++) {
+ var child = treeNode.children[i];
+
+ this.renderNode(child, {
+ container : childrenNode,
+ renderChildren: !lazyRender || child.isOpen()
+ });
+ }
+ }
+
+ // Keep track of whether or not this node's children have been rendered
+ // so we'll know whether we need to render them later if the node is
+ // opened.
+ treeNode.state.renderedChildren = true;
+
+ if (container) {
+ container.append(childrenNode);
+ }
+
+ return childrenNode;
+ },
+
+ /**
+ Renders the specified tree node and its children (if any).
+
+ If a container is specified, the rendered node will be appended to it.
+
+ @method renderNode
+ @param {Tree.Node} treeNode Tree node to render.
+ @param {Object} [options] Options.
+
+ @param {Node} [options.container] `Y.Node` instance of a container to
+ which the rendered tree node should be appended.
+
+ @param {Boolean} [options.force=false] If `true`, this node (and its
+ children if `renderChildren` is `true`) will be re-rendered from
+ scratch, even if it's already been rendered.
+
+ @param {Boolean} [options.renderChildren=false] Whether or not to render
+ this node's children.
+
+ @return {Node} `Y.Node` instance of the rendered tree node.
+ **/
+ renderNode: function (treeNode, options) {
+ options || (options = {});
+
+ var classNames = this.classNames,
+ hasChildren = treeNode.hasChildren(),
+ htmlNode = treeNode._htmlNode,
+ oldHtmlNode = options.force && htmlNode,
+ nodeClassNames = {},
+
+ className;
+
+ // Build the hash of CSS classes for this node.
+ nodeClassNames[classNames.node] = true;
+ nodeClassNames[classNames.canHaveChildren] = !!treeNode.canHaveChildren;
+ nodeClassNames[classNames.hasChildren] = hasChildren;
+
+ if (htmlNode && !options.force) {
+ // This node has already been rendered, so we just need to update
+ // the DOM instead of re-rendering it from scratch.
+ htmlNode.one('.' + classNames.label).setHTML(treeNode.label);
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className)) {
+ htmlNode.toggleClass(className, nodeClassNames[className]);
+ }
+ }
+ } else {
+ // This node hasn't been rendered yet or is being forcibly
+ // re-rendered.
+ var enabledClassNames = [];
+
+ for (className in nodeClassNames) {
+ if (nodeClassNames.hasOwnProperty(className) && nodeClassNames[className]) {
+ enabledClassNames.push(className);
+ }
+ }
+
+ htmlNode = treeNode._htmlNode = Y.Node.create(this.templates.node({
+ classNames : classNames,
+ nodeClassNames: enabledClassNames,
+ node : treeNode,
+ treeview : this // not currently used, but may be useful for custom templates
+ }));
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ this._syncNodeSelectedState(treeNode, htmlNode);
+
+ if (hasChildren) {
+ if (options.renderChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+ } else {
+ // If children were previously rendered but this node no longer has
+ // children, remove the empty child list.
+ var childrenNode = htmlNode.one('>.' + classNames.children);
+
+ if (childrenNode) {
+ childrenNode.remove(true);
+ }
+ }
+
+ treeNode.state.rendered = true;
+
+ if (options.force) {
+ oldHtmlNode.replace(htmlNode);
+ oldHtmlNode.destroy();
+ } else {
+ if (options.container && htmlNode.get('parentNode') !== options.container) {
+ options.container.append(htmlNode);
+ }
+ }
+
+ return htmlNode;
+ },
+
+ // -- Protected Methods ----------------------------------------------------
+
+ _attachTreeViewEvents: function () {
+ this._treeViewEvents || (this._treeViewEvents = []);
+
+ var classNames = this.classNames,
+ container = this.get('container');
+
+ this._treeViewEvents.push(
+ // Custom events.
+ this.after({
+ add : this._afterAdd,
+ clear : this._afterClear,
+ close : this._afterClose,
+ multiSelectChange: this._afterTreeViewMultiSelectChange, // sheesh
+ open : this._afterOpen,
+ remove : this._afterRemove,
+ select : this._afterSelect,
+ unselect : this._afterUnselect
+ }),
+
+ // DOM events.
+ container.on('mousedown', this._onMouseDown, this),
+
+ container.delegate('click', this._onIndicatorClick,
+ '.' + classNames.indicator, this),
+
+ container.delegate('click', this._onRowClick,
+ '.' + classNames.row, this),
+
+ container.delegate('dblclick', this._onRowDoubleClick,
+ '.' + classNames.canHaveChildren + ' > .' + classNames.row, this)
+ );
+ },
+
+ _detachTreeViewEvents: function () {
+ (new Y.EventHandle(this._treeViewEvents)).detach();
+ },
+
+ _processRenderQueue: function () {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ node;
+
+ for (var id in queue) {
+ if (queue.hasOwnProperty(id)) {
+ node = this.getNodeById(id);
+
+ if (node) {
+ this.renderNode(node, queue[id]);
+ }
+ }
+ }
+
+ this._renderQueue = {};
+ },
+
+ _queueRender: function (node, options) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var queue = this._renderQueue,
+ self = this;
+
+ clearTimeout(this._renderTimeout);
+
+ queue[node.id] = Y.merge(queue[node.id], options);
+
+ this._renderTimeout = setTimeout(function () {
+ self._processRenderQueue();
+ }, 15);
+
+ return this;
+ },
+
+ /**
+ Setter for the `lazyRender` attribute.
+
+ Just caches the value in a property for faster lookups.
+
+ @method _setLazyRender
+ @return {Boolean} Value.
+ @protected
+ **/
+ _setLazyRender: function (value) {
+ /*jshint boss:true */
+ return this._lazyRender = value;
+ },
+
+ _syncNodeOpenState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ if (node.isOpen()) {
+ htmlNode
+ .addClass(this.classNames.open)
+ .set('aria-expanded', true);
+ } else {
+ htmlNode
+ .removeClass(this.classNames.open)
+ .set('aria-expanded', false);
+ }
+ },
+
+ _syncNodeSelectedState: function (node, htmlNode) {
+ htmlNode || (htmlNode = this.getHTMLNode(node));
+
+ if (!htmlNode) {
+ return;
+ }
+
+ var multiSelect = this.get('multiSelect');
+
+ if (node.isSelected()) {
+ htmlNode.addClass(this.classNames.selected);
+
+ if (multiSelect) {
+ // It's only necessary to set aria-selected when multi-select is
+ // enabled and focus can't be used to track the selection state.
+ htmlNode.set('aria-selected', true);
+ } else {
+ htmlNode.set('tabIndex', 0);
+ }
+ } else {
+ htmlNode
+ .removeClass(this.classNames.selected)
+ .removeAttribute('tabIndex');
+
+ if (multiSelect) {
+ htmlNode.set('aria-selected', false);
+ }
+ }
+ },
+
+ // -- Protected Event Handlers ---------------------------------------------
+
+ _afterAdd: function (e) {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ var parent = e.parent,
+ parentIsRoot = parent.isRoot(),
+ treeNode = e.node,
+
+ htmlChildren,
+ htmlParent;
+
+ if (parentIsRoot) {
+ htmlChildren = this._childrenNode;
+ } else {
+ htmlParent = this.getHTMLNode(parent),
+ htmlChildren = htmlParent && htmlParent.one('>.' + this.classNames.children);
+ }
+
+ if (htmlChildren) {
+ // Parent's children have already been rendered. Instead of
+ // re-rendering all of them, just render the new node and insert it
+ // at the correct position.
+ htmlChildren.insert(this.renderNode(treeNode, {
+ renderChildren: !this._lazyRender || treeNode.isOpen()
+ }), e.index);
+
+ // Schedule the parent node to be re-rendered in order to update its
+ // state. This is done asynchronously and throttled in order to
+ // avoid re-rendering the parent many times if multiple children are
+ // added in quick succession.
+ if (!parentIsRoot) {
+ this._queueRender(parent);
+ }
+ } else if (!parentIsRoot) {
+ // Either the parent hasn't been rendered yet, or its children
+ // haven't been rendered yet. Schedule it to be rendered. This is
+ // done asynchronously and throttled in order to avoid re-rendering
+ // the parent many times if multiple children are added in quick
+ // succession.
+ this._queueRender(parent, {renderChildren: true});
+ }
+ },
+
+ _afterClear: function () {
+ // Nothing to do if the treeview hasn't been rendered yet.
+ if (!this.rendered) {
+ return;
+ }
+
+ clearTimeout(this._renderTimeout);
+ this._renderQueue = {};
+
+ delete this._childrenNode;
+ this.rendered = false;
+
+ this.get('container').empty();
+ this.render();
+ },
+
+ _afterClose: function (e) {
+ if (this.rendered) {
+ this._syncNodeOpenState(e.node);
+ }
+ },
+
+ _afterOpen: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ htmlNode = this.getHTMLNode(treeNode);
+
+ // If this node's children haven't been rendered yet, render them.
+ if (!treeNode.state.renderedChildren) {
+ this.renderChildren(treeNode, {
+ container: htmlNode
+ });
+ }
+
+ this._syncNodeOpenState(treeNode, htmlNode);
+ },
+
+ _afterRemove: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var treeNode = e.node,
+ parent = e.parent;
+
+ // If this node is in the render queue, remove it from the queue.
+ if (this._renderQueue[treeNode.id]) {
+ delete this._renderQueue[treeNode.id];
+ }
+
+ // Remove DOM nodes associated with this node and any of its
+ // descendants, and mark all nodes as unrendered so that they'll be
+ // re-rendered if they're reinserted in the tree.
+ var htmlNode = this.getHTMLNode(treeNode);
+
+ if (htmlNode) {
+ htmlNode
+ .empty()
+ .remove(true);
+
+ treeNode._htmlNode = null;
+ }
+
+ if (!treeNode.state.destroyed) {
+ treeNode.traverse(function (node) {
+ node._htmlNode = null;
+ node.state.rendered = false;
+ node.state.renderedChildren = false;
+ });
+ }
+
+ // Re-render the parent to update its state if this was its last child.
+ if (parent && !parent.hasChildren()) {
+ this.renderNode(parent);
+ }
+ },
+
+ _afterSelect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _afterTreeViewMultiSelectChange: function (e) {
+ if (!this.rendered) {
+ return;
+ }
+
+ var container = this.get('container'),
+ rootList = container.one('> .' + this.classNames.children),
+ htmlNodes = container.all('.' + this.classNames.node);
+
+ if (e.newVal) {
+ rootList.set('aria-multiselectable', true);
+ htmlNodes.set('aria-selected', false);
+ } else {
+ // When multiselect is disabled, aria-selected must not be set on
+ // any nodes, since focus is used to indicate selection.
+ rootList.removeAttribute('aria-multiselectable');
+ htmlNodes.removeAttribute('aria-selected');
+ }
+ },
+
+ _afterUnselect: function (e) {
+ if (this.rendered) {
+ this._syncNodeSelectedState(e.node);
+ }
+ },
+
+ _onIndicatorClick: function (e) {
+ var rowNode = e.currentTarget.ancestor('.' + this.classNames.row);
+
+ // Indicator clicks shouldn't toggle selection state, so don't allow
+ // this event to propagate to the _onRowClick() handler.
+ e.stopImmediatePropagation();
+
+ this.getNodeById(rowNode.getData('node-id')).toggleOpen();
+ },
+
+ _onMouseDown: function (e) {
+ // This prevents the tree from momentarily grabbing focus before focus
+ // is set on a node.
+ e.preventDefault();
+ },
+
+ _onRowClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ var node = this.getNodeById(e.currentTarget.getData('node-id'));
+
+ if (this.get('multiSelect')) {
+ node[node.isSelected() ? 'unselect' : 'select']();
+ } else {
+ node.select();
+ }
+ },
+
+ _onRowDoubleClick: function (e) {
+ // Ignore buttons other than the left button.
+ if (e.button > 1) {
+ return;
+ }
+
+ this.getNodeById(e.currentTarget.getData('node-id')).toggleOpen();
+ }
+}, {
+ ATTRS: {
+ /**
+ When `true`, a node's children won't be rendered until the first time
+ that node is opened.
+
+ This can significantly speed up the time it takes to render a large
+ tree, but might not make sense if you're using CSS that doesn't hide the
+ contents of closed nodes.
+
+ @attribute {Boolean} lazyRender
+ @default true
+ **/
+ lazyRender: {
+ lazyAdd: false, // to ensure that the setter runs on init
+ setter : '_setLazyRender',
+ value : true
+ }
+ }
+});
+
+Y.TreeView = Y.mix(TreeView, Y.TreeView);
+
+
+}, '@VERSION@', {
+ "requires": [
+ "base-build",
+ "classnamemanager",
+ "template-micro",
+ "tree",
+ "tree-labelable",
+ "tree-openable",
+ "tree-selectable",
+ "view"
+ ],
+ "skinnable": true
+});
diff --git a/mod/scorm/yui/src/treeview/js/gallery-sm-treeview-sortable-debug.js b/mod/scorm/yui/src/treeview/js/gallery-sm-treeview-sortable-debug.js
new file mode 100644
index 00000000000..76b4fee5574
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/js/gallery-sm-treeview-sortable-debug.js
@@ -0,0 +1,66 @@
+YUI.add('gallery-sm-treeview-sortable', function (Y, NAME) {
+
+/**
+Provides `Y.TreeView.Sortable`, a `Y.TreeView` extension that mixes in
+`Y.Tree.Sortable` and provides related TreeView-specific functionality.
+
+@module gallery-sm-treeview
+@submodule gallery-sm-treeview-sortable
+**/
+
+/**
+Extension for `Y.TreeView` that mixes in `Y.Tree.Sortable` and provides related
+TreeView-specific functionality (such as re-rendering a node after it's sorted).
+
+@class TreeView.Sortable
+@constructor
+@extensionfor TreeView
+@extends Tree.Sortable
+**/
+
+var Sortable = Y.TreeView.Sortable = function () {};
+
+Y.mix(Sortable.prototype, Y.Tree.Sortable.prototype);
+
+// -- Protected Methods ----------------------------------------------------
+
+// Overrides Y.TreeView#_attachTreeViewEvents().
+Sortable.prototype._attachTreeViewEvents = function () {
+ Y.TreeView.prototype._attachTreeViewEvents.call(this);
+
+ this._treeViewEvents.push(
+ this.after('sort', this._afterSort)
+ );
+};
+
+// -- Event Handlers -------------------------------------------------------
+
+/**
+Re-renders a node if necessary after a `sort` event.
+
+@method _afterSort
+@param {EventFacade} e
+@protected
+**/
+Sortable.prototype._afterSort = function (e) {
+ var node = e.node;
+
+ // If this tree hasn't been rendered yet or the sorted node's children
+ // haven't been rendered yet, there's nothing to do.
+ if (!this.rendered || !node.state.renderedChildren) {
+ return;
+ }
+
+ // Re-render the sorted node and its children.
+ if (node.isRoot()) {
+ this.render();
+ } else {
+ this.renderNode(node, {
+ force : true,
+ renderChildren: true
+ });
+ }
+};
+
+
+}, '@VERSION@', {"requires": ["gallery-sm-treeview", "tree-sortable"]});
diff --git a/mod/scorm/yui/src/treeview/js/treeview-sortable.js b/mod/scorm/yui/src/treeview/js/treeview-sortable.js
new file mode 100644
index 00000000000..e6448417039
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/js/treeview-sortable.js
@@ -0,0 +1 @@
+Y.use('gallery-sm-treeview-sortable');
diff --git a/mod/scorm/yui/src/treeview/js/treeview.js b/mod/scorm/yui/src/treeview/js/treeview.js
new file mode 100644
index 00000000000..7b2ca1cd85b
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/js/treeview.js
@@ -0,0 +1 @@
+Y.use('gallery-sm-treeview');
diff --git a/mod/scorm/yui/src/treeview/meta/sm-treeview.json b/mod/scorm/yui/src/treeview/meta/sm-treeview.json
new file mode 100644
index 00000000000..ac33dbe08a6
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/meta/sm-treeview.json
@@ -0,0 +1,22 @@
+{
+ "moodle-mod_scorm-treeview": {
+ "requires": [
+ "base-build",
+ "classnamemanager",
+ "template-micro",
+ "tree",
+ "tree-labelable",
+ "tree-openable",
+ "tree-selectable",
+ "view",
+ "moodle-mod_scorm-treeview-skin"
+ ]
+ },
+
+ "moodle-mod_scorm-treeview-sortable": {
+ "requires": [
+ "moodle-mod_scorm-treeview",
+ "tree-sortable"
+ ]
+ }
+}
diff --git a/mod/scorm/yui/src/treeview/readme_moodle.txt b/mod/scorm/yui/src/treeview/readme_moodle.txt
new file mode 100644
index 00000000000..372be724b49
--- /dev/null
+++ b/mod/scorm/yui/src/treeview/readme_moodle.txt
@@ -0,0 +1,21 @@
+Description of gallery-sm-treeview integration in Moodle
+=========================================================================================
+
+Copyright: SmugMug Inc.
+License: BSD
+Repository: https://github.com/smugmug/yui-gallery.git
+
+Moodle maintainer: Andrew Nicols (dobedobedoh)
+
+=========================================================================================
+Upgrade procedure:
+* git clone https://github.com/smugmug/yui-gallery.git
+* from that repository copy:
+** build/gallery-sm-treeview/gallery-sm-treeview-debug.js to js/gallery-sm-treeview-debug.js
+** build/gallery-sm-treeview-sortable/gallery-sm-treeview-sortable-debug.js to js/gallery-sm-treeview-sortable-debug.js
+** build/gallery-sm-treeview/assets/gallery-sm-treeview-core.css to assets/moodle-mod_scorm-treeview-core.css
+** build/gallery-sm-treeview/assets/skins/sam/*.png to assets/skins/sam
+** build/gallery-sm-treeview/assets/skins/sam/gallery-sm-treeview.css to assets/skins/sam/moodle-mod_scorm-treeview.css
+** build/gallery-sm-treeview-skin/assets/skins/sam/gallery-sm-treeview-skin.css to assets/skins/sam/moodle-mod_scorm-treeview-skin.css
+* run shifter on this directory as required
+* update ../../../thirdpartylibs.xml