IFrame.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. /**
  2. * --------------------------------------------
  3. * AdminLTE IFrame.js
  4. * License MIT
  5. * --------------------------------------------
  6. */
  7. import $ from 'jquery'
  8. /**
  9. * Constants
  10. * ====================================================
  11. */
  12. const NAME = 'IFrame'
  13. const DATA_KEY = 'lte.iframe'
  14. const JQUERY_NO_CONFLICT = $.fn[NAME]
  15. const SELECTOR_DATA_TOGGLE = '[data-widget="iframe"]'
  16. const SELECTOR_DATA_TOGGLE_CLOSE = '[data-widget="iframe-close"]'
  17. const SELECTOR_DATA_TOGGLE_SCROLL_LEFT = '[data-widget="iframe-scrollleft"]'
  18. const SELECTOR_DATA_TOGGLE_SCROLL_RIGHT = '[data-widget="iframe-scrollright"]'
  19. const SELECTOR_DATA_TOGGLE_FULLSCREEN = '[data-widget="iframe-fullscreen"]'
  20. const SELECTOR_CONTENT_WRAPPER = '.content-wrapper'
  21. const SELECTOR_CONTENT_IFRAME = `${SELECTOR_CONTENT_WRAPPER} iframe`
  22. const SELECTOR_TAB_NAV = `${SELECTOR_DATA_TOGGLE}.iframe-mode .nav`
  23. const SELECTOR_TAB_NAVBAR_NAV = `${SELECTOR_DATA_TOGGLE}.iframe-mode .navbar-nav`
  24. const SELECTOR_TAB_NAVBAR_NAV_ITEM = `${SELECTOR_TAB_NAVBAR_NAV} .nav-item`
  25. const SELECTOR_TAB_CONTENT = `${SELECTOR_DATA_TOGGLE}.iframe-mode .tab-content`
  26. const SELECTOR_TAB_EMPTY = `${SELECTOR_TAB_CONTENT} .tab-empty`
  27. const SELECTOR_TAB_LOADING = `${SELECTOR_TAB_CONTENT} .tab-loading`
  28. const SELECTOR_SIDEBAR_MENU_ITEM = '.main-sidebar .nav-item > a.nav-link'
  29. const SELECTOR_SIDEBAR_SEARCH_ITEM = '.sidebar-search-results .list-group-item'
  30. const SELECTOR_HEADER_MENU_ITEM = '.main-header .nav-item a.nav-link'
  31. const SELECTOR_HEADER_DROPDOWN_ITEM = '.main-header a.dropdown-item'
  32. const CLASS_NAME_IFRAME_MODE = 'iframe-mode'
  33. const CLASS_NAME_FULLSCREEN_MODE = 'iframe-mode-fullscreen'
  34. const Default = {
  35. onTabClick(item) {
  36. return item
  37. },
  38. onTabChanged(item) {
  39. return item
  40. },
  41. onTabCreated(item) {
  42. return item
  43. },
  44. autoIframeMode: true,
  45. autoItemActive: true,
  46. autoShowNewTab: true,
  47. allowDuplicates: false,
  48. loadingScreen: true,
  49. useNavbarItems: true,
  50. scrollOffset: 40,
  51. scrollBehaviorSwap: false,
  52. iconMaximize: 'fa-expand',
  53. iconMinimize: 'fa-compress'
  54. }
  55. /**
  56. * Class Definition
  57. * ====================================================
  58. */
  59. class IFrame {
  60. constructor(element, config) {
  61. this._config = config
  62. this._element = element
  63. this._init()
  64. }
  65. // Public
  66. onTabClick(item) {
  67. this._config.onTabClick(item)
  68. }
  69. onTabChanged(item) {
  70. this._config.onTabChanged(item)
  71. }
  72. onTabCreated(item) {
  73. this._config.onTabCreated(item)
  74. }
  75. createTab(title, link, uniqueName, autoOpen) {
  76. let tabId = `panel-${uniqueName}`
  77. let navId = `tab-${uniqueName}`
  78. if (this._config.allowDuplicates) {
  79. tabId += `-${Math.floor(Math.random() * 1000)}`
  80. navId += `-${Math.floor(Math.random() * 1000)}`
  81. }
  82. const newNavItem = `<li class="nav-item" role="presentation"><a class="nav-link" data-toggle="row" id="${navId}" href="#${tabId}" role="tab" aria-controls="${tabId}" aria-selected="false">${title}</a></li>`
  83. $(SELECTOR_TAB_NAVBAR_NAV).append(unescape(escape(newNavItem)))
  84. const newTabItem = `<div class="tab-pane fade" id="${tabId}" role="tabpanel" aria-labelledby="${navId}"><iframe src="${link}"></iframe></div>`
  85. $(SELECTOR_TAB_CONTENT).append(unescape(escape(newTabItem)))
  86. if (autoOpen) {
  87. if (this._config.loadingScreen) {
  88. const $loadingScreen = $(SELECTOR_TAB_LOADING)
  89. $loadingScreen.fadeIn()
  90. $(`${tabId} iframe`).ready(() => {
  91. if (typeof this._config.loadingScreen === 'number') {
  92. this.switchTab(`#${navId}`, this._config.loadingScreen)
  93. setTimeout(() => {
  94. $loadingScreen.fadeOut()
  95. }, this._config.loadingScreen)
  96. } else {
  97. this.switchTab(`#${navId}`, this._config.loadingScreen)
  98. $loadingScreen.fadeOut()
  99. }
  100. })
  101. } else {
  102. this.switchTab(`#${navId}`)
  103. }
  104. }
  105. this.onTabCreated($(`#${navId}`))
  106. }
  107. openTabSidebar(item, autoOpen = this._config.autoShowNewTab) {
  108. let $item = $(item).clone()
  109. if ($item.attr('href') === undefined) {
  110. $item = $(item).parent('a').clone()
  111. }
  112. $item.find('.right, .search-path').remove()
  113. let title = $item.find('p').text()
  114. if (title === '') {
  115. title = $item.text()
  116. }
  117. const link = $item.attr('href')
  118. if (link === '#' || link === '' || link === undefined) {
  119. return
  120. }
  121. const uniqueName = link.replace('.html', '').replace('./', '').replace(/["&'./=?[\]]/gi, '-').replace(/(--)/gi, '')
  122. const navId = `tab-${uniqueName}`
  123. // eslint-disable-next-line no-console
  124. console.log($(`#${navId}`))
  125. // eslint-disable-next-line no-console
  126. console.log(!this._config.allowDuplicates && $(`#${navId}`).length === 0)
  127. if (!this._config.allowDuplicates && $(`#${navId}`).length > 0) {
  128. return this.switchTab(`#${navId}`)
  129. }
  130. if ((!this._config.allowDuplicates && $(`#${navId}`).length === 0) || this._config.allowDuplicates) {
  131. this.createTab(title, link, uniqueName, autoOpen)
  132. }
  133. }
  134. switchTab(item) {
  135. const $item = $(item)
  136. const tabId = $item.attr('href')
  137. $(SELECTOR_TAB_EMPTY).hide()
  138. $(`${SELECTOR_TAB_NAVBAR_NAV} .active`).tab('dispose').removeClass('active')
  139. this._fixHeight()
  140. $item.tab('show')
  141. $item.parents('li').addClass('active')
  142. this.onTabChanged($item)
  143. if (this._config.autoItemActive) {
  144. this._setItemActive($(`${tabId} iframe`).attr('src'))
  145. }
  146. }
  147. removeActiveTab() {
  148. const $navItem = $(`${SELECTOR_TAB_NAVBAR_NAV_ITEM}.active`)
  149. const $navItemParent = $navItem.parent()
  150. const navItemIndex = $navItem.index()
  151. $navItem.remove()
  152. $('.tab-pane.active').remove()
  153. if ($(SELECTOR_TAB_CONTENT).children().length == $(`${SELECTOR_TAB_EMPTY}, ${SELECTOR_TAB_LOADING}`).length) {
  154. $(SELECTOR_TAB_EMPTY).show()
  155. } else {
  156. const prevNavItemIndex = navItemIndex - 1
  157. this.switchTab($navItemParent.children().eq(prevNavItemIndex).find('a'))
  158. }
  159. }
  160. toggleFullscreen() {
  161. if ($('body').hasClass(CLASS_NAME_FULLSCREEN_MODE)) {
  162. $(`${SELECTOR_DATA_TOGGLE_FULLSCREEN} i`).removeClass(this._config.iconMinimize).addClass(this._config.iconMaximize)
  163. $('body').removeClass(CLASS_NAME_FULLSCREEN_MODE)
  164. $(`${SELECTOR_TAB_EMPTY}, ${SELECTOR_TAB_LOADING}`).height('auto')
  165. $(SELECTOR_CONTENT_WRAPPER).height('auto')
  166. $(SELECTOR_CONTENT_IFRAME).height('auto')
  167. } else {
  168. $(`${SELECTOR_DATA_TOGGLE_FULLSCREEN} i`).removeClass(this._config.iconMaximize).addClass(this._config.iconMinimize)
  169. $('body').addClass(CLASS_NAME_FULLSCREEN_MODE)
  170. }
  171. $(window).trigger('resize')
  172. this._fixHeight(true)
  173. }
  174. // Private
  175. _init() {
  176. if (window.frameElement && this._config.autoIframeMode) {
  177. $('body').addClass(CLASS_NAME_IFRAME_MODE)
  178. } else if ($(SELECTOR_CONTENT_WRAPPER).hasClass(CLASS_NAME_IFRAME_MODE)) {
  179. this._setupListeners()
  180. this._fixHeight(true)
  181. }
  182. }
  183. _navScroll(offset) {
  184. const leftPos = $(SELECTOR_TAB_NAVBAR_NAV).scrollLeft()
  185. $(SELECTOR_TAB_NAVBAR_NAV).animate({ scrollLeft: (leftPos + offset) }, 250, 'linear')
  186. }
  187. _setupListeners() {
  188. $(window).on('resize', () => {
  189. setTimeout(() => {
  190. this._fixHeight()
  191. }, 1)
  192. })
  193. $(document).on('click', `${SELECTOR_SIDEBAR_MENU_ITEM}, ${SELECTOR_SIDEBAR_SEARCH_ITEM}`, e => {
  194. e.preventDefault()
  195. this.openTabSidebar(e.target)
  196. })
  197. if (this._config.useNavbarItems) {
  198. $(document).on('click', `${SELECTOR_HEADER_MENU_ITEM}, ${SELECTOR_HEADER_DROPDOWN_ITEM}`, e => {
  199. e.preventDefault()
  200. this.openTabSidebar(e.target)
  201. })
  202. }
  203. $(document).on('click', SELECTOR_TAB_NAVBAR_NAV_ITEM, e => {
  204. e.preventDefault()
  205. this.onTabClick(e.target)
  206. this.switchTab(e.target)
  207. })
  208. $(document).on('click', SELECTOR_DATA_TOGGLE_CLOSE, e => {
  209. e.preventDefault()
  210. this.removeActiveTab()
  211. })
  212. $(document).on('click', SELECTOR_DATA_TOGGLE_FULLSCREEN, e => {
  213. e.preventDefault()
  214. this.toggleFullscreen()
  215. })
  216. let mousedown = false
  217. let mousedownInterval = null
  218. $(document).on('mousedown', SELECTOR_DATA_TOGGLE_SCROLL_LEFT, e => {
  219. e.preventDefault()
  220. clearInterval(mousedownInterval)
  221. let { scrollOffset } = this._config
  222. if (!this._config.scrollBehaviorSwap) {
  223. scrollOffset = -scrollOffset
  224. }
  225. mousedown = true
  226. this._navScroll(scrollOffset)
  227. mousedownInterval = setInterval(() => {
  228. this._navScroll(scrollOffset)
  229. }, 250)
  230. })
  231. $(document).on('mousedown', SELECTOR_DATA_TOGGLE_SCROLL_RIGHT, e => {
  232. e.preventDefault()
  233. clearInterval(mousedownInterval)
  234. let { scrollOffset } = this._config
  235. if (this._config.scrollBehaviorSwap) {
  236. scrollOffset = -scrollOffset
  237. }
  238. mousedown = true
  239. this._navScroll(scrollOffset)
  240. mousedownInterval = setInterval(() => {
  241. this._navScroll(scrollOffset)
  242. }, 250)
  243. })
  244. $(document).on('mouseup', () => {
  245. if (mousedown) {
  246. mousedown = false
  247. clearInterval(mousedownInterval)
  248. mousedownInterval = null
  249. }
  250. })
  251. }
  252. _setItemActive(href) {
  253. $(`${SELECTOR_SIDEBAR_MENU_ITEM}, ${SELECTOR_HEADER_DROPDOWN_ITEM}`).removeClass('active')
  254. $(SELECTOR_HEADER_MENU_ITEM).parent().removeClass('active')
  255. const $headerMenuItem = $(`${SELECTOR_HEADER_MENU_ITEM}[href$="${href}"]`)
  256. const $headerDropdownItem = $(`${SELECTOR_HEADER_DROPDOWN_ITEM}[href$="${href}"]`)
  257. const $sidebarMenuItem = $(`${SELECTOR_SIDEBAR_MENU_ITEM}[href$="${href}"]`)
  258. $headerMenuItem.each((i, e) => {
  259. $(e).parent().addClass('active')
  260. })
  261. $headerDropdownItem.each((i, e) => {
  262. $(e).addClass('active')
  263. })
  264. $sidebarMenuItem.each((i, e) => {
  265. $(e).addClass('active')
  266. $(e).parents('.nav-treeview').prevAll('.nav-link').addClass('active')
  267. })
  268. }
  269. _fixHeight(tabEmpty = false) {
  270. if ($('body').hasClass(CLASS_NAME_FULLSCREEN_MODE)) {
  271. const windowHeight = $(window).height()
  272. const navbarHeight = $(SELECTOR_TAB_NAV).outerHeight()
  273. $(`${SELECTOR_TAB_EMPTY}, ${SELECTOR_TAB_LOADING}, ${SELECTOR_CONTENT_IFRAME}`).height(windowHeight - navbarHeight)
  274. $(SELECTOR_CONTENT_WRAPPER).height(windowHeight)
  275. } else {
  276. const contentWrapperHeight = parseFloat($(SELECTOR_CONTENT_WRAPPER).css('height'))
  277. const navbarHeight = $(SELECTOR_TAB_NAV).outerHeight()
  278. if (tabEmpty == true) {
  279. setTimeout(() => {
  280. $(`${SELECTOR_TAB_EMPTY}, ${SELECTOR_TAB_LOADING}`).height(contentWrapperHeight - navbarHeight)
  281. }, 50)
  282. } else {
  283. $(SELECTOR_CONTENT_IFRAME).height(contentWrapperHeight - navbarHeight)
  284. }
  285. }
  286. }
  287. // Static
  288. static _jQueryInterface(operation, ...args) {
  289. let data = $(this).data(DATA_KEY)
  290. const _options = $.extend({}, Default, $(this).data())
  291. if (!data) {
  292. data = new IFrame(this, _options)
  293. $(this).data(DATA_KEY, data)
  294. }
  295. if (typeof operation === 'string' && /createTab|openTabSidebar|switchTab|removeActiveTab/.test(operation)) {
  296. data[operation](...args)
  297. }
  298. }
  299. }
  300. /**
  301. * Data API
  302. * ====================================================
  303. */
  304. $(window).on('load', () => {
  305. IFrame._jQueryInterface.call($(SELECTOR_DATA_TOGGLE))
  306. })
  307. /**
  308. * jQuery API
  309. * ====================================================
  310. */
  311. $.fn[NAME] = IFrame._jQueryInterface
  312. $.fn[NAME].Constructor = IFrame
  313. $.fn[NAME].noConflict = function () {
  314. $.fn[NAME] = JQUERY_NO_CONFLICT
  315. return IFrame._jQueryInterface
  316. }
  317. export default IFrame