IFrame.js 11 KB

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