@@ -57,9 +57,151 @@ function applyDarkMode() {
5757 document . getElementById ( "topbar" ) . classList . toggle ( "dark-mode-topbar" ) ;
5858 document . getElementById ( "toolbar" ) . classList . toggle ( "dark-mode-toolbar" ) ;
5959 document . getElementById ( "navbar" ) . classList . toggle ( "dark-mode-navbar" ) ;
60+ var tabsEl = document . getElementById ( "tabs" ) ;
61+ if ( tabsEl ) tabsEl . classList . toggle ( "dark-mode-topbar" ) ;
6062}
6163
6264function printConsoleArt ( ) {
6365 const consoleStr = `Hello There!` ;
6466 console . log ( consoleStr ) ;
6567}
68+
69+ ( function ( ) {
70+ function debounce ( fn , delay ) {
71+ var t ;
72+ return function ( ) {
73+ var ctx = this , args = arguments ;
74+ clearTimeout ( t ) ;
75+ t = setTimeout ( function ( ) { fn . apply ( ctx , args ) } , delay ) ;
76+ }
77+ }
78+
79+ var TAB_STORAGE_KEY = "tabs.v1" ;
80+ var ACTIVE_TAB_KEY = "activeTabId.v1" ;
81+ var state = {
82+ tabs : [ ] ,
83+ activeId : null ,
84+ els : { list : null , add : null }
85+ } ;
86+
87+ function loadState ( ) {
88+ try { state . tabs = JSON . parse ( localStorage . getItem ( TAB_STORAGE_KEY ) ) || [ ] ; } catch ( e ) { state . tabs = [ ] ; }
89+ state . activeId = localStorage . getItem ( ACTIVE_TAB_KEY ) || ( state . tabs [ 0 ] && state . tabs [ 0 ] . id ) || null ;
90+ }
91+
92+ function saveTabs ( ) {
93+ localStorage . setItem ( TAB_STORAGE_KEY , JSON . stringify ( state . tabs ) ) ;
94+ }
95+
96+ function saveActive ( ) {
97+ if ( ! state . activeId ) return ;
98+ var t = state . tabs . find ( function ( x ) { return x . id === state . activeId } ) ;
99+ if ( ! t ) return ;
100+ if ( typeof quill !== 'undefined' ) {
101+ t . html = quill . root . innerHTML ;
102+ var text = quill . getText ( ) . trim ( ) ;
103+ var title = text . split ( "\n" ) [ 0 ] || "Untitled" ;
104+ if ( title . length > 30 ) title = title . slice ( 0 , 30 ) + "…" ;
105+ t . title = title || t . title || "Untitled" ;
106+ }
107+ saveTabs ( ) ;
108+ }
109+
110+ var debouncedSave = debounce ( saveActive , 500 ) ;
111+
112+ function createTab ( title , html ) {
113+ var id = ( typeof uuid !== 'undefined' && uuid . v4 ) ? uuid . v4 ( ) : ( Date . now ( ) + "" + Math . random ( ) ) ;
114+ var t = { id : id , title : title || "Untitled" , html : html || "" } ;
115+ state . tabs . push ( t ) ;
116+ state . activeId = id ;
117+ localStorage . setItem ( ACTIVE_TAB_KEY , state . activeId ) ;
118+ saveTabs ( ) ;
119+ render ( ) ;
120+ loadIntoEditor ( id ) ;
121+ }
122+
123+ function closeTab ( id ) {
124+ var idx = state . tabs . findIndex ( function ( x ) { return x . id === id } ) ;
125+ if ( idx < 0 ) return ;
126+ var wasActive = state . activeId === id ;
127+ state . tabs . splice ( idx , 1 ) ;
128+ if ( state . tabs . length === 0 ) {
129+ createTab ( ) ;
130+ } else if ( wasActive ) {
131+ var next = state . tabs [ idx ] || state . tabs [ idx - 1 ] || state . tabs [ 0 ] ;
132+ state . activeId = next . id ;
133+ localStorage . setItem ( ACTIVE_TAB_KEY , state . activeId ) ;
134+ loadIntoEditor ( state . activeId ) ;
135+ }
136+ saveTabs ( ) ;
137+ render ( ) ;
138+ }
139+
140+ function switchTab ( id ) {
141+ if ( state . activeId === id ) return ;
142+ saveActive ( ) ;
143+ state . activeId = id ;
144+ localStorage . setItem ( ACTIVE_TAB_KEY , state . activeId ) ;
145+ loadIntoEditor ( id ) ;
146+ render ( ) ;
147+ }
148+
149+ function loadIntoEditor ( id ) {
150+ var t = state . tabs . find ( function ( x ) { return x . id === id } ) ;
151+ if ( ! t || typeof quill === 'undefined' ) return ;
152+ quill . root . innerHTML = '' ;
153+ quill . clipboard . dangerouslyPasteHTML ( 0 , t . html || '' ) ;
154+ quill . setSelection ( 0 ) ;
155+ }
156+
157+ function render ( ) {
158+ if ( ! state . els . list ) return ;
159+ state . els . list . innerHTML = '' ;
160+ state . tabs . forEach ( function ( t ) {
161+ var b = document . createElement ( 'button' ) ;
162+ b . className = 'tab' + ( t . id === state . activeId ? ' active' : '' ) ;
163+ b . setAttribute ( 'data-id' , t . id ) ;
164+ var titleSpan = document . createElement ( 'span' ) ;
165+ titleSpan . className = 'title' ;
166+ titleSpan . textContent = t . title || 'Untitled' ;
167+ var close = document . createElement ( 'span' ) ;
168+ close . className = 'close' ;
169+ close . textContent = '×' ;
170+ close . addEventListener ( 'click' , function ( e ) {
171+ e . stopPropagation ( ) ;
172+ var title = t . title || 'this tab' ;
173+ var ok = window . confirm ( 'Close "' + title + '"? This will permanently remove its content from this browser.' ) ;
174+ if ( ok ) closeTab ( t . id ) ;
175+ } ) ;
176+ b . addEventListener ( 'click' , function ( ) { switchTab ( t . id ) ; } ) ;
177+ b . appendChild ( titleSpan ) ;
178+ b . appendChild ( close ) ;
179+ state . els . list . appendChild ( b ) ;
180+ } ) ;
181+ }
182+
183+ function bindQuill ( ) {
184+ if ( typeof quill === 'undefined' ) return ;
185+ quill . on ( 'text-change' , function ( ) { debouncedSave ( ) ; } ) ;
186+ }
187+
188+ function ensureInitial ( ) {
189+ if ( ! state . tabs || state . tabs . length === 0 ) {
190+ createTab ( ) ;
191+ } else {
192+ render ( ) ;
193+ loadIntoEditor ( state . activeId ) ;
194+ }
195+ }
196+
197+ function initTabs ( ) {
198+ state . els . list = document . getElementById ( 'tab-list' ) ;
199+ state . els . add = document . getElementById ( 'add-tab' ) ;
200+ if ( state . els . add ) { state . els . add . addEventListener ( 'click' , function ( ) { saveActive ( ) ; createTab ( ) ; } ) ; }
201+ loadState ( ) ;
202+ bindQuill ( ) ;
203+ ensureInitial ( ) ;
204+ }
205+
206+ window . initTabs = initTabs ;
207+ } ) ( ) ;
0 commit comments