Pagedown editor attach handler to image dialog - javascript

I've made my own upload library dialog with Semantic UI's modal. I'm trying to integrate this dialog within the Pagedown editor.
var converter = Markdown.getSanitizingConverter();
var editor = new Markdown.Editor(converter);
editor.hooks.set("insertImageDialog", function(callback) {
uploadModal.load(); // The dialog HTML gets loaded asynchronously
$('body').on('selection', '.upload-modal', function(e, src) {
console.log(src);
callback(src);
});
return true; // tell the editor that we'll take care of getting the image url
});
Everything works fine the first time selecting an image from the dialog, but after that it breaks:
Uncaught TypeError: Cannot call method 'removeChild' of null Markdown.Editor.js
According to this SO question it has something to do with defining the event handler multiple times, but I can't wrap my head around it.
Some more info:
The HTML for initializing the modal is loaded asynchronously, here is the JS code:
var uploadModal = (function() {
/**
* Load the upload modal.
*/
this.load = function() {
var $modal = $('.upload-modal');
if ($modal.length) {
// Show the already existing modal.
$modal.modal('show');
} else {
// Get the HTML
var request = $.get(appUrl('admin/upload/modal'));
request.done(function(html) {
// Make the modal.
var $modal = $(html);
$modal.modal({
observeChanges: true,
onApprove: function () {
var upload = $modal.find('.selected.upload'),
src = upload.find('.image > img').attr('src');
// Bind the event listener which will return the upload's source URL
$modal.trigger('selection', src);
return true;
}
}).modal('show');
// Some event handlers here
});
}
};
})();
I load the modal by calling uploadModal.load() and watch whenever an image is selected by attaching the 'selection' event handler.

Related

Load script on event and trigger the same event

I am trying to add certain javascript files on an event, say click. I am trying to use the Javascript to be used on same event, only if it is triggered. This is because the scripts are slowing down the page load and there is no need for the scripts otherwise.
Can I just move the scripts to the footer and be all set, or do this pro grammatically via loading them only when needed - via event triggering instead? Below is what I have so far:
HTML:
<a id="customId" href="#myLink"></a>
Javascript:
$(document).ready(function() {
//The async addition
var myJS = {
lazyload : function(scriptSrc) {
if(!this.isPresent(scriptSrc)){
var scriptTag = document.createElement('script');
scriptTag.src = scriptSrc;
scriptTag.async = true;
document.getElementsByTagName('head')[0].appendChild(scriptTag);
}
return false;
}
};
//The event trigger needs to do something using the said script
if($('#customId')){
//Approach 1:
var mapEl = document.getElementById("customId");
mapEl.addEventListener("click", customEventHandler, false);
//mapEl.dispatchEvent(event);
//*where
customEventHandler : function(e){
e.preventDefault;
myJS.lazyload('/jsfile.js');
// Update or use link relative #href (not complete path) and use the javascript without navigating out of page.
//e.currentTarget.dispatchEvent(?);
}
//2nd attempt: Adds the script, but not able to trigger event to use JS
$('#customId').click(function(event) {
event.preventDefault();
myJS.lazyload('/jsfile.js');
//Either approach:
//Trigger the custom event to do an actual click after doing the lazy load, using the JS file
(click); $('#customId').trigger('click'); //Is this correct on same element ID
});
}
}
Try using onload event of script element, defining a custom event to prevent recursively calling native click event handler on element
$(document).ready(function() {
//The async addition
var myJS = {
lazyload: function(scriptSrc, id, type) {
// use `.is()` to check if `script` element has `src`
// equal to `scriptSrc`
if (!$("script[src='"+ scriptSrc +"']").is("*")) {
var scriptTag = document.createElement("script");
scriptTag.src = scriptSrc;
scriptTag.async = true;
// use `script` `onload` to trigger custom event `customClick`
scriptTag.onload = function() {
$(id).trigger(type)
};
document.getElementsByTagName("head")[0].appendChild(scriptTag);
}
return false;
}
};
$("#customId").on("click", function() {
myJS.lazyload("jsfile.js", "#" + this.id, "customClick");
})
// do stuff at `customClick` event
.one("customClick", function(e) {
customClick(e.type)
});
});
plnkr http://plnkr.co/edit/Rw4BRAfSYlXXe5c6IUml?p=preview

Javascript & Soundcloud Widget: How to load new track into SC Widget iFrame (via URL)

I’ve seen different web apps like Playmoss, Whyd, and Songdrop etc. that, I believe, HAVE to utilize the Soundcloud Embedded Widget in order to produce the functionality of playing multiple tracks, in sucession, not apart of a set/(playlist). Currently I am having issues reproducing this functionality with the following library, so I decided to attempt to write my own:
https://github.com/eric-robinson/SCLPlayer
I am very new to writing javascript, but my below code, will load a first track, and play it once hitting the “ready” bind. Once hitting the “finish” bind, It will then jump to the loadNextTrack() function and load the next tracks URL, into the src of the widget’s iFrame. After that, it doesn’t ever hit the original “ready” bind, which would then begin playback.
So to clear things up, playback doesn’t begin for the second track.
<script type = "text/javascript">
var SCLPlayer = {
isPlayerLoaded : false,
isPlayerFullLoaded : false,
needsFirstTrackSkip : true,
isPaused: true,
scPlayer : function() {
widgetContainer = document.getElementById('sc');
widget = SC.Widget(widgetContainer);
return widget;
},
loadNextTrack : function() {
var ifr = document.getElementById('sc');
ifr.src = 'http://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/231758952';
console.log ('Loading Next Track');
SCLPlayer.scPlayer().bind(SC.Widget.Events.READY, function() {
console.log ('Player is Ready, next Track');
SCLPlayer.scPlayer().play();
});
}
};
$( '#sc' ).ready(function() {
SCLPlayer.scPlayer().bind(SC.Widget.Events.READY, function() {
SCLPlayer.isPlayerLoaded = true;
//window.location = 'sclplayer://didLoad';
console.log ('Player is Ready');
SCLPlayer.scPlayer().play();
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.PLAY, function() {
SCLPlayer.isPaused = false;
//window.location = 'sclplayer://didPlay';
console.log ('Player did Play');
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.PAUSE, function() {
SCLPlayer.isPaused = true;
//window.location = 'sclplayer://didPause';
console.log ('Player did Pause');
});
SCLPlayer.scPlayer().bind(SC.Widget.Events.FINISH, function() {
SCLPlayer.isPaused = true;
//window.location = 'sclplayer://didFinish';
console.log ('Player did Finish');
SCLPlayer.loadNextTrack();
});
});
</script>
</head>
<body>
<iframe id = "sc" width="100%" height="100%" scrolling="no" frameborder="no" src="http://w.soundcloud.com/player/?url=https://api.soundcloud.com/tracks/226183306"></iframe>
</body>
The whole point of me writing this Javascript is so that I can then use a Swift to Javascript bridge in my iOS app to then control the loading of tracks into the embedded players. For some reason over a slower connection, the next track doesn't always load into the player, using the "bridge". I hope to provide the nextTrackURL to the javascript side of things before the currentTrack finishes, so that the bridge conveys nothing and the Javascript handles new track loading, solely on its own.
I think you want to use the load function to specify the url for the new track
From the soundcloud Widget API docs:
load(url, options) — reloads the iframe element with a new widget specified by the url. All previously added event listeners will continue working. options is an object which allows you to define all possible widget parameters as well as a callback function which will be executed as soon as new widget is ready. See below for detailed list of widget parameters.
var url = "https://api.soundcloud.com/";
var options = [];
// if a track
url += "tracks/";
// if a playlist
url += "playlists/"
// append the id of the track / playlist to the url
url += id;
// set any options you want for the player
options.show_artwork = false;
options.liking = false;
options.auto_play = true;
widget.load(url, options, OPTIONAL_CALLBACK_FUNCTION);
Edited to show binding...
The bind code is called once, after the widget is initially loaded.
The ready event is only called once, when the widget is initially loaded, it is not called for each subsequent call using load().
try {
widget.bind(SC.Widget.Events.FINISH,
function finishedPlaying() {
// your code / function call
}
);
widget.bind(SC.Widget.Events.PAUSE,
function paused() {
// your code / function call
}
);
widget.bind(SC.Widget.Events.PLAY,
function playing() {
// your code / function call
widget.getCurrentSound(function scCurrentSound(sound) {
// this also binds getCurrent sound which is called
// each time a new sound is loaded
});
}
);
widget.bind(SC.Widget.Events.PLAY_PROGRESS,
function position(pos) {
// your code / function call
}
);
widget.bind(SC.Widget.Events.SEEK,
function seek(pos) {
// your code / function call
}
);
widget.bind(SC.Widget.Events.READY,
function ready() {
// your code / function call
}
);
} catch(e) {
// exception handler code
}

imagesLoaded Plugin not working with Angular JS

So i'm learning AngularJS and i'm building a small web app that allows you to click through images randomly. Basically you click the next button and an image is downloaded and shown, when you click the back button it goes to the previous image in the stack.
I'd like to show a loading spinner and disable the back/forward buttons until the ajax request for the new image is complete, AND the image is completely loaded
My image controller is structured like so:
app.controller('ImageController', ['imageService', function(imageService) {
var that = this;
that.position = 0;
that.images = [];
that.loading = false;
that.isLoading = function() {
return that.loading;
}
that.setLoading = function(isLoading) {
that.loading = isLoading;
}
that.currentImage = function() {
if (that.images.length > 0) {
return that.images[that.position];
} else {
return {};
}
};
that.fetchSkin = function() {
that.setLoading(true);
imageService.fetchRandomSkin().success(function(data) {
// data is just a js object that contains, among other things, the URL for the image I want to display.
that.images.push(data);
that.imagesLoaded = imagesLoaded('.skin-preview-wrapper', function() {
console.log('images loaded');
that.setLoading(false);
});
});
};
that.nextImage = function() {
that.position++;
if (that.position === that.images.length) {
that.fetchSkin();
}
};
that.previousImage = function() {
if (that.position > 0) {
that.position--;
}
};
that.fetchSkin();
}]);
If you notice inside of the that.fetchSkin() function, i'm calling the imagesLoaded plugin then when the images are loaded I am setting that.loading to false. In my template I am using ng-show to show the images when the loading variable is set to false.
If I set loading to false outside of the imagesLoaded callback (like when the ajax request is complete) then everything works as expected, when I set it inside of the imagesLoaded function the template doesn't update with the new loading value. Note that the console.log('images loaded'); does print to the console once the images have loaded so I know the imagesLoaded plugin is working correctly.
As your imagesLoaded callback is invoked asynchronously once images are loaded, Angular does not know that values of that.isLoading() method calls changed. It is because of dirty checking that Angular uses to provide you with easy to use 2 way data binding.
If you have a template like so:
<div ng-show="isLoading()"></div>
it won't update after you change the values.
You need to manually tell angular about data changes and that can be done by invoking $digest manually.
$scope.$digest();
just after you do
console.log('images loaded');
that.setLoading(false);
Pseudo code that can work (copied and pasted from my directive):
//inside your controller
$scope.isLoading = false;
// just another way of using imagesLoaded. Yours is ok.
$element.imagesLoaded(function() {
$scope.isLoading = true;
$scope.$digest();
});
As long as you only change your controller $scope within async callback, there's no need to call $apply() to run $digest on $rootScope because your model changes are only local.

Modal popup window with Javascript

I have a clickable image that when you click a modal popup appears. I want to make sure you can only click it once and while the popup is showing, the clickable image is unclickable. I've tried several methods but no solution works as I want it to.
Here is the code:
init: function () {
var myButton = document.getElementById("kaffePic");
var clickable = true;
console.log(clickable);
myButton.onclick = function(e) {
e.preventDefault();
console.log(clickable);
if(clickable)
{
clickable = false;
popup(myButton, clickable);
}
else
{
return false;
}
};
}
And here is a part of the popup window (removed some code that has nothing to do with the issue).
function popup(theButton, returnClick) {
var myDiv = document.createElement("div");
myDiv.className = "popupWindow";
var newButton = document.createElement('a');
var image = document.createElement('img');
image.src = 'img/theclosebutton.png';
image.className = "popupImage";
newButton.appendChild(image);
myDiv.appendChild(newButton);
newButton.onclick = function () {
document.body.removeChild(myDiv);
returnClick = true;
};
}
Right now I can click it once, and then never again.
Any suggestions?
it's called only once because clickable is set to false after the first click. i suspect you are trying to set it to true in your popup-method by calling returnClick = true; but all that does is setting your argument-value, not the actual clickable-variable itself.
right now, clickable is a variable in the scope of init, so popup can't access it. you could, for example, make clickable a var in the scope of init's parent object. then in popup, you'd access clickable by parentObject.clickable.
//illustration of my example
parentObject {
var clickable,
function init()
}
function popup() {
...
parentObject.clickable = true;
}
Check .one() event handler attachment of jquery and this the .one() of jquery is used to Attach a handler to an event for the elements. The handler is executed at most once per element per event type. second one is link to an stack overflow questions about resetting the .one().Hope this helps

CKeditor select the upload tab in image dialog by default

Is there a way to modify the image dialog of CKEditor to display the upload tab by default instead of the Image info tab?
I've tried doing this by adding a line of code to the onload of the dialog:
onLoad: function() {
this.getDialog().selectPage('Upload');
}
this seems to work fine, I'm able to upload the image to the server, but as soon as I hit the ok button I get a permission denied error.
I've also tried it the way CKSource describes but this gives me an exception since it overrides the onShow method.
Fixed this by adding this.selectPage('Upload'); to the end of the onShow function of the image plugin
As you noticed, the example in the docs is broken because the Image plugin already has an onShow() method.
The trick is to chain the methods like this:
CKEDITOR.on('dialogDefinition', function(e) {
if (e.data.name == 'image') {
var dialog = e.data.definition;
oldOnShow = dialog.onShow;
dialog.onShow = function() {
oldOnShow.apply(this, arguments);
this.selectPage('Upload');
};
}
});
This doc explains how to set a dialog tab by default in your ckeditor config:
http://docs.cksource.com/CKEditor_3.x/Howto/Default_Dialog_Tab
Can user following script.
<script type="text/javascript">
CKEDITOR.on('dialogDefinition', function(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
if (dialogName == 'image') {
dialogDefinition.onShow = function () {
// This code will open the Upload tab.
this.selectPage('Upload');
};
}
});
</script>

Categories