import { Metadata } from './mining/metadata' ; import { autoinject , Container , Instance } from './libs/container' ; import { CSS_PREFIX , EVENT_SETTINGS_CHANGED , EVENT_TOGGLE_NAVIGATION , EVENT_TOGGLE_EXTENSION } from './constants' ; import { Navigation } from './navigation' ; import { Structure } from './mining/structure' ; import { File } from './file' ; import { Views } from './libs/views' ; import { Settings } from './settings' ; import { PubSub } from './libs/pubsub' ; import { SettingsStore , ISettings } from './settings.store' ; declare const chrome , browser ; @ autoinject export class GitLabTree { isOn : boolean = true ; pathPrefix : string ; fileHolders : NodeList ; fileNames : string [ ] ; strippedFileNames : string [ ] ; wrapperElement : HTMLDivElement = document . createElement ( 'div' ) ; settingsElement : HTMLDivElement = document . createElement ( 'div' ) ; leftElement : HTMLDivElement = document . createElement ( 'div' ) ; rightElement : HTMLDivElement = document . createElement ( 'div' ) ; lastActive : string = '' ; storage = ( chrome || browser ) . storage . local ; hashChangeListener : ( ) => void ; expandListener : ( e : MouseEvent ) => void ; constructor ( private container , private views , private pubsub , private settingStore ) { const files = document . querySelector ( '.files' ) as HTMLElement ; if ( ! files ) { return ; } this . fileHolders = files . querySelectorAll ( '.file-holder' ) ; if ( ! files || this . fileHolders . length === 0 ) { return ; } files . classList . add ( CSS_PREFIX ) ; this . pubsub . subscribe ( EVENT_SETTINGS_CHANGED , ( action , data ) => this . settingsChanged ( action , data ) ) ; this . pubsub . subscribe ( EVENT_TOGGLE_NAVIGATION , ( action , isOpen ) => this . performNavigationIsOpen ( isOpen ) ) ; this . pubsub . subscribe ( EVENT_TOGGLE_EXTENSION , ( action , isOn ) => this . toggleExtensionIsOn ( isOn ) ) ; const metadata = new Metadata ( this . fileHolders ) ; this . container . set ( Metadata , metadata ) ; if ( metadata . getAll ( ) . length === 0 ) { return ; } this . wrapperElement . appendChild ( this . leftElement ) ; this . wrapperElement . appendChild ( this . rightElement ) ; document . body . appendChild ( this . settingsElement ) ; this . wrapperElement . classList . add ( CSS_PREFIX + '-wrapper' ) ; this . leftElement . classList . add ( CSS_PREFIX + '__left' ) ; this . rightElement . classList . add ( CSS_PREFIX + '__right' ) ; this . settingsElement . classList . add ( CSS_PREFIX + '-settings' ) ; const limitingElement = document . querySelector ( '.content-wrapper div.container-fluid.limit-container-width.container-limited' ) ; if ( limitingElement ) { limitingElement . classList . remove ( 'container-limited' ) ; } const navigation = container . get ( Navigation ) ; const settings = container . get ( Settings ) ; this . views . applyView ( 'navigation' , ( ) => navigation . render ( ) , this . leftElement ) ; this . views . applyView ( 'settings' , ( ) => settings . render ( ) , this . settingsElement ) ; this . settingsChanged ( 'internal' , this . settingStore . getAll ( ) ) ; this . copyAndHideFiles ( files ) ; files . appendChild ( this . wrapperElement ) ; const currentFileHash = location . hash ; this . showFile ( currentFileHash ) ; this . hashChangeListener = this . hashChanged . bind ( this ) ; window . addEventListener ( 'hashchange' , this . hashChangeListener ) ; } /**
     * Kind of destructor.
     */ teardown ( ) { this . pubsub . unsubscribeAll ( ) ; window . removeEventListener ( 'hashchange' , this . hashChangeListener ) ; document . removeEventListener ( 'click' , this . expandListener ) ; } param { HTMLElement } files copyAndHideFiles ( files ) { for ( let i = 0 ; i < this . fileHolders . length ; i ++ ) { let fileHolder = this . fileHolders [ i ] as HTMLElement ; files . removeChild ( fileHolder ) ; this . rightElement . appendChild ( fileHolder ) ; fileHolder . classList . add ( CSS_PREFIX + '--is-hidden' , CSS_PREFIX + '__file-holder' ) ; } } /**
     * Callback called after hash has changed (= user clicked on some file). It displays
     * corresponding file (based on id).
     */ hashChanged ( ) { let newHash = location . hash ; this . showFile ( newHash ) ; } param { number } id showFile ( hash ? ) { const metadata = this . container . get ( Metadata ) ; if ( metadata . getAll ( ) . length === 0 ) { return ; } if ( this . lastActive ) { this . getFileHolderByHash ( this . lastActive ) . classList . add ( CSS_PREFIX + '--is-hidden' ) ; this . getFileLinkByHash ( this . lastActive ) . setInactive ( ) ; } hash = metadata . getAll ( ) . filter ( m => m . hash === hash ) . length > 0 ? hash : metadata . getAll ( ) [ 0 ] . hash ; const fileHolder = this . getFileHolderByHash ( hash ) ; fileHolder . classList . remove ( CSS_PREFIX + '--is-hidden' ) ; this . getFileLinkByHash ( hash ) . setActive ( ) ; this . lastActive = hash ; var topOfDiff = fileHolder . offsetTop ; window . scrollTo ( 0 , topOfDiff - 50 ) ; } param { string } action param { } newSettings settingsChanged ( action ? , newSettings ? ) { this . leftElement . style . flexBasis = newSettings [ 'panel-width' ] + 'px' ; } param { boolean } isOpen performNavigationIsOpen ( isOpen ? ) { const className = 'gitlab-tree-plugin__left--is-collapsed' ; this . leftElement . classList . remove ( className ) ; if ( ! isOpen ) { this . leftElement . classList . add ( className ) ; } } param { boolean } isOn toggleExtensionIsOn ( isOn ? ) { this . isOn = isOn !== undefined ? isOn : ! this . isOn ; if ( this . isOn ) { const fileHolders = this . rightElement . querySelectorAll ( `.${ CSS_PREFIX }__file-holder` ) ; fileHolders . forEach ( file => file . classList . add ( `${ CSS_PREFIX }--is-hidden` ) ) ; this . showFile ( this . lastActive ) ; } else { const fileHolders = this . rightElement . querySelectorAll ( `.${ CSS_PREFIX }--is-hidden` ) ; fileHolders . forEach ( file => file . classList . remove ( `${ CSS_PREFIX }--is-hidden` ) ) ; } } private getFileHolderByHash ( hash ) { return this . rightElement . querySelector ( `[id='${ hash . substr ( 1 ) }']` ) as HTMLElement ; } private getFileLinkByHash ( hash ) { const structure = this . container . get ( Structure ) ; return structure . flatFileStructure . find ( file => file . state . hash === hash ) ; } }