UNPKG

teslacam-browser

Version:

A minimal TeslaCam Browser

250 lines (219 loc) 12.2 kB
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>TeslaCam Browser</title> <link rel="stylesheet" href="node_modules/flatpickr/dist/flatpickr.min.css"> <link rel="stylesheet" href="node_modules/bootstrap/dist/css/bootstrap.min.css" /> <link rel="stylesheet" href="node_modules/open-iconic/font/css/open-iconic-bootstrap.css" /> <link rel="stylesheet" href="content/app.css" /> <script type="text/javascript" src="node_modules/jquery/dist/jquery.min.js"></script> <script type="text/javascript" src="node_modules/bootstrap/dist/js/bootstrap.min.js"></script> <script type="text/javascript" src="node_modules/vue/dist/vue.js"></script> <script type="text/javascript" src="node_modules/flatpickr/dist/flatpickr.min.js"></script> <script type="text/javascript" src="content/helpers.js"></script> <script type="text/javascript" src="content/ui.js"></script> </head> <body> <div id="root" class="d-flex flex-column"> <div v-cloak class="d-flex flex-column"> <div class="d-flex flex-column"> <div id="heading" class="d-flex justify-content-between"> <div class="d-flex"> <div class="p-2 align-self-center" @click="showSidebar = !showSidebar"> <button class="btn btn-primary btn-lg" style="width: 60px;" v-show="!showSidebar"> <span class="oi oi-menu" title="Folders"></span> </button> <button class="btn btn-primary btn-lg" style="width: 60px;" v-show="showSidebar"> <span class="oi oi-chevron-left" title="Collapse"></span> </button> </div> <div class="p-2 align-self-baseline text-nowrap"> <span class="heading" @click="showSidebar = !showSidebar">TeslaCam Browser</span> </div> <div class="p-2 align-self-baseline text-nowrap"> <span class="version" id="version">{{ args.version }}</span> </div> <div class="p-2 align-self-baseline text-nowrap"> <span class="credits">by Chris Cavanagh</span> </div> </div> <div class="p-2 donate mt-2"> <div class="d-flex flex-row-reverse"> <form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank"> <input type="hidden" name="cmd" value="_donations" /> <input type="hidden" name="business" value="32J86B5QYPD6Y" /> <input type="hidden" name="currency_code" value="USD" /> <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif" border="0" name="submit" title="PayPal - The safer, easier way to pay online!" alt="Donate with PayPal button" /> <img alt="" border="0" src="https://www.paypal.com/en_US/i/scr/pixel.gif" width="1" height="1" /> </form> </div> <div class="d-flex flex-row custom-control custom-switch mr-2"> <input type="checkbox" v-model="combineClips" class="custom-control-input" id="combineClips"> <label class="custom-control-label" for="combineClips">Combine clips</label> </div> </div> </div> <div id="content" class="d-flex"> <nav id="sidebar" class="d-flex flex-column align-items-start" :class="{ hidden: !showSidebar }"> <div class="navigation overflow-auto"> <div class="px-2 mb-2 overflow-auto"> <button class="btn btn-primary btn-block dropdown-toggle" type="button" @click.prevent="showFolders = !showFolders" style="word-wrap: break-word; white-space: normal; max-width: 320px;"> <span class="oi oi-folder text-warning"></span> <span>{{args.folder}}</span> </button> <div v-show="showFolders"> <div v-for="f, i in args.folderPathParts" :style="{ marginLeft: ( i * 10 ) + 'px' }"> <a href="" @click.prevent="selectedFolder = f.path"> <span class="oi oi-folder text-warning"></span> <span :style="{ fontWeight: i < args.folderPathParts.length - 1 ? 'normal' : 'bold' }">{{f.name}}</span> </a> </div> <div v-for="subfolder in args.subfolders" :style="{ marginLeft: ( args.folderPathParts.length * 10 ) + 'px' }"> <a href="" @click.prevent="selectedFolder = subfolder.path"> <span class="oi oi-folder text-warning"></span> <span>{{subfolder.name}}</span> </a> </div> </div> </div> <div class="d-flex align-items-center"> <div class="px-2 mb-2 flex-grow-1" style="font-size: small;"> <div>{{ args && args.folderInfos ? args.folderInfos.length : 0 }} event{{ args && args.folderInfos && args.folderInfos.length != 1 ? "s" : "" }}</div> <div>{{ args && args.dates ? args.dates.length : 0 }} day{{ args && args.dates && args.dates != 1 ? "s" : "" }}</div> </div> <!--div class="px-2 mb-2 text-nowrap"> <button id="openButton" type="button" class="btn btn-primary" @click="openFolder()">Open...</button> <button id="browseButton" type="button" class="btn btn-secondary" @click="openBrowser()">Open in browser</button> </div--> </div> <div class="px-2 mb-2"> <input id="calendar" class="flatpickr flatpickr-input dates form-control" type="text" placeholder="Select Date..." readonly="readonly" /> </div> <div class="px-2 mb-2"> <div class="list-group list-group-flush overflow-auto times"> <a v-for="t in times" href="" class="list-group-item list-group-item-action" :class="{active: t === selectedTime}" @click.prevent="selectedTime = t" style="cursor: pointer;">{{ t.name }}</a> </div> </div> <div class="px-2 mb-2"> <button id="deleteButton" class="btn btn-danger" @click="deleteFolder( selectedPath )">Delete folder</button> <button id="copyButton" class="btn btn-info" @click="copyPath( selectedPath )">Copy path</button> </div> </div> </nav> <div id="detail" class="d-flex flex-column flex-grow-1"> <div id="videos"> <div class="timespan card mb-2 mx-2" v-if="combineClips"> <div class="d-flex align-items-center titleContainer card-header"> <div class="px-2 controls"> <button class="btn btn-success play" v-show="!controls.playing" @click="controls.playing = !controls.playing"><span class="oi oi-media-play" title="Play"></span></button> <button class="btn btn-warning pause" v-show="controls.playing" @click="controls.playing = !controls.playing"><span class="oi oi-media-pause" title="Pause"></span></button> </div> <div class="px-2 flex-grow-1 controls"> <input class="custom-range scrub" v-model="currentTime" type="range" :max="duration" @input="controls.playing = false"/> </div> <div class="px-2 controls"> <select class="form-control" v-model="controls.speed"> <option value="1">1x</option> <option value="2">2x</option> <option value="10">10x</option> </select> </div> </div> <div class="videoContainer card-body"> <div class="alert alert-info" v-if="loading != null">Loading {{ Math.round( loading ) }}%...<span v-if="selectedTime.time.recent"> (Clips from RecentFiles will take longer to load)</span></div> <video-group :controls="controls" :timespans="timespans"></video-group> <div class="text-center">{{ timespanTime( controls.timespan ) }}</div> </div> </div> <div v-if="!combineClips"> <div v-for="timespan in timespans" class="timespan card mb-2 mx-2"> <div class="d-flex align-items-center titleContainer card-header"> <div class="px-2 flex-grow-1 title" title="Show / hide" @click="timespan.visible = !timespan.visible"><a href="#">{{timespan.visible ? "↑" : "↓"}}</a> {{timespan.title}}</div> <div class="px-2 controls"> <button class="btn btn-danger delete" @click="deleteFiles( timespan )">Delete</button> <button class="btn btn-info copy" @click="copyFilePaths( timespan )">Copy paths</button> <button class="btn btn-success play" v-show="!timespan.playing" @click="playPause( timespan )"><span class="oi oi-media-play" title="Play"></span></button> <button class="btn btn-warning pause" v-show="timespan.playing" @click="playPause( timespan )"><span class="oi oi-media-pause" title="Pause"></span></button> </div> <div class="px-2 flex-grow-1 controls" v-if="timespan.visible"> <input class="custom-range scrub" v-model="timespan.currentTime" type="range" :max="timespan.duration" @input="scrubInput( timespan )"/> </div> <div class="px-2 controls"> <select class="form-control" v-model="controls.speed"> <option value="1">1x</option> <option value="2">2x</option> <option value="10">10x</option> </select> </div> </div> <div class="videoContainer card-body" v-if="timespan.visible"> <videos :controls="controls" :timespan="timespan"></videos> <div class="text-center">{{ timespanTime( timespan ) }}</div> </div> </div> </div> </div> </div> </div> </div> </div> </div> <script type="text/javascript"> (function () { var lastArgs = { folder: "" } function joinFilePaths( filePaths ) { return filePaths.map( f => `"${joinFolderPath( f )}` ).join( " " ) } function joinFolderPath( folderPath ) { return lastArgs.folder + folderPath } function copyToClipboard( value ) { var $temp = $("<input>"); $("body").append( $temp ); $temp.val( value ).select(); document.execCommand( "copy" ); $temp.remove(); } var vueApp = ui.initialize( { openFolders: success => $.getJSON("openFolders", success), reopenFolders: success => $.getJSON("reopenFolders", success), openFolder: (p, success) => $.getJSON( p ? "openFolder" + p : "openDefaultFolder", a => success( lastArgs = a ) ), getFiles: (p, success) => $.getJSON("files/" + p, success), openBrowser: () => $.post("openBrowser"), deleteFiles: files => $.post("deleteFiles", files), deleteFolder: folder => $.post("deleteFolder", folder), copyFilePaths: filePaths => copyToClipboard( joinFilePaths( filePaths ) ), copyPath: path => copyToClipboard( joinFolderPath( path ) ), openExternal: path => window.open(path) } ) })(); </script> </body> </html>