Check if an element is visible [JavaScript]

We can find plenty of answers about how to check if an element is visible.

In my case the one which works with opacity, and into a scrollable area, is this one (that I have optimized):

function isScrolledIntoView(el, holder) {
  var bndElem = el.getBoundingClientRect();
  var bndHolder = holder.getBoundingClientRect();
  return bndElem.top <= bndHolder.top ? !(bndHolder.top - bndElem.top > bndElem.height) : !(bndElem.bottom - bndHolder.bottom > bndElem.height);
}

Resolve Promise one after another, in sequence

Below is the code that is handy when you want to execute several Promise actions in sequence:

function PromiseChain(arr, fct) {
  var dfd = Promise.resolve();
  var res = arr.map(function(item,idx) {
    dfd = dfd.then(function() {
      return fct(item,idx)
    });
    return dfd
  });
  return Promise.all(res)
}

// for short version:
// function PromiseChain(n,r){var e=Promise.resolve(),i=n.map(function(n,i){return e=e.then(function(){return r(n,i)})});return Promise.all(i)}

And an example about how to use it:

function promFunc(key) {
  return new Promise(function(prom_res) {
    setTimeout(function() { prom_res(key) }, 1000);
  })
}

PromiseChain(["a", "b", "c"], function(val, idx) {
  console.log(idx, val);
  return promFunc(idx+"_"+val);
})
.then(function(res) {
  console.log(res);
})

// result:
// 0 a
// 1 b
// 2 c
// Array [ "0_a", "1_b", "2_c" ]

Deep clone an object in JavaScript

We can find many discussions and solutions about this issue.

In my case the one that worked is this code.

Below you’ll find a compressed version of the extend() function (with a fix):

function extend(){var r,t,n,o,e=arguments[0]||{},f=1,i=arguments.length,u=!1,y=function(r){if(null===r||"object"!=typeof r||r.nodeType||null!==r&&r===r.window)return!1;try{if(r.constructor&&!this.hasOwnProperty.call(r.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0};for("boolean"==typeof e&&(u=e,e=arguments[f]||{},f++),"object"!=typeof e&&"function"!=typeof e&&(e={}),!1;i>f;f+=1)if(null!==(r=arguments[f]))for(t in r)e!==r[t]&&"undefined"==typeof e[t]&&(u&&r[t]&&(y(r[t])||(n=Array.isArray(r[t])))?(n?(n=!1,o=e[t]&&Array.isArray(e[t])?e[t]:[]):o=e[t]&&y(e[t])?e[t]:{},e[t]=extend(u,o,r[t])):void 0!==r[t]&&(e[t]=r[t]));return e}

And here an example:

var object_a = {
    value_1: 'a',
    value_2: true,
    value_3: 354,
    value_4: [
      'x',
      'y',
      'z'
    ],
    value_5: {
      I: 'I',
      II: 'II',
      III: 'III',
      IV: {
        a: 'A',
        b: 'B'
      },
      V: [
        'elm', {
          5: 'five'
        },
        ['my_array']
      ]
    },
    value_6: function() {
      return "pouet"
    },
    value_7: new Date(2017, 0, 1)
  };
var object_b = {};
// clone object_a into object_b
extend(true, object_b, object_a);
object_a.value_2 = false;
object_a.value_1 = "b";
console.log("A => ", object_a);
console.log("B => ", object_b);

Check permissions in Sharepoint based on a PermMask

In some cases you could get this kind of hexadecimal numbers from Sharepoint for the PermMask: 0xb008431061, or 0x1b03c4313ff, and so on.

To verify the PermMask against the different level of permissions you can proceed with the below method (using functions from core.js):

var permMask = '0x1b03c4313ff';
var permissions = new SP.BasePermissions();
permissions.initPropertiesFromJson({High:GetPermMaskH(permMask), Low:GetPermMaskL(permMask)});
// we can now check permissions using SP.BasePermissions.has()
// and we can compare with SP.PermissionKind — see https://msdn.microsoft.com/en-us/library/office/ee556747(v=office.14).aspx
var canEdit = permissions.has(SP.PermissionKind.editListItems);

On Sharepoint 2010 GetPermMaskH and GetPermMaskL are not defined, so here is their code:

function GetPermMaskH(b) {
    var a = b.length;
    return a <= 10 ? 0 : parseInt(b.substring(2, a - 8), 16)
}

function GetPermMaskL(b) {
    var a = b.length;
    return a <= 10 ? parseInt(b) : parseInt(b.substring(a - 8, a), 16)
}

It took me many hours to find how to proceed, so I hope it will help some others.

Redirect after Deleting [Sharepoint]

When I open a dialog for an EditFrom and use the « Delete Item » button from the ribbon, then the main page is redirected to the related Sharepoint list. This behavior is very bad for the user experience.

To change it I used the below Javascript code:

// replace the Delete Item default action when into a popup
$(document).ready(function() {
  // if it's in a popup
  if (window !== window.top) {
    $('a[id$="_toolBarTbl_RptControls_diidIODeleteItem"]').attr("href","javascript:commonDeleteItem(); return false")
  }
})
// now when clicking on the Delete item it will call my own function
// here I use http://aymkdn.github.io/SharepointPlus/ to show a waiting message and to delete the current item, and then close the modal
function commonDeleteItem() {
  $SP().waitModalDialog("Deleting...");
  $SP().list("{"+WPQ2FormCtx.ListAttributes.Id+"}").remove({ID:GetUrlKeyValue("ID")}, {
    after:function() {
      $SP().closeModalDialog();
      $SP().closeModalDialog(2);
    }
  })
}

How to open an Excel document from SharePoint into Chrome/Firefox in readonly/edit mode

With Sharepoint 2010, when you use Internet Explorer you can open an Office document in readonly or in edit mode. Also, when trying to open an Excel file in Firefox/Chrome it will probably open the ugly xslviewer and won’t let you deal with your file.

After searching a bit about it, I found a clue that helped me to find a good solution: on Firefox/Chrome when you click on an Excel file, you’ll now have a modal asking if you want to open it in edit or readonly mode!

To achieve this result you have to insert the below Javascript code somewhere into your masterpage:

/**
 * fix problem with Excel documents on Firefox/Chrome
 * @param  {HTMLElement} p the <A> element
 * @param  {HTMLEvent} a the click event
 * @param  {Boolean} h TRUE
 * @param  {Boolean} e FALSE
 * @param  {Boolean} g FALSE
 * @param  {Strin} k the ActiveX command (e.g. "SharePoint.OpenDocuments.3")
 * @param  {Number} c 0
 * @param  {String} o the activeX command, here we look at "SharePoint.OpenDocuments"
 * @param  {String} m
 * @param  {String} b the replacement URL to the xslviewer
 */
var bak_DispEx;
var modalOpenDocument; // it will be use with the modal
SP.SOD.executeOrDelayUntilEventNotified(function() {
  bak_DispEx = _DispEx;
  _DispEx=function(p, a, h, e, g, k, c, o, m, b, j, l, i, f, d) {
    // if o==="SharePoint.OpenDocuments" && !IsClientAppInstalled(o)
    // in that case we want to open ask the user if he/she wants to readonly or edit the file
    var fileURL = b.replace(/.*_layouts\/xlviewer\.aspx\?id=(.*)/, "$1");
    if (o === "SharePoint.OpenDocuments" && !IsClientAppInstalled(o) && /\.xlsx?$/.test(fileURL)) {
      // if the URL doesn't start with http
      if (!/^http/.test(fileURL)) {
        fileURL = window.location.protocol + "//" + window.location.host + fileURL;
      }
      var ohtml = document.createElement('div');
      ohtml.style.padding = "10px";
      ohtml.style.display = "inline-block";
      ohtml.style.width = "200px";
      ohtml.style.width = "200px";
      ohtml.innerHTML = '<style>'
                      + '.opendocument_button { background-color:#fdfdfd; border:1px solid #ababab; color:#444; display:inline-block; padding: 7px 10px; }'
                      + '.opendocument_button:hover { box-shadow: none }'
                      + '#opendocument_readonly,#opendocument_edit { float:none; font-size: 100%; line-height: 1.15; margin: 0; overflow: visible; box-sizing: border-box; padding: 0; height:auto }'
                      + '.opendocument_ul { list-style-type:none;margin-top:10px;margin-bottom:10px;padding-top:0;padding-bottom:0 }'
                      + '</style>'
                      + 'You are about to open:'
                      + '<ul class="opendocument_ul">'
                      + '  <li>Name: <b>'+fileURL.split("/").slice(-1)+'</b></li>'
                      + '  <li>From: <b>'+window.location.hostname+'</b></li>'
                      + '</ul>'
                      + 'How would like to open this file?'
                      + '<ul class="opendocument_ul">'
                      + '  <li><label><input type="radio" name="opendocument_choices" id="opendocument_readonly" checked> Read Only</label></li>'
                      + '  <li><label><input type="radio" name="opendocument_choices" id="opendocument_edit"> Edit</label></li>'
                      + '</ul>'
                      + '<div style="text-align: center;margin-top: 20px;"><button type="button" class="opendocument_button" style="background-color: #2d9f2d;color: #fff;" onclick="modalOpenDocument.close(document.getElementById(\'opendocument_edit\').checked)">Open</button> <button type="button" class="opendocument_button" style="margin-left:10px" onclick="modalOpenDocument.close(-1)">Cancel</button></div>';
      // show the modal
      modalOpenDocument=SP.UI.ModalDialog.showModalDialog({
        html:ohtml,
        dialogReturnValueCallback:function(ret) {
          if (ret!==-1) {
            if (ret === true) { // edit
              // reformat the fileURL
              var ext;
              if (/\.xlsx?$/.test(b)) ext = "ms-excel";
              if (/\.docx?$/.test(b)) ext = "ms-word"; // not currently supported
              fileURL = ext + ":ofe|u|" + fileURL;
            }
            window.location.href = fileURL; // open the file
          }
        }
      });
      a.preventDefault();
      a.stopImmediatePropagation()
      a.cancelBubble = true;
      a.returnValue = false;
      return false;
    }
    return bak_DispEx.apply(this, arguments);
  }
}, "sp.scriptloaded-core.js")

[2] Bypass a lookup field not displayed because of threshold on NewForm and EditForm [Sharepoint 2013]

This is an update of this article.

This time the JS Link file’s content is the one below:

// loadExt permits to load JS and CSS files
// https://gist.github.com/Aymkdn/98acfbb46fbe7c1f00cdd3c753520ea8
function loadExt(e,t){var s=this;s.files=e,s.js=[],s.head=document.getElementsByTagName("head")[0],s.after=t||function(){},s.loadStyle=function(e){var t=document.createElement("link");t.rel="stylesheet",t.type="text/css",t.href=e,s.head.appendChild(t)},s.loadScript=function(e){var t=document.createElement("script");t.type="text/javascript",t.src=s.js[e];var a=function(){++e<s.js.length?s.loadScript(e):s.after()};t.onload=function(){a()},s.head.appendChild(t)};for(var a=0;a<s.files.length;a++)/\.js$|\.js\?/.test(s.files[a])&&s.js.push(s.files[a]),/\.css$|\.css\?/.test(s.files[a])&&s.loadStyle(s.files[a]);s.js.length>0?s.loadScript(0):s.after()}

(function() {
  // some "global" variables
  var lookupFieldsToFix = [];
  var storageThrottledLookup = localStorage.getItem("ThrottledLookup");
  if (storageThrottledLookup) storageThrottledLookup=JSON.parse(storageThrottledLookup);

  // onLoad() is called when all the fields in the form have been proceed
  function onLoad(ctx) {
    // we'll need jQuery (or nanoajax... see SharepointPlus doc) and SharepointPlus
    loadExt([
      "/Toolbox/Documents/js/jQuery/jquery-1.12.4.min.js",
      "/Toolbox/Documents/js/SharepointPlus/3.14/sharepointplus-3.14.js"
    ], function() {
      // if some lookup fields need to be fixed
      if (lookupFieldsToFix.length>0) {
        var aDeferred=[];
        for (var i=0; i<lookupFieldsToFix.length; i++) {
          aDeferred.push(fixLookup(lookupFieldsToFix[i]));
        }
        $.when.apply(this, aDeferred).done(function() {
          var save={}, count=0, i;
          for (i=arguments.length; i--;) {
            if (arguments[i]) {
              save[arguments[i].field] = arguments[i].choices; // save it as {fieldName:choices}
              count++;
            }
          }
          if (count > 0) {
            // we use localStorage to store the data retrieve from the Lists
            localStorage.setItem('ThrottledLookup', JSON.stringify(save));
            // now reload the page
            $SP().closeModalDialog();
            $('#aspnetForm').hide().after('<h1 style="margin-top:50vh;transform:translateY(-50%);text-align:center">Reloading...</h1>');
            window.location.reload();
          }
        })
      } else {
        // delete all existing localStorage
        localStorage.removeItem('ThrottledLookup');

        // here you can start doing anything you want on this form
      }
    });    
  }
  
  /**
   * Fix the broken lookup fields
   * @param  {String} field Name of the field to fix
   * @return {Deferred}
   */ 
  function fixLookup(field) {
    var deferred = jQuery.Deferred();
    var choices=[], modal;

    WPQ2FormCtx.ListSchema[field].ChoiceCount=0;
    WPQ2FormCtx.ListSchema[field].Choices=[];
    // show a Waiting message
    modal = $SP().getModalDialog('loading-throttled');
    if (!modal) {
      $SP().showModalDialog({
        wait:true,
        title:"Loading some data...",
        id:"loading-throttled"
      });
    }
    // and we get data from the list
    // here we'll use "ID" as the data returned by our field in the form
    var fieldID="ID";
    $SP().list('{'+WPQ2FormCtx.ListSchema[field].LookupListId+'}').get({fields:fieldID, paging:true}, function(data) {
      var res=[];
      for (var i=0, len=data.length; i<len; i++) {
        res.push({LookupId:data[i].getAttribute(fieldID), LookupValue:data[i].getAttribute(fieldID)});
      }
      deferred.resolve({field:field, choices:res});
    });

    return deferred;
  }

  // do some actions as soon as the fields are shown
  var loadAfterForm = {
    Templates: {
      OnPreRender:function(ctx) {
        // we want to show Lookup fields even if there are more than 5000 items in those lists
        if (ctx.ListSchema.Field[0].Throttled === true) {
          if (storageThrottledLookup) {
            // we use the stored data to create our dropdown
            ctx.ListSchema.Field[0].Throttled=false;
            ctx.ListSchema.Field[0].ChoiceCount=storageThrottledLookup[ctx.ListSchema.Field[0].Name].length;
            ctx.ListSchema.Field[0].Choices=storageThrottledLookup[ctx.ListSchema.Field[0].Name];
          }
          else {
            lookupFieldsToFix.push(ctx.ListSchema.Field[0].Name);
          }
        }
      },
      OnPostRender:function(ctx) {
        // only trigger when everything is loaded
        // --> ctx.ListData.Items[0] all the fields
        if (ctx.ListSchema.Field[0].Name === "Attachments") {
          onLoad(ctx)
        }
      }
    }
  }

  // don't do it when editing the page
  if (GetUrlKeyValue("PageView") !== "Shared" && GetUrlKeyValue("DisplayMode") !== "Design") SPClientTemplates.TemplateManager.RegisterTemplateOverrides(loadAfterForm);
})();

Yes, you can be lazy and get more than 5000 SharePoint Items

To reply to this blog post I wanted to share the same thing but with SharepointPlus.

To use the paging option and get all the content from a large Sharepoint list you can simply do the below code:

$SP().list("My List").get({
  fields:"ID,Title",
  rowlimit:5000,
  paging:true,
  progress:function progress(nbItemsLoaded) {
    // for each new page this function will be called
    console.log("It's still loading... already "+nbItemsLoaded+" items have been loaded!");
  }
}, function(data) {
  console.log(data.length); // -> 23587
  for (var i=0, len=data.length; i<len; i++) {
    console.log(data[i].getAttribute("ID"))
  }
})

If you only want to get the first 15,000 items, you can use the option page:3. See the documentation to know more.

Détecter si le Freebox Player est allumé ou éteint (en veille) via les API de Free [Programmation]

Il n’existe pas de commande directe qui permette de savoir si le Player est éteint (en veille) ou allumé… Cependant il existe une astuce qui consiste à faire une requête en utilisant les paramètres suivants :

{
  url:"http://mafreebox.freebox.fr/api/v3/airmedia/receivers/Freebox%20Player/",
  headers:{
    "X-Fbx-App-Auth": client.app.session_token
  }, 
  method:"POST",
  json: {
    "action":"stop",
    "media_type":"video"
  },
  encode:"utf-8"
}

La Freebox va alors retourner { success:true } si elle est allumée, ou { success:false } si elle est éteinte/en veille.