IFrame.js 13 KB

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