diff --git a/src/controls/ApplicationWindow.qml b/src/controls/ApplicationWindow.qml index eaddf75..9a2fd35 100644 --- a/src/controls/ApplicationWindow.qml +++ b/src/controls/ApplicationWindow.qml @@ -1,503 +1,505 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import QtQuick.Window 2.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtQuick.Controls.Material 2.1 import "private" Kirigami.AbstractApplicationWindow { id: root visible: true width: Screen.width * (Kirigami.Settings.isMobile ? 1 : 0.4) height: Screen.height * (Kirigami.Settings.isMobile ? 1 : 0.4) contentItem.anchors.leftMargin: root.sideBar && !root.globalDrawer ? ((root.sideBar.collapsible && root.sideBar.collapsed) ? root.sideBar.collapsedSize : (root.sideBar.modal ? 0 : root.sideBar.width * root.sideBar.position)) : (!root.sideBar && root.globalDrawer && (root.globalDrawer.modal === false) ? root.globalDrawer.width * root.globalDrawer.position : 0) property Maui.AbstractSideBar sideBar /***************************************************/ /******************** ALIASES *********************/ /*************************************************/ property alias headBar : _headBar property alias footBar: _footBar property alias dialog: dialogLoader.item property alias leftIcon : menuBtn property alias rightIcon : searchBtn property alias mainMenu : mainMenu.contentData property alias about : aboutDialog property alias accounts: _accountsDialogLoader.item property var currentAccount: Maui.App.accounts.currentAccount property alias notifyDialog: _notify property alias searchButton : searchBtn property alias menuButton : menuBtn wideScreen: isWide /***************************************************/ /*********************** UI ***********************/ /*************************************************/ property bool isWide : root.width >= Kirigami.Units.gridUnit * 30 property string colorSchemeName : Qt.application.name /***************************************************/ /********************* COLORS *********************/ /*************************************************/ property color headBarBGColor: Kirigami.Theme.backgroundColor property color headBarFGColor: Kirigami.Theme.textColor /***************************************************/ /**************** READONLY PROPS ******************/ /*************************************************/ readonly property bool isMobile : Kirigami.Settings.isMobile readonly property bool isAndroid: Qt.platform.os == "android" readonly property real screenWidth : Screen.width readonly property real screenHeight : Screen.height /***************************************************/ /******************** SIGNALS *********************/ /*************************************************/ signal menuButtonClicked(); signal searchButtonClicked(); onClosing: { if(!isMobile) { const height = root.height const width = root.width const x = root.x const y = root.y Maui.FM.saveSettings("GEOMETRY", Qt.rect(x, y, width, height), "WINDOW") } } property bool isPortrait: Screen.primaryOrientation === Qt.PortraitOrientation || Screen.primaryOrientation === Qt.InvertedPortraitOrientation onIsPortraitChanged: { if(isPortrait) { console.log("PORTARIT MODE CHANGED") width: Screen.width height: Screen.height } } // onHeadBarBGColorChanged: // { // if(!isMobile && colorSchemeName.length > 0) // Maui.KDE.setColorScheme(colorSchemeName, headBarBGColor, headBarFGColor) // else if(isAndroid && headBar.position === ToolBar.Header) // Maui.Android.statusbarColor(headBarBGColor, false) // else if(isAndroid && headBar.position === ToolBar.Footer) // Maui.Android.statusbarColor(Kirigami.Theme.viewBackgroundColor, true) // // } // // onHeadBarFGColorChanged: // { // if(!isAndroid && !isMobile && colorSchemeName.length > 0 && headBar.position === ToolBar.Header) // Maui.KDE.setColorScheme(colorSchemeName, headBarBGColor, headBarFGColor) // else if(isAndroid && headBar.position === ToolBar.Header) // Maui.Android.statusbarColor(headBarBGColor, false) // else if(isAndroid && headBar.position === ToolBar.Footer) // Maui.Android.statusbarColor(Kirigami.Theme.viewBackgroundColor, true) // } /* * background: Rectangle * { * color: bgColor } */ Component { id: _accountsComponent Column { - width: parent.width + // height: Math.max( 300, implicitHeight + // _accountsListing.contentHeight) spacing: Maui.Style.space.medium Kirigami.Icon { source: "user-identity" height: Maui.Style.iconSizes.huge width: height anchors.horizontalCenter: parent.horizontalCenter } Label { text: currentAccount.user width: parent.width horizontalAlignment: Qt.AlignHCenter elide: Text.ElideMiddle wrapMode: Text.NoWrap font.bold: true font.weight: Font.Bold } Kirigami.Separator { width: parent.width } ListBrowser { id: _accountsListing width: parent.width + listView.spacing: Maui.Style.space.medium height: Math.min(listView.contentHeight, 300) Kirigami.Theme.backgroundColor: "transparent" model: Maui.BaseModel { list: Maui.App.accounts } delegate: Maui.ListBrowserDelegate { iconSource: "amarok_artist" iconSizeHint: Maui.Style.iconSizes.medium label1.text: model.user label2.text: model.server width: _accountsListing.width height: Maui.Style.rowHeight * 1.2 leftPadding: Maui.Style.space.tiny rightPadding: Maui.Style.space.tiny onClicked: { _accountsListing.currentIndex = index Maui.App.accounts.currentAccountIndex = index } } Component.onCompleted: { if(_accountsListing.count > 0) { _accountsListing.currentIndex = 0 Maui.App.accounts.currentAccountIndex = _accountsListing.currentIndex } } } Kirigami.Separator { width: parent.width } Button { anchors.horizontalCenter: parent.horizontalCenter text: qsTr("Manage accounts") visible: Maui.App.handleAccounts icon.name: "list-add-user" onClicked: { if(root.accounts) accounts.open() mainMenu.close() } } Kirigami.Separator { width: parent.width } } } property Maui.ToolBar mheadBar : Maui.ToolBar { id: _headBar visible: count > 1 position: ToolBar.Header width: root.width height: implicitHeight // Kirigami.Theme.backgroundColor: headBarBGColor // Kirigami.Theme.textColor: headBarFGColor // Kirigami.Theme.inherit: true leftContent: [ ToolButton { id: menuBtn icon.name: "application-menu" icon.color: headBarFGColor icon.width: Maui.Style.iconSizes.medium icon.height: Maui.Style.iconSizes.medium checked: mainMenu.visible onClicked: { menuButtonClicked() mainMenu.visible ? mainMenu.close() : mainMenu.popup(parent, parent.x , parent.height+ Maui.Style.space.medium) } Menu { id: mainMenu modal: true z: 999 width: Maui.Style.unit * 250 Loader { id: _accountsMenuLoader - width: parent.width + width: parent.width * 0.9 + anchors.horizontalCenter: parent.horizontalCenter active: Maui.App.handleAccounts sourceComponent: Maui.App.handleAccounts ? _accountsComponent : null } MenuItem { text: qsTr("About") icon.name: "documentinfo" onTriggered: aboutDialog.open() } } } ] rightContent: ToolButton { id: searchBtn icon.name: "edit-find" icon.color: headBarFGColor onClicked: searchButtonClicked() icon.width: Maui.Style.iconSizes.medium icon.height: Maui.Style.iconSizes.medium } } property Maui.ToolBar mfootBar : Maui.ToolBar { id: _footBar visible: count position: ToolBar.Footer width: root.width height: implicitHeight } header: headBar.count && headBar.position === ToolBar.Header ? headBar : null footer: Column { id: _footer visible : children children: { if(headBar.position === ToolBar.Footer && headBar.count && footBar.count) return [footBar , headBar] else if(headBar.position === ToolBar.Footer && headBar.count) return [headBar] else if(footBar.count) return [footBar] else return [] } } Maui.AboutDialog { id: aboutDialog } Loader { id: _accountsDialogLoader source: Maui.App.handleAccounts ? "private/AccountsHelper.qml" : "" } Maui.Dialog { id: _notify property var cb : ({}) verticalAlignment: Qt.AlignTop defaultButtons: _notify.cb !== null rejectButton.visible: false onAccepted: { if(_notify.cb) { _notify.cb() _notify.close() } } page.padding: Maui.Style.space.medium footBar.background: null maxHeight: Math.max(Maui.Style.iconSizes.large + Maui.Style.space.huge, (_notifyLayout.implicitHeight)) + Maui.Style.space.big + footBar.height maxWidth: Kirigami.Settings.isMobile ? parent.width * 0.9 : Maui.Style.unit * 500 widthHint: 0.8 Timer { id: _notifyTimer onTriggered: { if(_mouseArea.containsPress || _mouseArea.containsMouse) return; _notify.close() } } onClosed: _notifyTimer.stop() GridLayout { anchors.fill: parent columns: 2 rows: 1 Item { Layout.fillHeight: true Layout.preferredWidth: Maui.Style.iconSizes.large + Maui.Style.space.big Layout.row: 1 Layout.column: 1 Kirigami.Icon { id: _notifyIcon width: Maui.Style.iconSizes.large height: width anchors.centerIn: parent fallback : "dialog-warning" } } Item { Layout.fillHeight: true Layout.fillWidth: true Layout.margins: Maui.Style.space.medium Layout.row: 1 Layout.column: 2 ColumnLayout { id: _notifyLayout anchors.centerIn: parent width: parent.width Label { id: _notifyTitle Layout.fillHeight: true Layout.fillWidth: true font.weight: Font.Bold font.bold: true font.pointSize:Maui.Style.fontSizes.big elide: Qt.ElideRight wrapMode: Text.NoWrap } Label { id: _notifyBody Layout.fillHeight: true Layout.fillWidth: true font.pointSize:Maui.Style.fontSizes.default elide: Qt.ElideRight wrapMode: Text.Wrap } } } } MouseArea { id: _mouseArea anchors.fill: parent hoverEnabled: true } function show(callback) { _notify.cb = callback || null _notifyTimer.start() _notify.open() } } Loader { id: dialogLoader } Component.onCompleted: { if(isAndroid && headBar.position === ToolBar.Footer) Maui.Android.statusbarColor(Kirigami.Theme.backgroundColor, true) if(!isMobile) { const rect = Maui.FM.loadSettings("GEOMETRY", "WINDOW", Qt.rect(root.x, root.y, root.width, root.height)) root.x = rect.x root.y = rect.y root.width = rect.width root.height = rect.height } } function notify(icon, title, body, callback, timeout, buttonText) { _notifyIcon.source = icon || "emblem-warning" _notifyTitle.text = title _notifyBody.text = body _notifyTimer.interval = timeout ? timeout : 2500 _notify.acceptButton.text = buttonText || qsTr ("Accept") _notify.show(callback) } } diff --git a/src/controls/SideBar.qml b/src/controls/SideBar.qml index c257845..4400c41 100644 --- a/src/controls/SideBar.qml +++ b/src/controls/SideBar.qml @@ -1,271 +1,272 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.12 import QtQuick.Controls 2.12 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import "private" Maui.AbstractSideBar { id: control implicitWidth: privateProperties.isCollapsed && collapsed && collapsible ? collapsedSize : preferredWidth width: implicitWidth modal: false position: 1 interactive: false default property alias content : _content.data property alias model : _listBrowser.model property alias count : _listBrowser.count property alias section : _listBrowser.section property alias currentIndex: _listBrowser.currentIndex property int iconSize : Maui.Style.iconSizes.small property bool showLabels: control.width > collapsedSize property QtObject privateProperties : QtObject { property bool isCollapsed: control.collapsed } signal itemClicked(int index) signal itemRightClicked(int index) // Connections // { // target: control.Overlay.overlay // onPressed: control.collapse() // } property Component delegate : Maui.ListDelegate { id: itemDelegate iconSize: control.iconSize labelVisible: control.showLabels label: model.label count: model.count > 0 ? model.count : "" iconName: model.icon + (Qt.platform.os == "android" ? ("-sidebar") : "") leftPadding: Maui.Style.space.tiny rightPadding: Maui.Style.space.tiny Connections { target: itemDelegate onClicked: { control.currentIndex = index control.itemClicked(index) } onRightClicked: { control.currentIndex = index control.itemRightClicked(index) } onPressAndHold: { control.currentIndex = index control.itemRightClicked(index) } } } onModalChanged: visible = true visible: true onCollapsedChanged : { if(!collapsible) return if(!collapsed && modal) { modal = false } if(!modal && !collapsed) { privateProperties.isCollapsed = false } if(collapsed && !modal) { privateProperties.isCollapsed = true } } ColumnLayout { id: _content anchors.fill: parent spacing: 0 Maui.ListBrowser { id: _listBrowser Layout.fillHeight: true Layout.fillWidth: true Layout.topMargin: Maui.Style.space.tiny Layout.bottomMargin: Maui.Style.space.tiny Layout.margins: Maui.Style.unit listView.flickableDirection: Flickable.VerticalFlick verticalScrollBarPolicy: Qt.ScrollBarAlwaysOff //this make sthe app crash delegate: control.delegate + Kirigami.Theme.backgroundColor: "transparent" } MouseArea { id: _handle visible: collapsible && collapsed Layout.preferredHeight: Maui.Style.toolBarHeight Layout.fillWidth: true hoverEnabled: true preventStealing: true propagateComposedEvents: false property int startX property int startY Rectangle { anchors.fill: parent color: _handle.containsMouse || _handle.containsPress ? Qt.rgba(Kirigami.Theme.highlightColor.r, Kirigami.Theme.highlightColor.g, Kirigami.Theme.highlightColor.b, 0.2) : "transparent" Kirigami.Separator { anchors { left: parent.left right: parent.right top: parent.top } height: Maui.Style.unit } Kirigami.Icon { source: privateProperties.isCollapsed ? "sidebar-expand" : "sidebar-collapse" color: _handle.containsMouse || _handle.containsPress ? Kirigami.Theme.highlightColor : Kirigami.Theme.textColor anchors.centerIn: parent width: Maui.Style.iconSizes.medium height: width } } onPositionChanged: { if (!pressed || !control.collapsible || !control.collapsed || !Kirigami.Settings.isMobile) return if(mouse.x > control.collapsedSize) { expand() } mouse.accepted = true } onPressed: { startY = mouse.y startX = mouse.x mouse.accepted = true } onReleased: { if(!control.collapsible) return if(mouse.x > control.width) return if(privateProperties.isCollapsed) expand() else collapse() mouse.accepted = true } } } MouseArea { z: control.modal ? applicationWindow().overlay.z + (control.position > 0 ? +1 : -1) : control.background.parent.z + 1 preventStealing: true anchors.horizontalCenter: parent.right anchors.top: parent.top anchors.bottom: parent.bottom visible: Kirigami.Settings.isMobile enabled: control.collapsed && visible width: Maui.Style.space.large property int startX property int startY onPressed: { startY = mouse.y startX = mouse.x mouse.accepted = true } onPositionChanged: { if (!pressed || !control.collapsible || !control.collapsed || !Kirigami.Settings.isMobile) return if(mouse.x > control.collapsedSize) { expand() }else { collapse() } mouse.accepted = true } } function collapse() { if(collapsible && !privateProperties.isCollapsed) { modal = false privateProperties.isCollapsed = true } } function expand() { if(collapsible && privateProperties.isCollapsed) { modal = true privateProperties.isCollapsed = false } } } diff --git a/src/controls/labs/SelectionBar.qml b/src/controls/labs/SelectionBar.qml index 83ca591..9188a59 100644 --- a/src/controls/labs/SelectionBar.qml +++ b/src/controls/labs/SelectionBar.qml @@ -1,432 +1,433 @@ /* * Copyright 2018 Camilo Higuita * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details * * You should have received a copy of the GNU Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.10 import QtQuick.Controls 2.10 import QtQuick.Layouts 1.3 import org.kde.kirigami 2.7 as Kirigami import org.kde.mauikit 1.0 as Maui import QtGraphicalEffects 1.0 Item { id: control focus: true default property list actions Kirigami.Theme.inherit: false Kirigami.Theme.colorSet: Kirigami.Theme.Complementary readonly property int barHeight : Maui.Style.iconSizes.large + Maui.Style.space.medium readonly property alias uris: _private._uris readonly property alias items: _private._items property alias selectionList : selectionList property alias count : selectionList.count readonly property QtObject m_private : QtObject { id: _private property var _uris : [] property var _items : [] } property Component listDelegate: Maui.ItemDelegate { id: delegate height: Maui.Style.rowHeight * 1.5 width: parent.width Kirigami.Theme.backgroundColor: "transparent" Kirigami.Theme.textColor: control.Kirigami.Theme.textColor onClicked: control.itemClicked(index) onPressAndHold: control.itemPressAndHold(index) RowLayout { anchors.fill: parent Item { Layout.fillHeight: true Layout.preferredWidth: _badge.height + Maui.Style.space.small Maui.Badge { id: _badge anchors.centerIn: parent size: Maui.Style.iconSizes.small iconName: "list-remove" onClicked: control.removeAtIndex(index) } } Maui.ListItemTemplate { id: _template Layout.fillWidth: true Layout.fillHeight: true iconVisible: false labelsVisible: true label1.text: model.uri } } } /** * if singleSelection is set to true then only a single item is selected * at time, and replaced with a newe item appended **/ property bool singleSelection: false signal iconClicked() signal cleared() signal exitClicked() signal itemClicked(int index) signal itemPressAndHold(int index) signal itemAdded(var item) signal itemRemoved(var item) signal uriAdded(string uri) signal uriRemoved(string uri) signal clicked(var mouse) signal rightClicked(var mouse) implicitHeight: barHeight implicitWidth: _layout.implicitWidth + Maui.Style.space.big visible: control.count > 0 DropShadow { id: rectShadow anchors.fill: _listContainer cached: true horizontalOffset: 0 verticalOffset: 0 radius: 8.0 samples: 16 color: "#333" smooth: true source: _listContainer } Rectangle { id: _listContainer property bool showList : false height: showList ? Math.min(Math.min(400, control.parent.parent.height), selectionList.contentHeight) + control.height + Maui.Style.space.medium : 0 width: showList ? parent.width : 0 color: Qt.lighter(Kirigami.Theme.backgroundColor) radius: Maui.Style.radiusV focus: true y: ((height) * -1) + control.height x: 0 opacity: showList ? 1 : .97 Behavior on height { NumberAnimation { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } Behavior on width { NumberAnimation { duration: Kirigami.Units.longDuration easing.type: Easing.InOutQuad } } Behavior on opacity { NumberAnimation { duration: Kirigami.Units.shortDuration easing.type: Easing.InOutQuad } } ListView { id: selectionList anchors.fill: parent anchors.margins: Maui.Style.space.medium anchors.bottomMargin: control.height visible: _listContainer.height > 10 highlightFollowsCurrentItem: true highlightMoveDuration: 0 keyNavigationEnabled: true interactive: Kirigami.Settings.isMobile boundsBehavior: !Kirigami.Settings.isMobile? Flickable.StopAtBounds : Flickable.OvershootBounds orientation: ListView.Vertical clip: true focus: true spacing: Maui.Style.space.small ScrollBar.vertical: ScrollBar { policy: Qt.ScrollBarAsNeeded } model: ListModel{} delegate: control.listDelegate } } Rectangle { id: bg anchors.fill: parent color: Kirigami.Theme.backgroundColor radius: Maui.Style.radiusV MouseArea { anchors.fill: parent acceptedButtons: Qt.RightButton | Qt.LeftButton onClicked: { if(!Kirigami.Settings.isMobile && mouse.button === Qt.RightButton) control.rightClicked(mouse) else control.clicked(mouse) } onPressAndHold : { if(Kirigami.Settings.isMobile) control.rightClicked(mouse) } } } RowLayout { anchors.fill: parent ToolButton { icon.name: "dialog-close" Layout.fillHeight: true Layout.preferredWidth: height onClicked: control.exitClicked() Kirigami.Theme.colorSet: control.Kirigami.Theme.colorSet } Maui.ToolBar { id: _layout background: null Layout.fillHeight: true Layout.fillWidth: true middleContent: [ // Kirigami.ActionToolBar // { // display: control.width > Kirigami.Units.gridUnit * 25 ? ToolButton.TextUnderIcon : ToolButton.IconOnly // actions: control.actions // Layout.fillWidth: true // Layout.fillHeight: true // }, - Repeater { model: control.actions ToolButton { action: modelData + Layout.preferredWidth: implicitWidth +Layout.fillHeight: true // display: control.width > Kirigami.Units.gridUnit * 25 ? ToolButton.TextUnderIcon : ToolButton.IconOnly Kirigami.Theme.colorSet: control.Kirigami.Theme.colorSet display: ToolButton.TextUnderIcon onClicked : _listContainer.showList = false } } ] } Maui.Badge { id: _counter Layout.fillHeight: true Layout.preferredWidth: height Layout.margins: Maui.Style.space.medium text: selectionList.count radius: Maui.Style.radiusV Kirigami.Theme.backgroundColor: _listContainer.showList ? Kirigami.Theme.highlightColor : Qt.darker(bg.color) border.color: "transparent" onClicked: { _listContainer.showList = !_listContainer.showList } Component.onCompleted: { _counter.item.font.pointSize= Maui.Style.fontSizes.big } SequentialAnimation { id: anim // PropertyAnimation // { // target: _counter // property: "opacity" // easing.type: Easing.InOutQuad // from: 0.5 // to: 1 // duration: 600 // } // PropertyAnimation { target: _counter property: "radius" easing.type: Easing.InOutQuad from: target.height to: Maui.Style.radiusV duration: 200 } } } } Keys.onEscapePressed: { control.exitClicked(); event.accepted = true } Keys.onBackPressed: { control.exitClicked(); event.accepted = true } function clear() { _private._uris = [] _private._items = [] selectionList.model.clear() control.cleared() } function itemAt(index) { if(index < 0 || index > selectionList.count) return return selectionList.model.get(index) } function removeAtIndex(index) { if(index < 0) return const item = selectionList.model.get(index) const uri = item.uri if(contains(uri)) { _private._uris.splice(index, 1) _private._items.splice(index, 1) selectionList.model.remove(index) control.itemRemoved(item) control.uriRemoved(uri) } } function removeAtUri(uri) { removeAtIndex(indexOf(uri)) } function indexOf(uri) { return _private._uris.indexOf(uri) } function append(uri, item) { const index = _private._uris.indexOf(uri) if(index < 0) { if(control.singleSelection) clear() _private._items.push(item) _private._uris.push(uri) item.uri = uri selectionList.model.append(item) selectionList.positionViewAtEnd() selectionList.currentIndex = selectionList.count - 1 control.itemAdded(item) control.uriAdded(uri) }else { selectionList.currentIndex = index // notify(item.icon, qsTr("Item already selected!"), String("The item '%1' is already in the selection box").arg(item.label), null, 4000) } animate() } function animate() { anim.running = true } function getSelectedUrisString() { return String(""+_private._uris.join(",")) } function contains(uri) { return _private._uris.includes(uri) } }