fixed authentication in Linux, showing Timeline now
This commit is contained in:
parent
f58281adc3
commit
b138887371
8 changed files with 196 additions and 85 deletions
|
@ -1,38 +1,70 @@
|
||||||
from PyQt4 import QtCore, QtGui, QtWebKit
|
from PyQt4 import QtCore, QtGui, QtWebKit
|
||||||
|
|
||||||
|
from PyQt4.QtCore import QTimer, QVariant, SIGNAL
|
||||||
|
from PyQt4.QtGui import *
|
||||||
|
from PyQt4.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||||
|
from PyQt4.QtWebKit import QWebView
|
||||||
|
|
||||||
class WebPage(QtWebKit.QWebPage):
|
class WebPage(QtWebKit.QWebPage):
|
||||||
def __init__(self, parent):
|
def __init__(self, parent=0, app=None):
|
||||||
super(QtWebKit.QWebPage, self).__init__(parent)
|
super(QtWebKit.QWebPage, self).__init__(parent)
|
||||||
|
self.app = app
|
||||||
|
|
||||||
def javaScriptConsoleMessage(self, message, lineNumber, sourceId):
|
def javaScriptConsoleMessage(self, message, lineNumber, sourceId):
|
||||||
print str(message) + " on line: " + str(lineNumber) + " Source: " + str(sourceId)
|
print str(message) + " on line: " + str(lineNumber) + " Source: " + str(sourceId)
|
||||||
|
|
||||||
class WebViewCreator(QtGui.QWidget):
|
def checkRequest(self, request):
|
||||||
def __init__(self, app, delegate):
|
print request
|
||||||
|
|
||||||
|
|
||||||
|
class WebViewCreator(QtWebKit.QWebView):
|
||||||
|
def __init__(self, app, local=True):
|
||||||
QtGui.QWidget.__init__(self)
|
QtGui.QWidget.__init__(self)
|
||||||
|
|
||||||
self.app = app
|
self.app = app
|
||||||
self.delegate = delegate
|
self.is_local = local
|
||||||
|
self.setPage(WebPage(self, self.app))
|
||||||
|
|
||||||
self.view = QtWebKit.QWebView(self)
|
def load_local(self, callback=None):
|
||||||
self.view.loadFinished.connect(self.load_finished)
|
self.page().settings().setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)
|
||||||
|
self.loadFinished.connect(lambda ok: self.load_finished(ok, callback))
|
||||||
|
|
||||||
self.page = WebPage(self)
|
frame = self.page().mainFrame()
|
||||||
self.view.setPage(self.page)
|
|
||||||
self.page.settings().setAttribute(QtWebKit.QWebSettings.LocalContentCanAccessRemoteUrls, True)
|
|
||||||
|
|
||||||
layout = QtGui.QVBoxLayout(self)
|
|
||||||
layout.addWidget(self.view)
|
|
||||||
|
|
||||||
frame = self.view.page().mainFrame()
|
|
||||||
frame.addToJavaScriptWindowObject("controller", self.app.controller)
|
frame.addToJavaScriptWindowObject("controller", self.app.controller)
|
||||||
frame.addToJavaScriptWindowObject("console", self.app.console)
|
frame.addToJavaScriptWindowObject("__console", self.app.console)
|
||||||
|
|
||||||
url = self.app.resources_uri() + "/index.html"
|
url = self.app.resources_uri() + "/index.html"
|
||||||
self.view.load(QtCore.QUrl(url))
|
self.load(QtCore.QUrl(url))
|
||||||
|
|
||||||
|
def load_url(self, url, callback=None):
|
||||||
|
self.loadFinished.connect(lambda ok: self.load_finished(ok, callback))
|
||||||
|
self.load(QtCore.QUrl(url))
|
||||||
|
|
||||||
|
def load_finished(self, ok, callback=None):
|
||||||
|
if self.is_local:
|
||||||
|
self.page().mainFrame().evaluateJavaScript("var OS_TYPE = 'linux';")
|
||||||
|
|
||||||
|
if callback:
|
||||||
|
callback(ok)
|
||||||
|
|
||||||
|
class NetworkAccessManager(QNetworkAccessManager):
|
||||||
|
|
||||||
|
def __init__(self, old_manager, tentia_callback):
|
||||||
|
QNetworkAccessManager.__init__(self)
|
||||||
|
|
||||||
|
self.tentia_callback = tentia_callback
|
||||||
|
|
||||||
|
self.old_manager = old_manager
|
||||||
|
self.setCache(old_manager.cache())
|
||||||
|
self.setCookieJar(old_manager.cookieJar())
|
||||||
|
self.setProxy(old_manager.proxy())
|
||||||
|
self.setProxyFactory(old_manager.proxyFactory())
|
||||||
|
|
||||||
|
def createRequest(self, operation, request, data):
|
||||||
|
if request.url().scheme() != "tentia":
|
||||||
|
return QNetworkAccessManager.createRequest(self, operation, request, data)
|
||||||
|
else:
|
||||||
|
self.tentia_callback(request.url())
|
||||||
|
return QNetworkAccessManager.createRequest(self, QNetworkAccessManager.GetOperation, QNetworkRequest(QtCore.QUrl()))
|
||||||
|
|
||||||
|
|
||||||
def load_finished(self, ok):
|
|
||||||
self.view.page().mainFrame().evaluateJavaScript("var OS_TYPE = 'linux';")
|
|
||||||
self.delegate.load_finished(ok)
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import sys
|
import sys, urllib
|
||||||
from PyQt4 import QtCore, QtGui, QtWebKit
|
from PyQt4 import QtCore, QtGui, QtWebKit
|
||||||
|
|
||||||
"""Html snippet."""
|
"""Html snippet."""
|
||||||
|
@ -28,6 +28,20 @@ class StupidClass(QtCore.QObject):
|
||||||
"""Python interpreter version property."""
|
"""Python interpreter version property."""
|
||||||
pyVersion = QtCore.pyqtProperty(str, fget=_pyVersion)
|
pyVersion = QtCore.pyqtProperty(str, fget=_pyVersion)
|
||||||
|
|
||||||
|
def isBasicAuth(url):
|
||||||
|
url_opener = urllib.URLopener()
|
||||||
|
|
||||||
|
try:
|
||||||
|
url_opener.open(url)
|
||||||
|
except IOError, error_code:
|
||||||
|
if error_code[0] == "http error" :
|
||||||
|
if error_code[1] == 401:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
app = QtGui.QApplication(sys.argv)
|
app = QtGui.QApplication(sys.argv)
|
||||||
|
|
||||||
|
@ -36,7 +50,13 @@ def main():
|
||||||
webView = QtWebKit.QWebView()
|
webView = QtWebKit.QWebView()
|
||||||
# Make myObj exposed as JavaScript object named 'pyObj'
|
# Make myObj exposed as JavaScript object named 'pyObj'
|
||||||
webView.page().mainFrame().addToJavaScriptWindowObject("pyObj", myObj)
|
webView.page().mainFrame().addToJavaScriptWindowObject("pyObj", myObj)
|
||||||
webView.setHtml(html)
|
|
||||||
|
url = "http://lala.home.jeena.net:3002/admin"
|
||||||
|
if isBasicAuth(url):
|
||||||
|
print "Basic Auth"
|
||||||
|
else:
|
||||||
|
webView.load(QtCore.QUrl(url))
|
||||||
|
#webView.setHtml(html)
|
||||||
|
|
||||||
window = QtGui.QMainWindow()
|
window = QtGui.QMainWindow()
|
||||||
window.setCentralWidget(webView)
|
window.setCentralWidget(webView)
|
||||||
|
|
|
@ -1,27 +1,25 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
import os, sys, pickle
|
import os, sys, pickle
|
||||||
from PyQt4 import QtCore, QtGui
|
from PyQt4 import QtCore, QtGui, QtWebKit
|
||||||
import Windows
|
import Windows, Helper
|
||||||
|
|
||||||
class Tentia:
|
class Tentia:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.app = QtGui.QApplication(sys.argv)
|
self.app = QtGui.QApplication(sys.argv)
|
||||||
self.controller = Controller()
|
self.controller = Controller(self)
|
||||||
self.console = Console()
|
self.console = Console()
|
||||||
|
|
||||||
self.setup_url_handler()
|
|
||||||
self.setup_windows()
|
|
||||||
self.preferences.show()
|
|
||||||
self.app.exec_()
|
|
||||||
|
|
||||||
def quit(self, sender):
|
|
||||||
print "quit"
|
|
||||||
|
|
||||||
def setup_windows(self):
|
|
||||||
self.preferences = Windows.Preferences(self)
|
self.preferences = Windows.Preferences(self)
|
||||||
#self.timeline = Windows.Timeline(self)
|
self.preferences.show()
|
||||||
#self.mentions = Windows.Timeline(self, action="mentions", title="Mentions")
|
|
||||||
|
self.timeline = Windows.Timeline(self)
|
||||||
|
self.mentions = Windows.Timeline(self, "mentions", "Mentions")
|
||||||
|
|
||||||
|
if self.controller.stringForKey("user_access_token") != "":
|
||||||
|
self.logged_in_successfully(True)
|
||||||
|
|
||||||
|
self.app.exec_()
|
||||||
|
|
||||||
def resources_path(self):
|
def resources_path(self):
|
||||||
return os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
return os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
|
||||||
|
@ -33,17 +31,31 @@ class Tentia:
|
||||||
self.controller.setStringForKey(entity, "entity")
|
self.controller.setStringForKey(entity, "entity")
|
||||||
self.oauth_implementation = Windows.Oauth(self)
|
self.oauth_implementation = Windows.Oauth(self)
|
||||||
|
|
||||||
def setup_url_handler(self):
|
def authentification_succeded(self):
|
||||||
QtGui.QDesktopServices.setUrlHandler("tentia://", self.reciveURI)
|
self.preferences.active(False)
|
||||||
|
self.preferences.hide()
|
||||||
|
self.init_web_views()
|
||||||
|
|
||||||
def reciveURI(uri):
|
def init_web_views(self):
|
||||||
print uri
|
self.timeline.show()
|
||||||
|
self.mentions.show()
|
||||||
|
|
||||||
|
def logged_in_successfully(self, success):
|
||||||
|
self.preferences.active(False)
|
||||||
|
if success:
|
||||||
|
self.preferences.hide()
|
||||||
|
self.timeline = Windows.Timeline(self)
|
||||||
|
self.timeline.show()
|
||||||
|
else:
|
||||||
|
print "not logged in"
|
||||||
|
|
||||||
|
|
||||||
class Controller(QtCore.QObject):
|
class Controller(QtCore.QObject):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, app):
|
||||||
QtCore.QObject.__init__(self)
|
QtCore.QObject.__init__(self)
|
||||||
|
self.app = app
|
||||||
|
|
||||||
self.config_path = os.path.expanduser('~/.tentia.cfg')
|
self.config_path = os.path.expanduser('~/.tentia.cfg')
|
||||||
if os.access(self.config_path, os.R_OK):
|
if os.access(self.config_path, os.R_OK):
|
||||||
with open(self.config_path, 'r') as f:
|
with open(self.config_path, 'r') as f:
|
||||||
|
@ -73,9 +85,10 @@ class Controller(QtCore.QObject):
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
def openURL(self, url):
|
def openURL(self, url):
|
||||||
print url
|
self.app.oauth_implementation.handle_authentication(str(url))
|
||||||
print QtCore.QUrl(url)
|
|
||||||
QtGui.QDesktopServices.openUrl(QtCore.QUrl(url));
|
def loggedIn(self):
|
||||||
|
self.app.authentification_succeded()
|
||||||
|
|
||||||
|
|
||||||
class Console(QtCore.QObject):
|
class Console(QtCore.QObject):
|
||||||
|
@ -84,13 +97,18 @@ class Console(QtCore.QObject):
|
||||||
def log(self, string):
|
def log(self, string):
|
||||||
print "<js>: " + string
|
print "<js>: " + string
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str)
|
||||||
|
def error(self, string):
|
||||||
|
print "<js ERROR>: " + string
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
def warn(self, string):
|
def warn(self, string):
|
||||||
print "<js WARN>: " + string
|
print "<js WARN>: " + string
|
||||||
|
|
||||||
@QtCore.pyqtSlot(str)
|
@QtCore.pyqtSlot(str)
|
||||||
def error(self, string):
|
def notice(self, string):
|
||||||
print "<js ERROR>: " + string
|
print "<js NOTICE>: " + string
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
from PyQt4 import QtCore, QtGui, QtWebKit
|
from PyQt4 import QtCore, QtGui, QtWebKit
|
||||||
import Helper
|
import Helper, urllib
|
||||||
|
|
||||||
class Preferences:
|
class Preferences:
|
||||||
|
|
||||||
|
@ -76,26 +76,14 @@ class Preferences:
|
||||||
|
|
||||||
class Timeline:
|
class Timeline:
|
||||||
|
|
||||||
def __init__(self, app, action="home_timeline", title="Tentia"):
|
def __init__(self, app, action="timeline", title="Tentia"):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.action = action
|
self.action = action
|
||||||
self.title = title
|
self.title = title
|
||||||
|
|
||||||
self.window = gtk.Window()
|
self.window = Helper.WebViewCreator(self.app)
|
||||||
self.window.connect("delete-event", self.quit)
|
self.window.setWindowTitle(title)
|
||||||
self.window.set_title(self.title)
|
self.window.load_local(self.load_finished)
|
||||||
self.window.set_position(gtk.WIN_POS_CENTER)
|
|
||||||
self.window.set_size_request(390, 650)
|
|
||||||
|
|
||||||
scroller = gtk.ScrolledWindow()
|
|
||||||
self.window.add(scroller)
|
|
||||||
|
|
||||||
self.web_view = webkit.WebView()
|
|
||||||
scroller.add(self.web_view)
|
|
||||||
|
|
||||||
def quit(self, widget, foo):
|
|
||||||
self.window.hide()
|
|
||||||
self.app.quit(self)
|
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
self.window.show()
|
self.window.show()
|
||||||
|
@ -103,32 +91,52 @@ class Timeline:
|
||||||
def hide(self):
|
def hide(self):
|
||||||
self.window.hide()
|
self.window.hide()
|
||||||
|
|
||||||
def init_web_view(self):
|
|
||||||
self.web_view.connect("load-finished", self.load_finished)
|
|
||||||
self.web_view.open(self.app.resources_path() + "index.html")
|
|
||||||
|
|
||||||
def load_finished(self, widget):
|
def load_finished(self, widget):
|
||||||
delay = 1
|
script = "function HostAppGo() { start('" + self.action + "'); }"
|
||||||
if self.action == "mentions":
|
self.window.page().mainFrame().evaluateJavaScript(script)
|
||||||
delay = 1000
|
|
||||||
|
|
||||||
script = "setTimeout(\
|
|
||||||
function() {\
|
|
||||||
tentia_instance = new Core('" + self.action + "');\
|
|
||||||
document.getElementsByTagName('body')[0].appendChild(tentia_instance.body);\
|
|
||||||
setTimeout(function(){ loadPlugin(controller.pluginURL()) }, 1); }, " + delay + "\
|
|
||||||
);"
|
|
||||||
|
|
||||||
self.web_view.execute_script(script)
|
|
||||||
|
|
||||||
class Oauth:
|
class Oauth:
|
||||||
|
|
||||||
def __init__(self, app):
|
def __init__(self, app):
|
||||||
self.app = app
|
self.app = app
|
||||||
self.window = Helper.WebViewCreator(self.app, self)
|
self.core = Helper.WebViewCreator(self.app)
|
||||||
|
self.core.load_local(self.load_finished)
|
||||||
|
|
||||||
def load_finished(self, ok):
|
def load_finished(self, ok):
|
||||||
if ok:
|
if ok:
|
||||||
script = "function HostAppGo() { start('oauth'); }"
|
script = "function HostAppGo() { start('oauth'); }"
|
||||||
self.window.view.page().mainFrame().evaluateJavaScript(script)
|
self.core.page().mainFrame().evaluateJavaScript(script)
|
||||||
|
|
||||||
|
def handle_authentication(self, url):
|
||||||
|
self.auth_view = Helper.WebViewCreator(self.app)
|
||||||
|
self.auth_view.setWindowTitle("Authentification")
|
||||||
|
|
||||||
|
old_manager = self.auth_view.page().networkAccessManager()
|
||||||
|
new_manager = Helper.NetworkAccessManager(old_manager, self.tentia_callback)
|
||||||
|
self.auth_view.page().setNetworkAccessManager(new_manager)
|
||||||
|
|
||||||
|
self.auth_view.show()
|
||||||
|
|
||||||
|
if self.is_basic_auth(url):
|
||||||
|
print "Basic auth"
|
||||||
|
else:
|
||||||
|
self.auth_view.load_url(url)
|
||||||
|
|
||||||
|
|
||||||
|
def is_basic_auth(self, url):
|
||||||
|
url_opener = urllib.URLopener()
|
||||||
|
|
||||||
|
try:
|
||||||
|
url_opener.open(url)
|
||||||
|
except IOError, error_code:
|
||||||
|
if error_code[0] == "http error" :
|
||||||
|
if error_code[1] == 401:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tentia_callback(self, url):
|
||||||
|
script = "tentia_instance.requestAccessToken('" + url.toString() + "');"
|
||||||
|
print script
|
||||||
|
self.core.page().mainFrame().evaluateJavaScript(script)
|
||||||
|
|
|
@ -9,12 +9,12 @@
|
||||||
<title>Version 0.2.4</title>
|
<title>Version 0.2.4</title>
|
||||||
<sparkle:minimumSystemVersion>10.5.0</sparkle:minimumSystemVersion>
|
<sparkle:minimumSystemVersion>10.5.0</sparkle:minimumSystemVersion>
|
||||||
<sparkle:releaseNotesLink>http://jabs.nu/Tentia/download/ReleaseNotes.html</sparkle:releaseNotesLink>
|
<sparkle:releaseNotesLink>http://jabs.nu/Tentia/download/ReleaseNotes.html</sparkle:releaseNotesLink>
|
||||||
<pubDate>Sat, 17 Nov 2012 20:43:06 +0100</pubDate>
|
<pubDate>Sat, 17 Nov 2012 20:45:22 +0100</pubDate>
|
||||||
<enclosure url="http://jabs.nu/Tentia/download/Tentia.app.zip"
|
<enclosure url="http://jabs.nu/Tentia/download/Tentia.app.zip"
|
||||||
sparkle:version="0.2.4"
|
sparkle:version="0.2.4"
|
||||||
length="1069131"
|
length="1069130"
|
||||||
type="application/octet-stream"
|
type="application/octet-stream"
|
||||||
sparkle:dsaSignature="MCwCFFN5Bvd7SsvrHqvnXfFje2lCF+h3AhRs3koGlp6Ze3IedFmMj3l+ZxGSIQ==" />
|
sparkle:dsaSignature="MC0CFCvZgr1pcB0bW0IQ8E7ffqs0SjvpAhUAioix/kjwYUqGqchCTDN6EjncmSE=" />
|
||||||
</item>
|
</item>
|
||||||
</channel>
|
</channel>
|
||||||
</rss>
|
</rss>
|
||||||
|
|
|
@ -103,6 +103,7 @@ function(HostApp, Paths, Hmac) {
|
||||||
|
|
||||||
Oauth.prototype.requestAccessToken = function(responseBody) {
|
Oauth.prototype.requestAccessToken = function(responseBody) {
|
||||||
// /oauthtoken?code=51d0115b04d1ed94001dde751c5b360f&state=aQfH1VEohYsQr86qqyv
|
// /oauthtoken?code=51d0115b04d1ed94001dde751c5b360f&state=aQfH1VEohYsQr86qqyv
|
||||||
|
|
||||||
var urlVars = Paths.getUrlVars(responseBody);
|
var urlVars = Paths.getUrlVars(responseBody);
|
||||||
if(this.state && this.state != "" && urlVars["state"] == this.state) {
|
if(this.state && this.state != "" && urlVars["state"] == this.state) {
|
||||||
|
|
||||||
|
@ -145,6 +146,7 @@ function(HostApp, Paths, Hmac) {
|
||||||
HostApp.setStringForKey(access["token_type"], "user_token_type");
|
HostApp.setStringForKey(access["token_type"], "user_token_type");
|
||||||
|
|
||||||
HostApp.loggedIn();
|
HostApp.loggedIn();
|
||||||
|
console.log("D")
|
||||||
}
|
}
|
||||||
|
|
||||||
Oauth.prototype.logout = function() {
|
Oauth.prototype.logout = function() {
|
||||||
|
|
|
@ -55,7 +55,7 @@ function(jQuery, HostApp, Hmac) {
|
||||||
data: data,
|
data: data,
|
||||||
processData: false,
|
processData: false,
|
||||||
error: function(xhr, ajaxOptions, thrownError) {
|
error: function(xhr, ajaxOptions, thrownError) {
|
||||||
alert("getURL " + xhr.statusText + " " + http_method + " (" + url + "): '" + xhr.responseText + "'");
|
console.error("getURL " + xhr.statusText + " " + http_method + " (" + url + "): '" + xhr.responseText + "'");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ function(jQuery, HostApp, Hmac) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
error: function(xhr, ajaxOptions, thrownError) {
|
error: function(xhr, ajaxOptions, thrownError) {
|
||||||
alert("findProfileURL " + xhr.statusText + " (" + entity + "): " + xhr.responseText);
|
console.error("findProfileURL " + xhr.statusText + " (" + entity + "): " + xhr.responseText);
|
||||||
if (errorCallback) errorCallback(xhr.statusText + " - " + xhr.responseText)
|
if (errorCallback) errorCallback(xhr.statusText + " - " + xhr.responseText)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -55,6 +55,37 @@ String.prototype.endsWith = function(suffix) {
|
||||||
return this.match(suffix+"$") == suffix;
|
return this.match(suffix+"$") == suffix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var console = {
|
||||||
|
log: function(s) {
|
||||||
|
if (OS_TYPE == "mac") {
|
||||||
|
alert(s)
|
||||||
|
} else {
|
||||||
|
__console.log(s);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function(s) {
|
||||||
|
if (OS_TYPE == "mac") {
|
||||||
|
alert("ERROR: " + s);
|
||||||
|
} else {
|
||||||
|
__console.error(s);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
warning: function (s) {
|
||||||
|
if (OS_TYPE == "mac") {
|
||||||
|
alert("WARNING: " + s);
|
||||||
|
} else {
|
||||||
|
__console.warning(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
notice: function(s) {
|
||||||
|
if (OS_TYPE == "mac") {
|
||||||
|
alert("NOTICE: " + s);
|
||||||
|
} else {
|
||||||
|
__console.notice(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function loadPlugin(url) {
|
function loadPlugin(url) {
|
||||||
var plugin = document.createElement("script");
|
var plugin = document.createElement("script");
|
||||||
plugin.type = "text/javascript";
|
plugin.type = "text/javascript";
|
||||||
|
|
Reference in a new issue