In loop to display fields from EF table, I want to convert boolean to empty space if False - entity-framework

these 2 lines are booleans in the database, checkboxes on the add record page.
But when I display rows entered, I want to have empty space where value is False (checkbox was not checked, field in table saves False).
<td align="center">#m.IsTOTPresent</td>
<td align="center">#m.IsEColi</td>
I'm thinking there is an if conditional that would change how it looks on the webpage based on the boolean result of true or false. In my case, nothing displayed would be optimum for false, and a red "true" with a grey background would be optimum for a true result. In a perfect world, that is.

There are many ways to accomplish what you're asking for. Here are a couple...
1. Mostly razor
CSS:
.myTrue {
background-color: red;
color: white;
};
.myFalse {
background-color: white;
color: black;
};
Markup/Razor:
<td align='center' class='#(m.IsTOTPresent ? "myTrue" : "myFalse")'>#(m.IsTOTPresent ? "true" : "")</td>
<td align='center' class='#(m.IsEColi? "myTrue" : "myFalse")'>#(m.IsEColi? "true" : "")</td>
2. Mostly js
Markup/Razor:
<td align='center' data-boolean-value="#m.IsTOTPresent"></td>
<td align='center' data-boolean-value="#m.IsEColi"></td>
js:
(function() {
document.querySelectorAll('[data-boolean-value]').forEach(
function (obj, idx, arr) {
if (typeof obj.dataset.booleanValue !== 'undefined') {
if(obj.dataset.booleanValue) {
obj.innerHTML = "true";
obj.style.backgroundColor = "red";
obj.style.color = "white";
} else {
obj.innerHTML = "";
obj.style.backgroundColor = "white";
obj.style.color = "black";
}
}
});
})();

Related

how to resize image and reorient it if it rotates when uploaded through IOS devices using image intervention package?

I have developed a Laravel web application with the help of some tutorial videos and codes given by other developers on stackoverflow .The app is working pretty good except for the image upload feature. I am facing an issue related to the uploaded image being cut either on the sides or on the bottom as well as the image when uploaded through any IOS device then the image under goes rotation. I have installed image intervention but i don't know where to put the code inside my files i am sharing my controller code as well as the image displaying code here
controller code
namespace App\Http\Controllers;
use Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\MessageBag;
use App\comment;
use App\User;
use App\post;
use View;
use Lang;
use image;
class usersController extends Controller
{
private $u;
public function __construct(){
$this->u = new User();
}
public function search(Request $request){
$search_input = $request["input"];
$path = $request["path"];
$users = User::where("name","like","%$search_input%")->orWhere("username","like","%$search_input%")->get();
if ($users->isEmpty()) {
return "<p style='text-align: center;width: 100%;color: gray;font-size: 12px;margin: 3px'>".Lang::get('trans.nothingToShow')."</p>";
}else{
foreach ($users as $user) {
if ($user->verify == 1) {
$ifVerify = '<span class="verify-badge" style="width: 420px; height: 700px; background: url(\''.$path.'/imgs/verify.png\') no-repeat; background-size: cover; margin: 0px 2px;"></span>';
}else{
$ifVerify = '';
}
if($user->avatar == "avatar.png"){
$avatar_path = '/imgs/avatar.png';
}else{
$avatar_path = '/storage/avatar/'.$user->avatar;
}
echo '<div class="navbar-search-item">
<a href="'.$path.'/'.$user->username.'">
<div>
<div style="background-image:url(\''.$path.$avatar_path.'\');">
</div>
<p>
'.$user->name.$ifVerify.'<br>
<span>#'.$user->username.'</span>
</p>
</div>
</a>
</div>';
}
}
}
public function profile($username){
$user_info = User::where("username",$username)->get();
foreach ($user_info as $uinfo) {
$user_id = $uinfo->uid;
}
if (isset($user_id)) {
$feedbacks = post::where("to_id",$user_id)->where("privacy",1)->orderBy('time', 'desc')->get();
$feedbacks_count = post::where("to_id",$user_id)->get()->count();
$new_count = post::where("to_id",$user_id)->where('read',0)->get()->count();
// check comments on post (count)
$commentsCount = array();
foreach ($feedbacks as $fb) {
$pid = $fb->pid;
$countComments = comment::where("c_pid",$pid)->get()->count();
array_push($commentsCount,$countComments);
}
return view("pages.profile")->with(["user_info" => $user_info,"feedbacks" => $feedbacks,'feedbacks_count' => $feedbacks_count,'new_count' => $new_count,'commentsCount' => $commentsCount]);
}else{
return view("pages.profile")->with(["user_info" => $user_info]);
}
}
public function settings($username){
$user_info = User::where("username",$username)->get();
if (Auth::user()->username == $username) {
return view("pages.settings")->with("user_info",$user_info);
}else{
return redirect()->back();
}
}
public function s_general(Request $request){
$this->validate($request,[
'avatar' => 'nullable|image|mimes:jpeg,png,jpg|max:3072',
'fullname' => 'required',
'email' => 'required|email'
]);
if ($request['fullname'] == Auth::user()->name && $request['email'] == Auth::user()->email && !$request->hasFile('avatar')) {
return redirect()->back()->with('general_msg', Lang::get('trans.noChanges_MSG'));
}else{
$avatar = $request->file('avatar');
if ($request->hasFile('avatar')) {
$avatar_ext = $avatar->getClientOriginalExtension();
$avatar_name = rand(9,999999999)+time().".".$avatar_ext;
$avatar_new = $avatar->storeAs("avatar",$avatar_name);
}else{
$avatar_name = Auth::user()->avatar;
}
$update_general = User::where('uid',Auth::user()->uid)->update(['name' => $request['fullname'],'email' => $request['email'],'avatar' => $avatar_name]);
return redirect()->back()->with('general_msg', Lang::get('trans.changes_saved'));
}
}
and this is how i display the image
<div class="profile-avatar" style="width: 300px;height:400px; border-radius: 0%;background-image: url('#if(Auth::user()->avatar == "avatar.png") {{ url("/imgs/".Auth::user()->avatar) }} #else {{ url("/storage/avatar/".Auth::user()->avatar) }} #endif');">
please help me with code should i put and where i should put that in order to resolve this issue
this code is for preview of the image
<div style="display: inline-flex;">
<div class="profile-avatar" id="settings_img_elm" style="margin: 10px; width:350px;margin-top: 0; margin-bottom: 0;border-color: #fff; text-align: center;background-image: url('#if(Auth::user()->avatar == "avatar.png") {{ url("/imgs/".Auth::user()->avatar) }} #else {{ url("/storage/avatar/".Auth::user()->avatar) }} #endif');">
</div>
<p style="color: #a7aab5; font-size: 9px;padding: 25px 10px 25px 10px; margin: 0;">#lang("trans.preview")<br>#lang("trans.maxSize")<br>upload vertical <br>images.<br>Save the<br>image first<br> and then<br> check the<br> preview</p>
</div>
<p style="border-bottom: 1px solid #dfe2e6;margin: 0; margin-top: 12px; margin-bottom: 12px;">
<input type="file" name="avatar" style="display: none;" id="settings_img">
<label for="settings_img" class="btn btn-success">#lang("trans.selectImage")</label>
the javascript for the preview image is
function imagePreview(input,elm) {
if (input.files && input.files[0]) {
var reader = new FileReader();
reader.onload = function (e) {
$(elm).css("background-image","url('"+e.target.result+"')");
}
reader.readAsDataURL(input.files[0]);
}
}
$("#settings_img").on("change",function(){
imagePreview(this,"#settings_img_elm");
});
$("#feedback_hFile").on("change",function(){
$(".send_feedback_image").show();
imagePreview(this,"#sfb_image_preview");
});
The code below should get you started. The two key things you are looking for here are Interventions orientate and resize methods which should take care of the two issues you mention. Orientation will rotate the image based on EXIF data, and resize can resize your image to whatever specifications you need.
Import the facade
use Intervention\Image\Facades\Image;
Suggestions
Remove use image; from your imports as it is probably causing or going to cause you issues. It is invalid.
s_general method adjustments
public function s_general(Request $request){
$this->validate($request,[
'avatar' => 'nullable|image|mimes:jpeg,png,jpg|max:3072',
'fullname' => 'required',
'email' => 'required|email'
]);
if ($request['fullname'] === Auth::user()->name && $request['email'] === Auth::user()->email && !$request->hasFile('avatar')) {
return redirect()->back()->with('general_msg', Lang::get('trans.noChanges_MSG'));
}
if ($request->hasFile('avatar')) {
// Grab the original image from the request
$originalImage = $request->file('avatar');
// Grab the original image extension
$originalExtension = $originalImage->getClientOriginalExtension();
// Instantiate a new Intervention Image using our original image,
// read the EXIF 'Orientation' data of the image and rotate the image if needed
$newImage = Image::make($originalImage)->orientate();
// Resize the new image to a width of 300 and a height of null (auto)
// we also use a callback to constrain the aspect ratio so we don't distort the image on resize
$newImage->resize(300, null, function ($constraint) {
$constraint->aspectRatio();
});
// Generate a randomized filename and append the original image extension
$filename = random_int(9, 999999999) + time() . '.' . $originalExtension;
// Save the new image to disk
$newImage->save(storage_path('app/public/avatar/' . $filename));
} else {
$filename = Auth::user()->avatar;
}
User::where('uid', Auth::user()->uid)->update(
[
'name' => $request['fullname'],
'email' => $request['email'],
'avatar' => $filename
]);
return redirect()->back()->with('general_msg', Lang::get('trans.changes_saved'));
}
More suggestions
I know this isn't a code review, but
Use PascalCase for your class names. I see you have a few imports such as App\comment and App\post
Your constructor doesn't seem to be needed. I'd ditch it. If you are keeping it, i would get use to more descriptive variable names. Short names like $u are generally considered bad practice.
You have a few unused imports, Validator, Hash and MessageBag could be removed to clean this up.
Your controller is doing a lot of stuff that most would consider bad practice. Fumbling around with html for example. 9.9 times out of 10 you should probably be leveraging blade for these things as it's one of its main purposes.
Stick to one naming convention or another. You are using a mixture of camelCase and snake_case for your variables. I prefer camelCase but whichever you choose it's best to stick with it and not mix them.
Sorry, i know this isn't suppose to be a code review, i just thought that a few little suggestions might help you in the future.
You can check Intervation docs on how to resize image, I've also created an example for you for s_general function:
$avatar = $request->file('avatar');
if ($request->hasFile('avatar')) {
//pass uploaded avatar to Intervention Image instance
$image = \Image::make($avatar);
//resize image to 300 width, keep height automated adjusted, or any other width you need
$image->resize(300, null, function ($constraint) {
$constraint->aspectRatio();
});
//Or you can set specific width and height
//$image->resize(300, 200);
$avatar_ext = $avatar->getClientOriginalExtension();
$avatar_name = rand(9,999999999) + time().".".$avatar_ext;
//I used public path to store the image you can change it based on your needs
$image->save(public_path('/avatar/'.$avatar_name));
}else{
$avatar_name = Auth::user()->avatar;
}
use image intervention for resizing and for correcting the orientation of the image uploaded from a mobile phone. IF you are showing the preview of this image using simple javascript something like
reader.onload = function (e) {
$(elm).css("background-image","url('"+e.target.result+"')");
then there is a chance that the preview of the image won't be oriented as we want but once you save the image the orientation will be proper.
this problem of orientation is encountered mostly on iphone.
The code for orientation correction and resizing is
$newImage = Image::make($originalImage)->orientate();
$newImage->resize(300, null, function ($constraint) {
$constraint->aspectRatio();
});
///or for resizing use
$newImage->resize(width,height);

View doesn't update on observablearray.remove(item) in knockout without call to applyBindings

I am learning to use MVC 4/MVVM/Knockout for a web-managed data project. I have been running into a problem updating the View when using the remove function on an observable array. The updates happen when using push or unshift, but not remove. Using the debugger in chrome I can see that the data is being removed from the array, the update event just isn't working.
Snippet from the html is the table below, there is a form I did not include for adding or editing data.
<div id="MessageDiv" data-bind="message: Message"></div>
<div class="tableContainer hiddenHead">
<div class="headerBackground"></div>
<div class="tableContainerInner">
<table id="adapter-table" class="grid" data-bind="sortTable: true">
<thead>
<tr>
<th class="first">
<span class="th-inner">Name</span>
</th>
<th>
<span class="th-inner">DeviceID</span>
</th>
<th>
<span class="th-inner"></span>
</th>
<th>
<span class="th-inner"></span>
</th>
</tr>
</thead>
<tbody data-bind="template: { name: 'AdaptersTemplate', foreach: Adapters }">
</tbody>
</table>
<script id="AdaptersTemplate" type="text/html">
<tr>
<td data-bind="text: Name"></td>
<td data-bind="text: DeviceID"></td>
<td>Edit
<td>Delete
</tr>
</script>
</div>
<input type="button" data-bind='click: addAdapter' value="Add New Adapter" />
<input type="button" data-bind='click: saveAll' value="Save Changes" id="SaveChangesButton" />
</div>
My javascript has been set up to manage the VM as restful and caches the changes. Add, Edit, and Saving/Deleting data all seems to work without throwing errors that I am seeing in the debugger in Chrome. Confirming changes seems to work fine and makes the changes to the database as expected.
$(function () {
var viewModel = new AdaptersModel();
getData(viewModel);
});
function getData(viewModel) {
$.getJSON("/api/AdapterList",
function (data) {
if (data && data.length > 0) {
viewModel.SetAdaptersFromJSON(data);
}
ko.applyBindings(viewModel);
});
}
//#region AdapterVM
function Adapter(name, siFamily, deviceIDs) {
var self = this;
self.Name = ko.observable(name);
self.DeviceID = ko.observable(deviceIDs);
self.ID = 0;
}
function AdaptersModel() {
var self = this;
self.Adapters = ko.observableArray([]);
self.DeleteAdapters = ko.observableArray([]);
self.NewAdapter = ko.observable(new Adapter("", "", "", ""));
self.Message = ko.observable("");
self.SetAdaptersFromJSON = function (jsData) {
self.Adapters = ko.mapping.fromJS(jsData);
};
//#region Edit List Options: confirmChanges
self.confirmChanges = function () {
if (self.NewAdapter().ID == 0) {
self.Adapters.push(self.NewAdapter());
}
};
//#endregion
//#region Adapter List Options: addAdapter, selectItem, deleteItem, saveAll
self.addAdapter = function () {
self.NewAdapter(new Adapter("", "", "", ""));
};
self.selectItem = function (item) {
self.NewAdapter(item);
};
self.deleteItem = function(item) {
self.DeleteAdapters.push(item.ID());
self.Adapters.remove(item);
};
self.saveAll = function () {
if (self.Adapters && self.Adapters().length > 0) {
var filtered = ko.utils.arrayFilter(self.Adapters(),
function(adapter) {
return ((!isEmpty(adapter.Manufacturer())) &&
(!isEmpty(adapter.Name())) &&
(!isEmpty(adapter.DeviceIDs()))
);
}
);
var updateSuccess = true;
if (self.DeleteAdapters().length > 0) {
jsonData = ko.toJSON(self.DeleteAdapters());
$.ajax({
url: "/api/AdapterList",
cache: false,
type: "DELETE",
data: jsonData,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function () { updateSuccess = true; },
error: function () { updateSuccess = false; }
});
}
var jsonData = ko.toJSON(filtered);
$.ajax({
url: "/api/AdapterList",
type: "POST",
data: jsonData,
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function(data) {
self.SetAdaptersFromJSON(data);
updateSuccess = true && updateSuccess;
},
error: function () { updateSuccess = false; }
});
if (updateSuccess == true) { self.Message("Update Successfull"); }
else { self.Message("Update Failed"); }
}
};
//#endregion
}
//#endregion
ko.bindingHandlers.message = {
update: function(element, valueAccessor) {
$(element).hide();
ko.bindingHandlers.text.update(element, valueAccessor);
$(element).fadeIn();
$(element).fadeOut(4000);
}
};
ko.bindingHandlers.sortTable = {
init: function (element, valueAccessor) {
setTimeout(function () {
$(element).addClass('tablesorter');
$(element).tablesorter({ widgets: ['zebra'] });
}, 0);
}
};
function isEmpty(obj) {
if (typeof obj == 'undefined' || obj === null || obj === '') return true;
if (typeof obj == 'number' && isNaN(obj)) return true;
if (obj instanceof Date && isNaN(Number(obj))) return true;
return false;
}
The specific script portion that is failing to update my html table is:
self.deleteItem = function(item) {
self.DeleteAdapters.push(item.ID());
self.Adapters.remove(item);
};
Everything seems to work except for the remove, so I seem to be at a loss for what to look at next, and I am too new to javascript or knockout to know if this is a clue: If I run ko.applyBindings() command in the self.deleteItem function, I get the update to happen but it does give me an unhandled error:
Uncaught Error: Unable to parse bindings.
Message: ReferenceError: Message is not defined;
Bindings value: message: Message
Message was defined in the VM before binding... was there something I missed in all this?
In the beginning of your Js file you are defining var viewModel = new AdaptersModel(); but lower you are stating that function Adapter() is the view model in your region declaration. It is making your code difficult to read. I am going to take another stab at what you can do to troubleshoot, but I would suggest that your viewmodel contains the adapters and your model contains a class-like instance of what each adapter should be.
The specific error you are getting is because you are binding Message() to something and then deleting Message(). One thing you could do to trouble shoot this is to change your div to something like :
<div id="MessageDiv" data-bind="with: Message">
<h5 data-bind="message: $data"><h5>
</div>
If you could create a fiddle I could give a more definite example of why, but basically if Message() is blank the with binding should not show the header which is undefined after deletion.
What you probably need to do though is look at what is being sent as 'item' and make sure it is not your viewmodel.
self.deleteItem = function(item) {
console.log(item); // << Check console and see what is being returned
self.DeleteAdapters.push(item.ID());
self.Adapters.remove(item);
};
You are probably deleting more than just a single adapter.
This will lead you the right direction, but I would seriously consider either renaming your code.
There was a lot of help solving surrounding issues but nothing actually solved the "why" of the problem. The updates worked perfectly sometimes but not other times. When I was troubleshooting it and started to get it dumbed down and working in JSFiddle I didn't include the data-bind="sortTable: true" in all my working versions. Apparently, if you sort a table or using the code as I did it will not work. The example code I have seen floating around is here at http://jsfiddle.net/gregmason/UChLF/16/, pertinent code:
ko.bindingHandlers.tableSorter = {
init: function (element) {
setTimeout(function () { $(element).tablesorter(); }, 0);
},
update: function (element, valueAccessor) {
ko.utils.unwrapObservable(valueAccessor()); //just to get a dependency
$(element).trigger("update");
}
};
The errant behavior can be obvious by clicking the delete link on the row.
If you click on the row without sorting, you will see the row disappear correctly.
If you first click on a column to re-sort in a different order, THEN delete the row, it remains in the table and appears to have cached.
This can be handled by binding each of the table headers instead of the table itself and replacing the tableSorter code with a custom sort behavior as discussed in this thread:
knockout js - Table sorting using column headers. The sort replacement is here:
ko.bindingHandlers.sort = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var asc = false;
element.style.cursor = 'pointer';
element.onclick = function(){
var value = valueAccessor();
var prop = value.prop;
var data = value.arr;
asc = !asc;
if(asc){
data.sort(function(left, right){
return left[prop]() == right[prop]() ? 0 : left[prop]() < right[prop]() ? -1 : 1;
});
} else {
data.sort(function(left, right){
return left[prop]() == right[prop]() ? 0 : left[prop]() > right[prop]() ? -1 : 1;
});
}
}
}
};
This has fixed my sorting/editing/deleting issues and a working jsFiddle is here: http://jsfiddle.net/gregmason/UChLF/18/

IE9 multiple select overflow while printing

I'm having problems with IE9 ignoring the select borders when printing a multiple select.
Here's how to recreate the problem:
Open IE9 on Windows 7.
Go to w3schools's multiple select edit page.
Now highlight the options and copy/paste until there is a long list of duplicates.
Then remove the size attribute.
Click on "Edit and Click Me" so that the page reloads and you now have your modified select in the second panel.
Now, print the document (even using the XPS viewer).
For me, all of the options are printed on the page, even though the select is only 4 option elements tall. This still happens to some degree if you leave the "size" attribute at the default value of 2, but it's far more obvious when it is changed or removed.
Is anyone else experiencing this? Is this an IE bug? Does anyone know of a workaround?
You can work around this by viewing the site in IE9's compatibility mode. Usually IE will determine that it cannot display a site properly and give you the option to turn on compatibility mode from within the address bar but sometimes you need to explicitly set it.
How to turn on compatibility mode - http://www.sevenforums.com/tutorials/1196-internet-explorer-compatibility-view-turn-off.html - I used the first one in method 2.
There doesn't seem to be any CSS solution for this. Instead, I wrote a small jQuery script that copies the <select multiple> contents into a <div>, so that it can be printed. Then I applied some CSS to make it look like a select, and only show the copy when actually printing.
Script:
//jQuery required
$(function() {
if(!$.browser.msie) return false;
$('select[multiple]').each(function() {
$lPrintableDiv = $('<div data-for="' + this.id + '" />').addClass($(this).attr('class')).addClass('printable');
//update printable on changes
$(this).after($lPrintableDiv).change(function($aEvent){
updatePrintable($aEvent.target);
});
//run once on load
updatePrintable(this);
});
});
function updatePrintable($aTarget) {
var $lSelect = $($aTarget);
var $lSelected = $($aTarget).val();
var $lPrintable = $('[data-for="'+$aTarget.id+'"]');
$($lPrintable).width($lSelect.width()).height($lSelect.height());
$($lPrintable).html('');
$($aTarget).children().each(function($lElm){
$lVal = $(this).val();
$lLabel = $('<label />').text($lVal);
$lOption = $('<input type="checkbox" />').val($lVal);
if($(this).is(':selected'))
$lOption.prop('checked', true);
$lPrintable.append($lOption).append($lLabel);
});
}
CSS:
.printable {
border: 1px solid grey;
display: none;
overflow: auto;
}
.printable label {
display: block;
font: .8em sans-serif;
overflow: hidden;
white-space: nowrap;
}
.printable [type="checkbox"] {
display: none;
}
.printable [type="checkbox"]:checked + label {
background: #3399ff;
color: white;
}
#media print {
select[multiple] { display: none; }
.printable { display: inline-block; }
.printable [type="checkbox"]:checked + label { background: grey; }
}
Also see the jsFiddle and original post about this fix

jquery ui connected sortables: cancel, disable, destroy... nada

I have two connected sortable lists.
For the sake of my question, when I start dragging an item from #sortable1 to #sortable2, in the start event I want to cancel/ disable/ the drop in #sortable2
Nothing works?
$("#sortable1, #sortable2").sortable({
connectWith: "#sortable1, #sortable2",
start: startDrag
});
function startDrag(event, ui) {
$("#sortable2").css("opacity","0.5");
// $("#sortable2").sortable("cancel"); // goes whooooo
/* docs say:
If the sortable item is being moved from one connected sortable to another:
$(ui.sender).sortable('cancel');
will cancel the change. Useful in the 'receive' callback.
*/
// $("#sortable1").sortable("cancel"); // I only want to cancel dropping in sortable2...
// $("#sortable2").sortable("disable"); // disables only after the drop event
// $("#sortable2").sortable("destroy"); // same as "disable"
}
function stopDrag(event, ui) {
$("#sortable2").css("opacity","1.0");
// $("#sortable2").sortable("enable");
}
My JSFiddle here: http://jsfiddle.net/tunafish/m32XW/
I have found 2 more questions like mine:
jQuery sortable('disable') from start event not entirely working like expected
http://forum.jquery.com/topic/disable-a-sortable-while-dragging-with-connectedlists
No responses..
Anything appreciated!
EDIT: I also tried to overlay a div like a modal on the sortable, but can still be dragged to that way. The only thing that worked is a show/hide, but that's not an option for me.
OK here is my app; two lists of images, sortable and you can copy over from the connected list.
If an item already exists in the target it's disabled.
Hopefully useful to someone...
JSFiffle here: http://jsfiddle.net/tunafish/VBG5V/
CSS:
.page { width: 410px; padding: 20px; margin: 0 auto; background: darkgray; }
.album { list-style: none; overflow: hidden;
width: 410px; margin: 0; padding: 0; padding-top: 5px;
background: gray;
}
.listing { margin-bottom: 10px; }
.album li { float: left; outline: none;
width: 120px; height: 80px; margin: 0 0 5px 5px; padding: 5px;
background: #222222;
}
li.placeholder { background: lightgray; }
JS:
$("ul, li").disableSelection();
$(".album, .favorites").sortable({
connectWith: ".album, .favorites",
placeholder: "placeholder",
forcePlaceholderSize: true,
revert: 300,
helper: "clone",
stop: uiStop,
receive: uiReceive,
over: uiOver
});
$(".album li").mousedown(mStart);
var iSender, iTarget, iIndex, iId, iSrc, iCopy;
var overCount = 0;
/* everything starts here */
function mStart() {
// remove any remaining .copy classes
$(iSender + " li").removeClass("copy");
// set vars
if ($(this).parent().hasClass("listing")) { iSender = ".listing"; iTarget = ".favorites"; }
else { iSender = ".favorites"; iTarget = ".listing"; }
iIndex = $(this).index();
iId = $(this).attr("id");
iSrc = $(this).find("img").attr("src");
iCopy = $(iTarget + " li img[src*='" + iSrc + "']").length > 0; // boolean, true if there is allready a copy in the target list
// disable target if item is allready in there
if (iCopy) { $(iTarget).css("opacity","0.5").sortable("disable"); }
}
/* when sorting has stopped */
function uiStop(event, ui) {
// enable target
$(iTarget).css("opacity","1.0").sortable("enable");
// reset item vars
iSender = iTarget = iIndex = iId = iSrc = iCopy = undefined;
overCount = 0;
// reinit mousedown, live() did not work to disable
$(".album li").mousedown(mStart);
}
/* rolling over the receiver - over, out, over etc. */
function uiOver(event, ui) {
// only if item in not allready in the target
if (!iCopy) {
// counter for over/out (numbers even/uneven)
overCount++;
// if even [over], clone to original index in sender, show and fadein (sortables hides it)
if (overCount%2) {
if (iIndex == 0) { ui.item.clone().addClass("copy").attr("id", iId).prependTo(iSender).fadeIn("slow"); }
else { ui.item.clone().addClass("copy").attr("id", iId).insertAfter(iSender + " li:eq(" + iIndex + ")").fadeIn("slow"); }
}
// else uneven [out], remove copies
else { $(iSender + " li.copy").remove(); }
}
// else whoooooo
}
/* list transfers, fix ID's here */
function uiReceive(event, ui) {
(iTarget == ".favorites") ? liPrefix = "fli-" : liPrefix = "lli-";
// set ID with index for each matched element
$(iTarget + " li").each(function(index) {
$(this).attr("id", liPrefix + (index + 1)); // id's start from 1
});
}
HTML:
<div class="page">
<div class="container">
<h2>Photo Album</h2>
<ul class="listing album">
<li id="li-1"><img src="tn/001.jpg" /></li>
<li id="li-2"><img src="tn/002.jpg" /></li>
<li id="li-3"><img src="tn/003.jpg" /></li>
<li id="li-4"><img src="tn/004.jpg" /></li>
<li id="li-5"><img src="tn/005.jpg" /></li>
</ul>
</div>
<div style="clear:both;"></div>
<div class="container">
<h2>Favorites</h2>
<ul class="favorites album">
<li id="fli-1"><img src="tn/001.jpg" /></li>
<li id="fli-2"><img src="tn/002.jpg" /></li>
<li id="fli-3"><img src="tn/010.jpg" /></li>
</ul>
</div>
</div>
/* docs say:
If the sortable item is being moved from one connected sortable to another:
$(ui.sender).sortable('cancel');
will cancel the change. Useful in the 'receive' callback.
*/
This code was what I spent 30 minutes looking for!
Ok I found some sort of hack for this.
When an item is moved from #sortable1 to #sortable2, when dragged over #sortable2 there is a list item added with class .placeholder
So in UI's over event I did
$("#sortable2 li.placeholder").hide();
Than set it back with UI's stop event
$("#sortable2 li.placeholder").show();
This is only for visuals though.. The item is still moved into #sortable2 so you need to remove() it there. To mimic copying you need to add a clone() back in #sortable2. You can get it's original index() in UI's start event and than use insertAfter(id - 1)
At the moment I can only clone in UI's receive event, I would prefer in UI's over but can't get it to work..

Display an icon in jQuery UI autocomplete results

I'm using the jQuery UI Autocomplete plugin (version 1.8), and I'd like to customize the way the suggestions show up. Specifically, I want to display not only some text, but an icon as well. However, when I send the <img> tag, it just gets rendered as plain text in the results list.
Is there some way to change this behavior? Alternatively, can you suggest a different way to include images in the returned results and have them show up in the suggestions?
Taken from here
$("#search_input").autocomplete({source: "/search",
minLength: 3,
select: function (event, ui) {
document.location = ui.item.url;
}
})
.data("autocomplete")._renderItem = function (ul, item) {
//As per recent documemtation above line should be
//.autocomplete( "instance" )._renderItem = function (ul, item) {
return $('<li class="ui-menu-item-with-icon"></li>')
.data("item.autocomplete", item)
.append('<a><span class="' + item.type + '-item-icon"></span>' + item.label + '</a>')
.appendTo(ul);
};
And the CSS:
.ui-menu .ui-menu-item-with-icon a {
padding-left: 20px;
}
span.group-item-icon,
span.file-item-icon {
display: inline-block;
height: 16px;
width: 16px;
margin-left: -16px;
}
span.group-item-icon {
background: url("/image/icons/group.png") no-repeat left 4px;
}
span.product-item-icon {
background: url("/image/icons/product.png") no-repeat left 7px;
}