Visar inlägg med etikett Javascript. Visa alla inlägg
Visar inlägg med etikett Javascript. Visa alla inlägg

måndag 30 juli 2012

Gruppera sidtyper i adminläget på EPiServer CMS 5

Hej igen, åter från semester och tänkte slänga upp lite fulkod som grupperar sidtyperna i adminläget på EPiServer CMS 5.

Jag har nämligen upplevt att när man bygger en stor EPiServer-site med många sidtyper, så blir det ofta rörigt att bläddra bland sidtyper i adminläget. Eftersom så gott som samtliga projekt jag varit med i följt namnsättningsstandarden som EPiServer själva följer i sitt templates-projekt, med prefixet [Public] osv., så kändes gruppering på prefix som den logiska vägen att gå.

Obs. på att följande lösning enbart är testad i EPiServer CMS 5 R2 SP2 (5.2.375.236)

Hittar ni några fel eller andra dômmheter så hojta gärna till!

Steg 1, hitta & öppna aspx-sida som sköter EPiServers adminläge-meny.

Den återfinns under EPiServers installationsmapp, t.ex. "C:\Program Files\EPiServer\CMS\5.2.375.236\Application\UI\Admin\" och heter menu.aspx. Börja med att skapa en backup av filen.

Steg 2, lägga in javascript för gruppering

Öppna sedan filen i din favoriteditor, leta upp raden:


EPi.AddEventListener(window, "load", EPiCheckSortOrder);

och släng in följande javascript efteråt:


/* Change grouping behavior to selected one */
function changeGrouping(grouping) {
    var pageTypeTreeGrouping = document.getElementById('PageTypeTreeGrouping');

    if (!window.top.epiMenuGrouping)
        window.top.epiMenuGrouping = null;

    // Already toggled, untoggle
    if (window.top.epiMenuGrouping == grouping) {
        pageTypeTreeGrouping.className = 'EPEdit-CommandTool';
        window.top.epiMenuGrouping = null;
        UnGroupPageTypesByPrefix();
    } else {
        switch (grouping) {
            case 'prefix':
                pageTypeTreeGrouping.className = 'EPEdit-CommandToolClicked';
                GroupPageTypesByPrefix();
                break;
        } // Add more grouping alternatives when implemented...
        window.top.epiMenuGrouping = grouping;
    }
}

/* For browser compability (IE) */
var getElementsByClassName = function(oNode, class_name) {
    var docList = oNode.all || oNode.getElementsByTagName('*'),
            matchArray = new Array(),
            re = new RegExp("(?:^|\\s)" + class_name + "(?:\\s|$)"),
            i = 0,
            docListLength = docList.length;

    for (i = 0; i < docListLength; i = i + 1) {
        if (re.test(docList[i].className)) {
            matchArray[matchArray.length] = docList[i];
        }
    }
    return matchArray;
};

/* Toggles selected group visibility */
function toggleGroup() {
    var oParent = this.parentElement,
            oToggle = getElementsByClassName(oParent, 'toggle'),
            oExpandIcon = getElementsByClassName(oParent, 'expandIcon');
    if (!!oToggle && oToggle.length > 0 && !!oExpandIcon && oExpandIcon.length > 0) {
        oToggle = oToggle[0];
        var toggleVisible = oToggle.style.display == 'none';
        oToggle.style.display = (toggleVisible ? '' : 'none');
        oExpandIcon = oExpandIcon[0];
        oExpandIcon.style.backgroundImage = (toggleVisible ? 'url(../../App_Themes/Default/Images/ExplorerTree/minus.gif)' : 'url(../../App_Themes/Default/Images/ExplorerTree/plus.gif)');
    }
    return false;
};

/* Ungroups the page types and restores them into default behavior */
function UnGroupPageTypesByPrefix() {
    var oListDiv = document.getElementById('PageTypeListDiv'),
            oMenuItems = getElementsByClassName(oListDiv, 'EPAdmin-menuItem'),
            clonedMenuItems = [],
            i = 0;

    for (i; i < oMenuItems.length; i = i + 1) {
        clonedMenuItems.push(oMenuItems[i].cloneNode(true));
    }
    oListDiv.innerHTML = '';
    for (i = 0; i < clonedMenuItems.length; i = i + 1) {
        oListDiv.appendChild(clonedMenuItems[i]);
    }
};

/* Groups the page types depending on their prefix (eg. [Public] ... */
function GroupPageTypesByPrefix() {
    var oListDiv = document.getElementById('PageTypeListDiv'),
            oPageTypeLinks = getElementsByClassName(oListDiv, 'pageTypeLink'),
            linkGroups = [],
            i = 0;

    /* Get prefixes */
    for (i; i < oPageTypeLinks.length; i = i + 1) {
        var pageTypeName = oPageTypeLinks[i].innerText.replace(new RegExp(" ", "g"), '').replace(/(\r\n|\n|\r)/gm, ""),
                pageTypePrefixes = pageTypeName.match("\\[[^\\]]*]"),
                pageTypePrefix = (!!pageTypePrefixes && pageTypePrefixes.length > 0) ? pageTypePrefixes[0] : "";

        if (!!!pageTypePrefix) pageTypePrefix = '[ ]';

        if (!!!linkGroups[pageTypePrefix]) {
            linkGroups.push(pageTypePrefix);
            linkGroups[pageTypePrefix] = [oPageTypeLinks[i].cloneNode(true)];
        } else {
            linkGroups[pageTypePrefix].push(oPageTypeLinks[i].cloneNode(true));
        }
    }

    oListDiv.innerHTML = '';

    /* Create prefix group holders */
    var oDiv,
            oInnerDiv,
            j = 0,
            expandIcon,
            groupTitle = '',
            oClearDiv;

    for (i = 0; i < linkGroups.length; i = i + 1) {
        oDiv = document.createElement('div');
        oDiv.className = 'group';
        oDiv.style.marginTop = '5px';
        oListDiv.appendChild(oDiv);

        expandIcon = document.createElement('span');
        expandIcon.className = 'expandIcon';
        expandIcon.style.backgroundImage = 'url(../../App_Themes/Default/Images/ExplorerTree/plus.gif)';
        expandIcon.style.width = '15px';
        expandIcon.style.height = '18px';
        expandIcon.style.position = 'absolute';
        expandIcon.style.cursor = 'pointer';
        expandIcon.style.backgroundPosition = 'left center';
        expandIcon.style.backgroundRepeat = 'no-repeat';
        expandIcon.style.backgroundColor = '#EEEEEE';
        expandIcon.onclick = groupTitle.onclick = toggleGroup;
        expandIcon.innerHtml = '&nbsp;';
        oDiv.appendChild(expandIcon);

        groupTitle = document.createElement('span');
        groupTitle.innerText = linkGroups[i];
        groupTitle.className = 'groupTitle';
        groupTitle.style.marginLeft = "20px";
        groupTitle.style.fontWeight = "bold";
        groupTitle.style.cursor = 'pointer';
        groupTitle.onclick = toggleGroup;
        oDiv.appendChild(groupTitle);

        oInnerDiv = document.createElement('div');
        oInnerDiv.style.display = 'none';
        oInnerDiv.style.marginLeft = '25px';
        oInnerDiv.className = 'toggle';
        oDiv.appendChild(oInnerDiv);

        for (j = 0; j < linkGroups[linkGroups[i]].length; j = j + 1) {
            oInnerInnerDiv = document.createElement('div');
            oInnerInnerDiv.className = 'EPAdmin-menuItem';
            oInnerDiv.appendChild(oInnerInnerDiv);
            oInnerInnerDiv.appendChild(linkGroups[linkGroups[i]][j]);
        }

        oClearDiv = document.createElement('div');
        oClearDiv.style.clear = 'both';
        oDiv.appendChild(oClearDiv);
    }
}


Steg 3, modifiera sortering så att grupperingen inte bryts vid sortering. 

Ersätt funktionen "changeSortOrder" med följande:


/* Note: modified */
function changeSortOrder(order) {
    var container = document.getElementById('PageTypeListDiv'),
            groups = getElementsByClassName(container, 'group'),
                                       links,
                                       linkArray = [[]],
                                       sortFunction,
            i = 0,
            j = 0,
            oPageTypeIndexSorting = document.getElementById('PageTypeIndexSorting'),
            oPageTypeAlphabeticalSorting = document.getElementById('PageTypeAlphabeticalSorting');

    // IE has innerText property which other browsers don't
    // why we have to get and set the text in another way.
    function GetInnerText(node) {
        if (node.innerText != null)
            return node.innerText;
        else
            if (node.childNodes.length > 0 && node.childNodes[0].nodeType == 3) // nodeType 3 is textNode.
            return node.childNodes[0].nodeValue;
    }

    function SetInnerText(node, text) {
        if (node.innerText != null)
            node.innerText = text;
        else
            if (node.childNodes.length > 0 && node.childNodes[0].nodeType == 3) // nodeType 3 is textNode.
            return node.childNodes[0].nodeValue = text;
    }

    function CreateLinkObject(oLink) {
        var link = new Object();
        link.innerText = GetInnerText(oLink);
        link.id = oLink.id;
        link.href = oLink.href;
        return link;
    }

    if (!!groups && groups.length > 0) {
        for (i = 0; i < groups.length; i = i + 1) {
            links = getElementsByClassName(groups[i], 'pageTypeLink');
            for (j = 0; j < links.length; j = j + 1) {
                if (!!!linkArray[i]) {
                    linkArray[i] = [];
                }
                linkArray[i].push(CreateLinkObject(links[j]))
            }
        }
    } else {
        links = document.getElementsByName('pLink');
        linkArray[0] = [];
        for (i = 0; i < links.length; i = i + 1) {
            linkArray[0].push(CreateLinkObject(links[i]));
        }
    }

    switch (order) {
        case 'index':
            sortFunction = SortByIndex;
            oPageTypeIndexSorting.className = 'EPEdit-CommandToolClicked';
            oPageTypeIndexSorting.style.cursor = 'default';
            oPageTypeAlphabeticalSorting.className = 'EPEdit-CommandTool';
            oPageTypeAlphabeticalSorting.style.cursor = 'pointer';
            break;
        case 'alphabetical':
            sortFunction = SortByAlphabet;
            oPageTypeAlphabeticalSorting.className = 'EPEdit-CommandToolClicked';
            oPageTypeAlphabeticalSorting.style.cursor = 'default';
            oPageTypeIndexSorting.className = 'EPEdit-CommandTool';
            oPageTypeIndexSorting.style.cursor = 'pointer';
            break;
    }

    for (i = 0; i < linkArray.length; i = i + 1) {
        linkArray[i].sort(sortFunction);
    }

    window.top.epiMenuSortOrder = order;

    if (!!groups && groups.length > 0) {
        for (i = 0; i < groups.length; i = i + 1) {
            links = getElementsByClassName(groups[i], 'pageTypeLink');
            for (j = 0; j < links.length; j = j + 1) {
                var link = linkArray[i][j];
                links[j].href = link.href;
                SetInnerText(links[j], link.innerText);
                links[j].id = link.id;
            }
        }
    } else {
        for (i = 0; i < links.length; i = i + 1) {
            var link = linkArray[0][i];
            links[i].href = link.href;
            SetInnerText(links[i], link.innerText);
            links[i].id = link.id;
        }
    }
}



Steg 4, lägg till ikon.

Kopiera in en ikon i mappen Images/AdminMenu (under App_Themes), i mitt fall är sökvägen: "C:\Program Files\EPiServer\CMS\5.2.375.236\Application\App_Themes\Default\Images\AdminMenu".

Jag valde följande ikon:



(som jag kallade för PrefixGroup.jpg).

Steg 5, lägg till ikon-knapp i markup.

Återvänd till filen menu.aspx, och leta upp följande rad:


<img id="PageTypeAlphabeticalSorting" class="EPEdit-CommandTool" style="vertical-align: bottom; cursor: pointer;" src="<%=GetImageThemeUrl("AdminMenu/AlphabeticalSort.gif")%>" alt="<%=EPiServer.Core.LanguageManager.Instance.Translate("/admin/menuheadings/pagetypesorting/sortalphabetically")%>" title="<%=EPiServer.Core.LanguageManager.Instance.Translate("/admin/menuheadings/pagetypesorting/sortalphabetically")%>" onclick="changeSortOrder('alphabetical')" />

Lägg till följande efter denna rad:



<img id="PageTypeTreeGrouping" class="EPEdit-CommandTool" style="vertical-align: bottom; cursor: pointer;" src="<%=GetImageThemeUrl("AdminMenu/PrefixGroup.jpg")%>" alt="<%=EPiServer.Core.LanguageManager.Instance.Translate("/admin/menuheadings/pagetypegrouping/prefixgroup")%>" title="<%=EPiServer.Core.LanguageManager.Instance.Translate("/admin/menuheadings/pagetypegrouping/prefixgroup")%>" onclick="changeGrouping('prefix')" />


Steg 6, lägg till i språkfiler:

Välj önskade språkfiler i mappen för edit/admin-språkfiler, i ditt projekt. Dvs. webbapplikationens språk-mapp (som standard: /lang/language<språkändelse>.xml)

Leta upp elementet <pagetypesorting>...</...> och lägg till följande direkt efteråt (ex. engelska):

<pagetypegrouping>
  <prefixgroup>Group by prefix</prefixgroup>
</pagetypegrouping>



Sen är det klart!
Obs. på att EPiServer-uppdateringar som installeras antagligen förstör implementationen ovan.

Några skärmdumpar:

Utseende utan gruppering aktiverad.




















Grupperad och sorterad alfabetiskt.

tisdag 26 juni 2012

Validera personnummer med javascript

Idag skulle jag skriva ett enkelt javascript för att validera svenska personnummer med javascript.

Googlade lite men hittade inget jag gillade. Ett oläsligt script i sourcen på http://www.personnummer.nu/ och ett annat som inte webbläsaren köpte på http://0x539.se/validera-personnummer-med-jquery-validation/

Därför skrev jag mitt eget bidrag, som tolererar personnummer i följande format:

ååmmddxxxx 
ååmmdd-xxxx
ååååmmddxxxx
ååååmmdd-xxxx

samt kollar checksum på sista siffran mha. Luhn.



var validatePersonalNumber = function(input) {
    // Check valid length & form
    if (!input) return false;

    if (input.indexOf('-') == -1) {
        if (input.length === 10) {
            input = input.slice(0, 6) + "-" + input.slice(6);
        } else {
            input = input.slice(0, 8) + "-" + input.slice(8);
        }
    }
    if (!input.match(/^(\d{2})(\d{2})(\d{2})\-(\d{4})|(\d{4})(\d{2})(\d{2})\-(\d{4})$/)) return false;

    // Clean input
    input = input.replace('-', '');
    if (input.length == 12) {
        input = input.substring(2);
    }

    // Declare variables
    var d = new Date(((!!RegExp.$1) ? RegExp.$1 : RegExp.$5), (((!!RegExp.$2) ? RegExp.$2 : RegExp.$6)-1), ((!!RegExp.$3) ? RegExp.$3 : RegExp.$7)),
            sum = 0,
            numdigits = input.length,
            parity = numdigits % 2,
            i,
            digit;

    // Check valid date
    if (Object.prototype.toString.call(d) !== "[object Date]" || isNaN(d.getTime())) return false;

    // Check luhn algorithm
    for (i = 0; i < numdigits; i = i + 1) {
        digit = parseInt(input.charAt(i))
        if (i % 2 == parity) digit *= 2;
        if (digit > 9) digit -= 9;
        sum += digit;
    }
    return (sum % 10) == 0;
};


Edit 2012-10-24: Redigerat raden var d = new Date(...) som inte gillades av IE (tack till Raita som uppmärksammade detta).

Hoppas någon annan har nytta av det! :)